diff --git a/Aaru.Compression/ADC.cs b/Aaru.Compression/ADC.cs index d0884e7ae..cff6f5008 100644 --- a/Aaru.Compression/ADC.cs +++ b/Aaru.Compression/ADC.cs @@ -39,149 +39,148 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -namespace Aaru.Compression +namespace Aaru.Compression; + +/// Implements the Apple version of RLE +public static class ADC { - /// Implements the Apple version of RLE - public static class ADC + /// + /// Set to true if this algorithm is supported, false otherwise. + /// + public static bool IsSupported => true; + + [DllImport("libAaru.Compression.Native", SetLastError = true)] + static extern int AARU_adc_decode_buffer(byte[] dst_buffer, int dst_size, byte[] src_buffer, int src_size); + + const int PLAIN = 1; + const int TWO_BYTE = 2; + const int THREE_BYTE = 3; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetChunkType(byte byt) => (byt & 0x80) == 0x80 + ? PLAIN + : (byt & 0x40) == 0x40 + ? THREE_BYTE + : TWO_BYTE; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetChunkSize(byte byt) => GetChunkType(byt) switch { - /// - /// Set to true if this algorithm is supported, false otherwise. - /// - public static bool IsSupported => true; + PLAIN => (byt & 0x7F) + 1, + TWO_BYTE => ((byt & 0x3F) >> 2) + 3, + THREE_BYTE => (byt & 0x3F) + 4, + _ => -1 + }; - [DllImport("libAaru.Compression.Native", SetLastError = true)] - static extern int AARU_adc_decode_buffer(byte[] dst_buffer, int dst_size, byte[] src_buffer, int src_size); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static int GetOffset(ReadOnlySpan chunk) => GetChunkType(chunk[0]) switch + { + PLAIN => 0, + TWO_BYTE => ((chunk[0] & 0x03) << 8) + chunk[1], + THREE_BYTE => (chunk[1] << 8) + chunk[2], + _ => -1 + }; - const int PLAIN = 1; - const int TWO_BYTE = 2; - const int THREE_BYTE = 3; + /// Decompresses a byte buffer that's compressed with ADC + /// Compressed buffer + /// Buffer to hold decompressed data + /// How many bytes are stored on + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static int DecodeBuffer(byte[] source, byte[] destination) + { + if(Native.IsSupported) + return AARU_adc_decode_buffer(destination, destination.Length, source, source.Length); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static int GetChunkType(byte byt) => (byt & 0x80) == 0x80 - ? PLAIN - : (byt & 0x40) == 0x40 - ? THREE_BYTE - : TWO_BYTE; + int inputPosition = 0; + int chunkSize; + int offset; + int chunkType; + int outPosition = 0; + Span temp = stackalloc byte[3]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static int GetChunkSize(byte byt) => GetChunkType(byt) switch + while(inputPosition < source.Length) { - PLAIN => (byt & 0x7F) + 1, - TWO_BYTE => ((byt & 0x3F) >> 2) + 3, - THREE_BYTE => (byt & 0x3F) + 4, - _ => -1 - }; + byte readByte = source[inputPosition++]; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static int GetOffset(ReadOnlySpan chunk) => GetChunkType(chunk[0]) switch - { - PLAIN => 0, - TWO_BYTE => ((chunk[0] & 0x03) << 8) + chunk[1], - THREE_BYTE => (chunk[1] << 8) + chunk[2], - _ => -1 - }; + chunkType = GetChunkType(readByte); - /// Decompresses a byte buffer that's compressed with ADC - /// Compressed buffer - /// Buffer to hold decompressed data - /// How many bytes are stored on - [MethodImpl(MethodImplOptions.AggressiveOptimization)] - public static int DecodeBuffer(byte[] source, byte[] destination) - { - if(Native.IsSupported) - return AARU_adc_decode_buffer(destination, destination.Length, source, source.Length); - - int inputPosition = 0; - int chunkSize; - int offset; - int chunkType; - int outPosition = 0; - Span temp = stackalloc byte[3]; - - while(inputPosition < source.Length) + switch(chunkType) { - byte readByte = source[inputPosition++]; + case PLAIN: + chunkSize = GetChunkSize(readByte); - chunkType = GetChunkType(readByte); + if(outPosition + chunkSize > destination.Length) + goto finished; - switch(chunkType) - { - case PLAIN: - chunkSize = GetChunkSize(readByte); + Array.Copy(source, inputPosition, destination, outPosition, chunkSize); + outPosition += chunkSize; + inputPosition += chunkSize; - if(outPosition + chunkSize > destination.Length) - goto finished; + break; + case TWO_BYTE: + chunkSize = GetChunkSize(readByte); + temp[0] = readByte; + temp[1] = source[inputPosition++]; + offset = GetOffset(temp); - Array.Copy(source, inputPosition, destination, outPosition, chunkSize); - outPosition += chunkSize; - inputPosition += chunkSize; + if(outPosition + chunkSize > destination.Length) + goto finished; - break; - case TWO_BYTE: - chunkSize = GetChunkSize(readByte); - temp[0] = readByte; - temp[1] = source[inputPosition++]; - offset = GetOffset(temp); + if(offset == 0) + { + byte lastByte = destination[outPosition - 1]; - if(outPosition + chunkSize > destination.Length) - goto finished; - - if(offset == 0) + for(int i = 0; i < chunkSize; i++) { - byte lastByte = destination[outPosition - 1]; - - for(int i = 0; i < chunkSize; i++) - { - destination[outPosition] = lastByte; - outPosition++; - } + destination[outPosition] = lastByte; + outPosition++; } - else + } + else + { + for(int i = 0; i < chunkSize; i++) { - for(int i = 0; i < chunkSize; i++) - { - destination[outPosition] = destination[outPosition - offset - 1]; - outPosition++; - } + destination[outPosition] = destination[outPosition - offset - 1]; + outPosition++; } + } - break; - case THREE_BYTE: - chunkSize = GetChunkSize(readByte); - temp[0] = readByte; - temp[1] = source[inputPosition++]; - temp[2] = source[inputPosition++]; - offset = GetOffset(temp); + break; + case THREE_BYTE: + chunkSize = GetChunkSize(readByte); + temp[0] = readByte; + temp[1] = source[inputPosition++]; + temp[2] = source[inputPosition++]; + offset = GetOffset(temp); - if(outPosition + chunkSize > destination.Length) - goto finished; + if(outPosition + chunkSize > destination.Length) + goto finished; - if(offset == 0) + if(offset == 0) + { + byte lastByte = destination[outPosition - 1]; + + for(int i = 0; i < chunkSize; i++) { - byte lastByte = destination[outPosition - 1]; - - for(int i = 0; i < chunkSize; i++) - { - destination[outPosition] = lastByte; - outPosition++; - } + destination[outPosition] = lastByte; + outPosition++; } - else + } + else + { + for(int i = 0; i < chunkSize; i++) { - for(int i = 0; i < chunkSize; i++) - { - destination[outPosition] = destination[outPosition - offset - 1]; - outPosition++; - } + destination[outPosition] = destination[outPosition - offset - 1]; + outPosition++; } + } - break; - } + break; } - - finished: - - return outPosition; } + + finished: + + return outPosition; } } \ No newline at end of file diff --git a/Aaru.Compression/AppleRle.cs b/Aaru.Compression/AppleRle.cs index 513fc2f59..569d15aac 100644 --- a/Aaru.Compression/AppleRle.cs +++ b/Aaru.Compression/AppleRle.cs @@ -33,105 +33,104 @@ using System.Runtime.InteropServices; -namespace Aaru.Compression +namespace Aaru.Compression; + +/// Implements the Apple version of RLE +public static class AppleRle { - /// Implements the Apple version of RLE - public static class AppleRle + /// + /// Set to true if this algorithm is supported, false otherwise. + /// + public static bool IsSupported => true; + + [DllImport("libAaru.Compression.Native", SetLastError = true)] + static extern int AARU_apple_rle_decode_buffer(byte[] dst_buffer, int dst_size, byte[] src_buffer, int src_size); + + const uint DART_CHUNK = 20960; + + /// Decodes a buffer compressed with Apple RLE + /// Encoded buffer + /// Buffer where to write the decoded data + /// The number of decoded bytes + public static int DecodeBuffer(byte[] source, byte[] destination) { - /// - /// Set to true if this algorithm is supported, false otherwise. - /// - public static bool IsSupported => true; + if(Native.IsSupported) + return AARU_apple_rle_decode_buffer(destination, destination.Length, source, source.Length); - [DllImport("libAaru.Compression.Native", SetLastError = true)] - static extern int AARU_apple_rle_decode_buffer(byte[] dst_buffer, int dst_size, byte[] src_buffer, int src_size); + int count = 0; + bool nextA = true; // true if A, false if B + byte repeatedByteA = 0, repeatedByteB = 0; + bool repeatMode = false; // true if we're repeating, false if we're just copying + int inPosition = 0, outPosition = 0; - const uint DART_CHUNK = 20960; - - /// Decodes a buffer compressed with Apple RLE - /// Encoded buffer - /// Buffer where to write the decoded data - /// The number of decoded bytes - public static int DecodeBuffer(byte[] source, byte[] destination) + while(inPosition <= source.Length && + outPosition <= destination.Length) { - if(Native.IsSupported) - return AARU_apple_rle_decode_buffer(destination, destination.Length, source, source.Length); - - int count = 0; - bool nextA = true; // true if A, false if B - byte repeatedByteA = 0, repeatedByteB = 0; - bool repeatMode = false; // true if we're repeating, false if we're just copying - int inPosition = 0, outPosition = 0; - - while(inPosition <= source.Length && - outPosition <= destination.Length) + switch(repeatMode) { - switch(repeatMode) + case true when count > 0: { - case true when count > 0: + count--; + + if(nextA) { - count--; - - if(nextA) - { - nextA = false; - - destination[outPosition++] = repeatedByteA; - - continue; - } - - nextA = true; - - destination[outPosition++] = repeatedByteB; - - continue; - } - case false when count > 0: - count--; - - destination[outPosition++] = source[inPosition++]; - - continue; - } - - if(inPosition == source.Length) - break; - - while(true) - { - byte b1 = source[inPosition++]; - byte b2 = source[inPosition++]; - short s = (short)((b1 << 8) | b2); - - if(s == 0 || - s >= DART_CHUNK || - s <= -DART_CHUNK) - continue; - - if(s < 0) - { - repeatMode = true; - repeatedByteA = source[inPosition++]; - repeatedByteB = source[inPosition++]; - count = (-s * 2) - 1; - nextA = false; + nextA = false; destination[outPosition++] = repeatedByteA; - break; + continue; } - repeatMode = false; - count = (s * 2) - 1; + nextA = true; + + destination[outPosition++] = repeatedByteB; + + continue; + } + case false when count > 0: + count--; destination[outPosition++] = source[inPosition++]; - break; - } + continue; } - return outPosition; + if(inPosition == source.Length) + break; + + while(true) + { + byte b1 = source[inPosition++]; + byte b2 = source[inPosition++]; + short s = (short)((b1 << 8) | b2); + + if(s == 0 || + s >= DART_CHUNK || + s <= -DART_CHUNK) + continue; + + if(s < 0) + { + repeatMode = true; + repeatedByteA = source[inPosition++]; + repeatedByteB = source[inPosition++]; + count = (-s * 2) - 1; + nextA = false; + + destination[outPosition++] = repeatedByteA; + + break; + } + + repeatMode = false; + count = (s * 2) - 1; + + destination[outPosition++] = source[inPosition++]; + + break; + } } + + return outPosition; } } \ No newline at end of file diff --git a/Aaru.Compression/Native.cs b/Aaru.Compression/Native.cs index 3b4d59ea7..59b88218f 100644 --- a/Aaru.Compression/Native.cs +++ b/Aaru.Compression/Native.cs @@ -32,52 +32,51 @@ using System.Runtime.InteropServices; -namespace Aaru.Compression +namespace Aaru.Compression; + +public static class Native { - public static class Native + static bool _checked; + static bool _supported; + + /// Set to return native as never supported + public static bool ForceManaged { get; set; } + + /// + /// If set to true the native library was found and loaded correctly and its reported version is + /// compatible. + /// + public static bool IsSupported { - static bool _checked; - static bool _supported; - - /// Set to return native as never supported - public static bool ForceManaged { get; set; } - - /// - /// If set to true the native library was found and loaded correctly and its reported version is - /// compatible. - /// - public static bool IsSupported + get { - get - { - if(ForceManaged) - return false; - - if(_checked) - return _supported; - - ulong version; - _checked = true; - - try - { - version = AARU_get_acn_version(); - } - catch - { - _supported = false; - - return false; - } - - // TODO: Check version compatibility - _supported = version >= 0x06000000; + if(ForceManaged) + return false; + if(_checked) return _supported; - } - } - [DllImport("libAaru.Compression.Native", SetLastError = true)] - static extern ulong AARU_get_acn_version(); + ulong version; + _checked = true; + + try + { + version = AARU_get_acn_version(); + } + catch + { + _supported = false; + + return false; + } + + // TODO: Check version compatibility + _supported = version >= 0x06000000; + + return _supported; + } } + + [DllImport("libAaru.Compression.Native", SetLastError = true)] + static extern ulong AARU_get_acn_version(); } \ No newline at end of file diff --git a/Aaru.Compression/TeleDiskLzh.cs b/Aaru.Compression/TeleDiskLzh.cs index 3958ce884..f62a774dc 100644 --- a/Aaru.Compression/TeleDiskLzh.cs +++ b/Aaru.Compression/TeleDiskLzh.cs @@ -45,415 +45,414 @@ using System; using System.IO; -namespace Aaru.Compression +namespace Aaru.Compression; + +/* + * Based on Japanese version 29-NOV-1988 + * LZSS coded by Haruhiko OKUMURA + * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI + * Edited and translated to English by Kenji RIKITAKE + */ +/// Implements the TeleDisk version of LZH +public class TeleDiskLzh { + /// + /// Set to true if this algorithm is supported, false otherwise. + /// + public static bool IsSupported => true; + + const int BUFSZ = 512; + + /* LZSS Parameters */ + + const int N = 4096; /* Size of string buffer */ + const int F = 60; /* Size of look-ahead buffer */ + const int THRESHOLD = 2; + + /* Huffman coding parameters */ + + const int N_CHAR = 256 - THRESHOLD + F; + /* character code (= 0..N_CHAR-1) */ + const int T = (N_CHAR * 2) - 1; /* Size of table */ + const int ROOT = T - 1; /* root position */ + const int MAX_FREQ = 0x8000; + /* - * Based on Japanese version 29-NOV-1988 - * LZSS coded by Haruhiko OKUMURA - * Adaptive Huffman Coding coded by Haruyasu YOSHIZAKI - * Edited and translated to English by Kenji RIKITAKE + * Tables for encoding/decoding upper 6 bits of + * sliding dictionary pointer */ - /// Implements the TeleDisk version of LZH - public class TeleDiskLzh + + /* decoder table */ + readonly byte[] _dCode = { - /// - /// Set to true if this algorithm is supported, false otherwise. - /// - public static bool IsSupported => true; + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, + 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, + 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, + 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10, + 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, + 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, + 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, + 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C, + 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, + 0x3C, 0x3D, 0x3E, 0x3F + }; - const int BUFSZ = 512; + readonly byte[] _dLen = + { + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, + 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, + 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, + 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, + 0x08, 0x08, 0x08, 0x08 + }; + readonly ushort[] _freq = new ushort[T + 1]; /* cumulative freq table */ - /* LZSS Parameters */ + readonly Stream _inStream; - const int N = 4096; /* Size of string buffer */ - const int F = 60; /* Size of look-ahead buffer */ - const int THRESHOLD = 2; + /* + * pointing parent nodes. + * area [T..(T + N_CHAR - 1)] are pointers for leaves + */ + readonly short[] _prnt = new short[T + N_CHAR]; - /* Huffman coding parameters */ + /* pointing children nodes (son[], son[] + 1)*/ + readonly short[] _son = new short[T]; + readonly byte[] _textBuf = new byte[N + F - 1]; - const int N_CHAR = 256 - THRESHOLD + F; - /* character code (= 0..N_CHAR-1) */ - const int T = (N_CHAR * 2) - 1; /* Size of table */ - const int ROOT = T - 1; /* root position */ - const int MAX_FREQ = 0x8000; + ushort _getbuf; + byte _getlen; - /* - * Tables for encoding/decoding upper 6 bits of - * sliding dictionary pointer - */ + Tdlzhuf _tdctl; - /* decoder table */ - readonly byte[] _dCode = - { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, - 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - 0x09, 0x09, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0A, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, 0x0B, - 0x0C, 0x0C, 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x0D, 0x0E, 0x0E, 0x0E, 0x0E, 0x0F, 0x0F, 0x0F, 0x0F, 0x10, 0x10, - 0x10, 0x10, 0x11, 0x11, 0x11, 0x11, 0x12, 0x12, 0x12, 0x12, 0x13, 0x13, 0x13, 0x13, 0x14, 0x14, 0x14, 0x14, - 0x15, 0x15, 0x15, 0x15, 0x16, 0x16, 0x16, 0x16, 0x17, 0x17, 0x17, 0x17, 0x18, 0x18, 0x19, 0x19, 0x1A, 0x1A, - 0x1B, 0x1B, 0x1C, 0x1C, 0x1D, 0x1D, 0x1E, 0x1E, 0x1F, 0x1F, 0x20, 0x20, 0x21, 0x21, 0x22, 0x22, 0x23, 0x23, - 0x24, 0x24, 0x25, 0x25, 0x26, 0x26, 0x27, 0x27, 0x28, 0x28, 0x29, 0x29, 0x2A, 0x2A, 0x2B, 0x2B, 0x2C, 0x2C, - 0x2D, 0x2D, 0x2E, 0x2E, 0x2F, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, - 0x3C, 0x3D, 0x3E, 0x3F - }; + /// Implements the TeleDisk LZH algorithm over the specified stream. + /// Stream with compressed data. + public TeleDiskLzh(Stream dataStream) + { + int i; + _getbuf = 0; + _getlen = 0; + _tdctl = new Tdlzhuf(); + _tdctl.Ibufcnt = _tdctl.Ibufndx = 0; // input buffer is empty + _tdctl.Bufcnt = 0; + StartHuff(); - readonly byte[] _dLen = - { - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, - 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, - 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, - 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x06, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, - 0x07, 0x07, 0x07, 0x07, 0x07, 0x07, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, - 0x08, 0x08, 0x08, 0x08 - }; - readonly ushort[] _freq = new ushort[T + 1]; /* cumulative freq table */ + for(i = 0; i < N - F; i++) + _textBuf[i] = 0x20; - readonly Stream _inStream; + _tdctl.R = N - F; + _inStream = dataStream; + } - /* - * pointing parent nodes. - * area [T..(T + N_CHAR - 1)] are pointers for leaves - */ - readonly short[] _prnt = new short[T + N_CHAR]; + /* DeCompression - /* pointing children nodes (son[], son[] + 1)*/ - readonly short[] _son = new short[T]; - readonly byte[] _textBuf = new byte[N + F - 1]; + split out initialization code to init_Decode() - ushort _getbuf; - byte _getlen; + */ - Tdlzhuf _tdctl; + /// Decompresses data + /// Buffer to write the decompressed data to + /// Number of bytes to decompress + /// Number of decompressed bytes + public int Decode(out byte[] buf, int len) /* Decoding/Uncompressing */ + { + short c; + buf = new byte[len]; + int count; // was an unsigned long, seems unnecessary - /// Implements the TeleDisk LZH algorithm over the specified stream. - /// Stream with compressed data. - public TeleDiskLzh(Stream dataStream) - { - int i; - _getbuf = 0; - _getlen = 0; - _tdctl = new Tdlzhuf(); - _tdctl.Ibufcnt = _tdctl.Ibufndx = 0; // input buffer is empty - _tdctl.Bufcnt = 0; - StartHuff(); + for(count = 0; count < len;) + if(_tdctl.Bufcnt == 0) + { + if((c = DecodeChar()) < 0) + return count; // fatal error - for(i = 0; i < N - F; i++) - _textBuf[i] = 0x20; - - _tdctl.R = N - F; - _inStream = dataStream; - } - - /* DeCompression - - split out initialization code to init_Decode() - - */ - - /// Decompresses data - /// Buffer to write the decompressed data to - /// Number of bytes to decompress - /// Number of decompressed bytes - public int Decode(out byte[] buf, int len) /* Decoding/Uncompressing */ - { - short c; - buf = new byte[len]; - int count; // was an unsigned long, seems unnecessary - - for(count = 0; count < len;) - if(_tdctl.Bufcnt == 0) + if(c < 256) { - if((c = DecodeChar()) < 0) - return count; // fatal error - - if(c < 256) - { - buf[count] = (byte)c; - _textBuf[_tdctl.R++] = (byte)c; - _tdctl.R &= N - 1; - count++; - } - else - { - short pos; - - if((pos = DecodePosition()) < 0) - return count; // fatal error - - _tdctl.Bufpos = (ushort)((_tdctl.R - pos - 1) & (N - 1)); - _tdctl.Bufcnt = (ushort)(c - 255 + THRESHOLD); - _tdctl.Bufndx = 0; - } + buf[count] = (byte)c; + _textBuf[_tdctl.R++] = (byte)c; + _tdctl.R &= N - 1; + count++; } else { - // still chars from last string - while(_tdctl.Bufndx < _tdctl.Bufcnt && - count < len) - { - c = _textBuf[(_tdctl.Bufpos + _tdctl.Bufndx) & (N - 1)]; - buf[count] = (byte)c; - _tdctl.Bufndx++; - _textBuf[_tdctl.R++] = (byte)c; - _tdctl.R &= N - 1; - count++; - } + short pos; - // reset bufcnt after copy string from text_buf[] - if(_tdctl.Bufndx >= _tdctl.Bufcnt) - _tdctl.Bufndx = _tdctl.Bufcnt = 0; + if((pos = DecodePosition()) < 0) + return count; // fatal error + + _tdctl.Bufpos = (ushort)((_tdctl.R - pos - 1) & (N - 1)); + _tdctl.Bufcnt = (ushort)(c - 255 + THRESHOLD); + _tdctl.Bufndx = 0; + } + } + else + { + // still chars from last string + while(_tdctl.Bufndx < _tdctl.Bufcnt && + count < len) + { + c = _textBuf[(_tdctl.Bufpos + _tdctl.Bufndx) & (N - 1)]; + buf[count] = (byte)c; + _tdctl.Bufndx++; + _textBuf[_tdctl.R++] = (byte)c; + _tdctl.R &= N - 1; + count++; } - return count; // count == len, success - } - - long DataRead(out byte[] buf, long size) - { - if(size > _inStream.Length - _inStream.Position) - size = _inStream.Length - _inStream.Position; - - buf = new byte[size]; - _inStream.Read(buf, 0, (int)size); - - return size; - } - - int NextWord() - { - if(_tdctl.Ibufndx >= _tdctl.Ibufcnt) - { - _tdctl.Ibufndx = 0; - _tdctl.Ibufcnt = (ushort)DataRead(out _tdctl.Inbuf, BUFSZ); - - if(_tdctl.Ibufcnt <= 0) - return -1; + // reset bufcnt after copy string from text_buf[] + if(_tdctl.Bufndx >= _tdctl.Bufcnt) + _tdctl.Bufndx = _tdctl.Bufcnt = 0; } - while(_getlen <= 8) - { - // typically reads a word at a time - _getbuf |= (ushort)(_tdctl.Inbuf[_tdctl.Ibufndx++] << (8 - _getlen)); - _getlen += 8; - } + return count; // count == len, success + } - return 0; - } + long DataRead(out byte[] buf, long size) + { + if(size > _inStream.Length - _inStream.Position) + size = _inStream.Length - _inStream.Position; - int GetBit() /* get one bit */ + buf = new byte[size]; + _inStream.Read(buf, 0, (int)size); + + return size; + } + + int NextWord() + { + if(_tdctl.Ibufndx >= _tdctl.Ibufcnt) { - if(NextWord() < 0) + _tdctl.Ibufndx = 0; + _tdctl.Ibufcnt = (ushort)DataRead(out _tdctl.Inbuf, BUFSZ); + + if(_tdctl.Ibufcnt <= 0) return -1; - - short i = (short)_getbuf; - _getbuf <<= 1; - _getlen--; - - return i < 0 ? 1 : 0; } - int GetByte() /* get a byte */ + while(_getlen <= 8) { - if(NextWord() != 0) - return -1; - - ushort i = _getbuf; - _getbuf <<= 8; - _getlen -= 8; - i = (ushort)(i >> 8); - - return i; + // typically reads a word at a time + _getbuf |= (ushort)(_tdctl.Inbuf[_tdctl.Ibufndx++] << (8 - _getlen)); + _getlen += 8; } - /* initialize freq tree */ + return 0; + } - void StartHuff() + int GetBit() /* get one bit */ + { + if(NextWord() < 0) + return -1; + + short i = (short)_getbuf; + _getbuf <<= 1; + _getlen--; + + return i < 0 ? 1 : 0; + } + + int GetByte() /* get a byte */ + { + if(NextWord() != 0) + return -1; + + ushort i = _getbuf; + _getbuf <<= 8; + _getlen -= 8; + i = (ushort)(i >> 8); + + return i; + } + + /* initialize freq tree */ + + void StartHuff() + { + int i; + + for(i = 0; i < N_CHAR; i++) { - int i; + _freq[i] = 1; + _son[i] = (short)(i + T); + _prnt[i + T] = (short)i; + } - for(i = 0; i < N_CHAR; i++) + i = 0; + int j = N_CHAR; + + while(j <= ROOT) + { + _freq[j] = (ushort)(_freq[i] + _freq[i + 1]); + _son[j] = (short)i; + _prnt[i] = _prnt[i + 1] = (short)j; + i += 2; + j++; + } + + _freq[T] = 0xffff; + _prnt[ROOT] = 0; + } + + /* reconstruct freq tree */ + + void Reconst() + { + short i, k; + + /* halven cumulative freq for leaf nodes */ + short j = 0; + + for(i = 0; i < T; i++) + if(_son[i] >= T) { - _freq[i] = 1; - _son[i] = (short)(i + T); - _prnt[i + T] = (short)i; - } - - i = 0; - int j = N_CHAR; - - while(j <= ROOT) - { - _freq[j] = (ushort)(_freq[i] + _freq[i + 1]); - _son[j] = (short)i; - _prnt[i] = _prnt[i + 1] = (short)j; - i += 2; + _freq[j] = (ushort)((_freq[i] + 1) / 2); + _son[j] = _son[i]; j++; } - _freq[T] = 0xffff; - _prnt[ROOT] = 0; + /* make a tree : first, connect children nodes */ + for(i = 0, j = N_CHAR; j < T; i += 2, j++) + { + k = (short)(i + 1); + ushort f = _freq[j] = (ushort)(_freq[i] + _freq[k]); + + for(k = (short)(j - 1); f < _freq[k]; k--) {} + + k++; + ushort l = (ushort)((j - k) * 2); + + Array.ConstrainedCopy(_freq, k, _freq, k + 1, l); + _freq[k] = f; + Array.ConstrainedCopy(_son, k, _son, k + 1, l); + _son[k] = i; } - /* reconstruct freq tree */ + /* connect parent nodes */ + for(i = 0; i < T; i++) + if((k = _son[i]) >= T) + _prnt[k] = i; + else + _prnt[k] = _prnt[k + 1] = i; + } - void Reconst() + /* update freq tree */ + + void Update(int c) + { + if(_freq[ROOT] == MAX_FREQ) + Reconst(); + + c = _prnt[c + T]; + + do { - short i, k; + int k = ++_freq[c]; - /* halven cumulative freq for leaf nodes */ - short j = 0; + /* swap nodes to keep the tree freq-ordered */ + int l; - for(i = 0; i < T; i++) - if(_son[i] >= T) - { - _freq[j] = (ushort)((_freq[i] + 1) / 2); - _son[j] = _son[i]; - j++; - } + if(k <= _freq[l = c + 1]) + continue; - /* make a tree : first, connect children nodes */ - for(i = 0, j = N_CHAR; j < T; i += 2, j++) - { - k = (short)(i + 1); - ushort f = _freq[j] = (ushort)(_freq[i] + _freq[k]); + while(k > _freq[++l]) {} - for(k = (short)(j - 1); f < _freq[k]; k--) {} + l--; + _freq[c] = _freq[l]; + _freq[l] = (ushort)k; - k++; - ushort l = (ushort)((j - k) * 2); + int i = _son[c]; + _prnt[i] = (short)l; - Array.ConstrainedCopy(_freq, k, _freq, k + 1, l); - _freq[k] = f; - Array.ConstrainedCopy(_son, k, _son, k + 1, l); - _son[k] = i; - } + if(i < T) + _prnt[i + 1] = (short)l; - /* connect parent nodes */ - for(i = 0; i < T; i++) - if((k = _son[i]) >= T) - _prnt[k] = i; - else - _prnt[k] = _prnt[k + 1] = i; - } + int j = _son[l]; + _son[l] = (short)i; - /* update freq tree */ + _prnt[j] = (short)c; - void Update(int c) + if(j < T) + _prnt[j + 1] = (short)c; + + _son[c] = (short)j; + + c = l; + } while((c = _prnt[c]) != 0); /* do it until reaching the root */ + } + + short DecodeChar() + { + ushort c = (ushort)_son[ROOT]; + + /* + * start searching tree from the root to leaves. + * choose node #(son[]) if input bit == 0 + * else choose #(son[]+1) (input bit == 1) + */ + while(c < T) { - if(_freq[ROOT] == MAX_FREQ) - Reconst(); + int ret; - c = _prnt[c + T]; - - do - { - int k = ++_freq[c]; - - /* swap nodes to keep the tree freq-ordered */ - int l; - - if(k <= _freq[l = c + 1]) - continue; - - while(k > _freq[++l]) {} - - l--; - _freq[c] = _freq[l]; - _freq[l] = (ushort)k; - - int i = _son[c]; - _prnt[i] = (short)l; - - if(i < T) - _prnt[i + 1] = (short)l; - - int j = _son[l]; - _son[l] = (short)i; - - _prnt[j] = (short)c; - - if(j < T) - _prnt[j + 1] = (short)c; - - _son[c] = (short)j; - - c = l; - } while((c = _prnt[c]) != 0); /* do it until reaching the root */ - } - - short DecodeChar() - { - ushort c = (ushort)_son[ROOT]; - - /* - * start searching tree from the root to leaves. - * choose node #(son[]) if input bit == 0 - * else choose #(son[]+1) (input bit == 1) - */ - while(c < T) - { - int ret; - - if((ret = GetBit()) < 0) - return -1; - - c += (ushort)ret; - c = (ushort)_son[c]; - } - - c -= T; - Update(c); - - return (short)c; - } - - short DecodePosition() - { - short bit; - - /* decode upper 6 bits from given table */ - if((bit = (short)GetByte()) < 0) + if((ret = GetBit()) < 0) return -1; - ushort i = (ushort)bit; - ushort c = (ushort)(_dCode[i] << 6); - ushort j = _dLen[i]; - - /* input lower 6 bits directly */ - j -= 2; - - while(j-- > 0) - { - if((bit = (short)GetBit()) < 0) - return -1; - - i = (ushort)((i << 1) + bit); - } - - return (short)(c | (i & 0x3f)); + c += (ushort)ret; + c = (ushort)_son[c]; } - /* update when cumulative frequency */ - /* reaches to this value */ - struct Tdlzhuf + c -= T; + Update(c); + + return (short)c; + } + + short DecodePosition() + { + short bit; + + /* decode upper 6 bits from given table */ + if((bit = (short)GetByte()) < 0) + return -1; + + ushort i = (ushort)bit; + ushort c = (ushort)(_dCode[i] << 6); + ushort j = _dLen[i]; + + /* input lower 6 bits directly */ + j -= 2; + + while(j-- > 0) { - public ushort R, Bufcnt, Bufndx, Bufpos, // string buffer - // the following to allow block reads from input in next_word() - Ibufcnt, Ibufndx; // input buffer counters - public byte[] Inbuf; // input buffer + if((bit = (short)GetBit()) < 0) + return -1; + + i = (ushort)((i << 1) + bit); } + + return (short)(c | (i & 0x3f)); + } + /* update when cumulative frequency */ + /* reaches to this value */ + + struct Tdlzhuf + { + public ushort R, Bufcnt, Bufndx, Bufpos, // string buffer + // the following to allow block reads from input in next_word() + Ibufcnt, Ibufndx; // input buffer counters + public byte[] Inbuf; // input buffer } } \ No newline at end of file diff --git a/Aaru.Core/Checksum.cs b/Aaru.Core/Checksum.cs index c2077fd20..9cb023244 100644 --- a/Aaru.Core/Checksum.cs +++ b/Aaru.Core/Checksum.cs @@ -37,834 +37,833 @@ using Aaru.Checksums; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Core +namespace Aaru.Core; + +/// Enabled checksums +[Flags] +public enum EnableChecksum { - /// Enabled checksums - [Flags] - public enum EnableChecksum + /// Enables Adler-32 + Adler32 = 1, + /// Enables CRC-16 + Crc16 = 2, + /// Enables CRC-32 + Crc32 = 4, + /// Enables CRC-64 + Crc64 = 8, + /// Enables MD5 + Md5 = 16, + /// Enables SHA1 + Sha1 = 64, + /// Enables SHA2-256 + Sha256 = 128, + /// Enables SHA2-384 + Sha384 = 256, + /// Enables SHA2-512 + Sha512 = 512, + /// Enables SpamSum + SpamSum = 1024, + /// Enables Fletcher-16 + Fletcher16 = 2048, + /// Enables Fletcher-32 + Fletcher32 = 4096, + /// Enables all known checksums + All = Adler32 | Crc16 | Crc32 | Crc64 | Md5 | Sha1 | Sha256 | Sha384 | Sha512 | SpamSum | Fletcher16 | + Fletcher32 +} + +/// Checksums and hashes data, with different algorithms, multithreaded +public sealed class Checksum +{ + readonly IChecksum _adler32Ctx; + readonly IChecksum _crc16Ctx; + readonly IChecksum _crc32Ctx; + readonly IChecksum _crc64Ctx; + readonly EnableChecksum _enabled; + readonly IChecksum _f16Ctx; + readonly IChecksum _f32Ctx; + readonly IChecksum _md5Ctx; + readonly IChecksum _sha1Ctx; + readonly IChecksum _sha256Ctx; + readonly IChecksum _sha384Ctx; + readonly IChecksum _sha512Ctx; + readonly IChecksum _ssCtx; + HashPacket _adlerPkt; + Thread _adlerThread; + HashPacket _crc16Pkt; + Thread _crc16Thread; + HashPacket _crc32Pkt; + Thread _crc32Thread; + HashPacket _crc64Pkt; + Thread _crc64Thread; + HashPacket _f16Pkt; + Thread _f16Thread; + HashPacket _f32Pkt; + Thread _f32Thread; + HashPacket _md5Pkt; + Thread _md5Thread; + HashPacket _sha1Pkt; + Thread _sha1Thread; + HashPacket _sha256Pkt; + Thread _sha256Thread; + HashPacket _sha384Pkt; + Thread _sha384Thread; + HashPacket _sha512Pkt; + Thread _sha512Thread; + HashPacket _spamsumPkt; + Thread _spamsumThread; + + /// Initializes an instance of the checksum operations + /// Enabled checksums + public Checksum(EnableChecksum enabled = EnableChecksum.All) { - /// Enables Adler-32 - Adler32 = 1, - /// Enables CRC-16 - Crc16 = 2, - /// Enables CRC-32 - Crc32 = 4, - /// Enables CRC-64 - Crc64 = 8, - /// Enables MD5 - Md5 = 16, - /// Enables SHA1 - Sha1 = 64, - /// Enables SHA2-256 - Sha256 = 128, - /// Enables SHA2-384 - Sha384 = 256, - /// Enables SHA2-512 - Sha512 = 512, - /// Enables SpamSum - SpamSum = 1024, - /// Enables Fletcher-16 - Fletcher16 = 2048, - /// Enables Fletcher-32 - Fletcher32 = 4096, - /// Enables all known checksums - All = Adler32 | Crc16 | Crc32 | Crc64 | Md5 | Sha1 | Sha256 | Sha384 | Sha512 | SpamSum | Fletcher16 | - Fletcher32 + _enabled = enabled; + + if(enabled.HasFlag(EnableChecksum.Adler32)) + { + _adler32Ctx = new Adler32Context(); + + _adlerPkt = new HashPacket + { + Context = _adler32Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Crc16)) + { + _crc16Ctx = new CRC16IBMContext(); + + _crc16Pkt = new HashPacket + { + Context = _crc16Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Crc32)) + { + _crc32Ctx = new Crc32Context(); + + _crc32Pkt = new HashPacket + { + Context = _crc32Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Crc64)) + { + _crc64Ctx = new Crc64Context(); + + _crc64Pkt = new HashPacket + { + Context = _crc64Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Md5)) + { + _md5Ctx = new Md5Context(); + + _md5Pkt = new HashPacket + { + Context = _md5Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Sha1)) + { + _sha1Ctx = new Sha1Context(); + + _sha1Pkt = new HashPacket + { + Context = _sha1Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Sha256)) + { + _sha256Ctx = new Sha256Context(); + + _sha256Pkt = new HashPacket + { + Context = _sha256Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Sha384)) + { + _sha384Ctx = new Sha384Context(); + + _sha384Pkt = new HashPacket + { + Context = _sha384Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Sha512)) + { + _sha512Ctx = new Sha512Context(); + + _sha512Pkt = new HashPacket + { + Context = _sha512Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.SpamSum)) + { + _ssCtx = new SpamSumContext(); + + _spamsumPkt = new HashPacket + { + Context = _ssCtx + }; + } + + if(enabled.HasFlag(EnableChecksum.Fletcher16)) + { + _f16Ctx = new Fletcher16Context(); + + _f16Pkt = new HashPacket + { + Context = _f16Ctx + }; + } + + if(enabled.HasFlag(EnableChecksum.Fletcher32)) + { + _f32Ctx = new Fletcher32Context(); + + _f32Pkt = new HashPacket + { + Context = _f32Ctx + }; + } + + _adlerThread = new Thread(UpdateHash); + _crc16Thread = new Thread(UpdateHash); + _crc32Thread = new Thread(UpdateHash); + _crc64Thread = new Thread(UpdateHash); + _md5Thread = new Thread(UpdateHash); + _sha1Thread = new Thread(UpdateHash); + _sha256Thread = new Thread(UpdateHash); + _sha384Thread = new Thread(UpdateHash); + _sha512Thread = new Thread(UpdateHash); + _spamsumThread = new Thread(UpdateHash); + _f16Thread = new Thread(UpdateHash); + _f32Thread = new Thread(UpdateHash); } - /// Checksums and hashes data, with different algorithms, multithreaded - public sealed class Checksum + /// Updates the checksum with new data + /// New data + public void Update(byte[] data) { - readonly IChecksum _adler32Ctx; - readonly IChecksum _crc16Ctx; - readonly IChecksum _crc32Ctx; - readonly IChecksum _crc64Ctx; - readonly EnableChecksum _enabled; - readonly IChecksum _f16Ctx; - readonly IChecksum _f32Ctx; - readonly IChecksum _md5Ctx; - readonly IChecksum _sha1Ctx; - readonly IChecksum _sha256Ctx; - readonly IChecksum _sha384Ctx; - readonly IChecksum _sha512Ctx; - readonly IChecksum _ssCtx; - HashPacket _adlerPkt; - Thread _adlerThread; - HashPacket _crc16Pkt; - Thread _crc16Thread; - HashPacket _crc32Pkt; - Thread _crc32Thread; - HashPacket _crc64Pkt; - Thread _crc64Thread; - HashPacket _f16Pkt; - Thread _f16Thread; - HashPacket _f32Pkt; - Thread _f32Thread; - HashPacket _md5Pkt; - Thread _md5Thread; - HashPacket _sha1Pkt; - Thread _sha1Thread; - HashPacket _sha256Pkt; - Thread _sha256Thread; - HashPacket _sha384Pkt; - Thread _sha384Thread; - HashPacket _sha512Pkt; - Thread _sha512Thread; - HashPacket _spamsumPkt; - Thread _spamsumThread; - - /// Initializes an instance of the checksum operations - /// Enabled checksums - public Checksum(EnableChecksum enabled = EnableChecksum.All) + if(_enabled.HasFlag(EnableChecksum.Adler32)) { - _enabled = enabled; + _adlerPkt.Data = data; + _adlerThread.Start(_adlerPkt); + } - if(enabled.HasFlag(EnableChecksum.Adler32)) - { - _adler32Ctx = new Adler32Context(); + if(_enabled.HasFlag(EnableChecksum.Crc16)) + { + _crc16Pkt.Data = data; + _crc16Thread.Start(_crc16Pkt); + } - _adlerPkt = new HashPacket - { - Context = _adler32Ctx - }; - } + if(_enabled.HasFlag(EnableChecksum.Crc32)) + { + _crc32Pkt.Data = data; + _crc32Thread.Start(_crc32Pkt); + } - if(enabled.HasFlag(EnableChecksum.Crc16)) - { - _crc16Ctx = new CRC16IBMContext(); + if(_enabled.HasFlag(EnableChecksum.Crc64)) + { + _crc64Pkt.Data = data; + _crc64Thread.Start(_crc64Pkt); + } - _crc16Pkt = new HashPacket - { - Context = _crc16Ctx - }; - } + if(_enabled.HasFlag(EnableChecksum.Md5)) + { + _md5Pkt.Data = data; + _md5Thread.Start(_md5Pkt); + } - if(enabled.HasFlag(EnableChecksum.Crc32)) - { - _crc32Ctx = new Crc32Context(); + if(_enabled.HasFlag(EnableChecksum.Sha1)) + { + _sha1Pkt.Data = data; + _sha1Thread.Start(_sha1Pkt); + } - _crc32Pkt = new HashPacket - { - Context = _crc32Ctx - }; - } + if(_enabled.HasFlag(EnableChecksum.Sha256)) + { + _sha256Pkt.Data = data; + _sha256Thread.Start(_sha256Pkt); + } - if(enabled.HasFlag(EnableChecksum.Crc64)) - { - _crc64Ctx = new Crc64Context(); + if(_enabled.HasFlag(EnableChecksum.Sha384)) + { + _sha384Pkt.Data = data; + _sha384Thread.Start(_sha384Pkt); + } - _crc64Pkt = new HashPacket - { - Context = _crc64Ctx - }; - } + if(_enabled.HasFlag(EnableChecksum.Sha512)) + { + _sha512Pkt.Data = data; + _sha512Thread.Start(_sha512Pkt); + } - if(enabled.HasFlag(EnableChecksum.Md5)) - { - _md5Ctx = new Md5Context(); + if(_enabled.HasFlag(EnableChecksum.SpamSum)) + { + _spamsumPkt.Data = data; + _spamsumThread.Start(_spamsumPkt); + } - _md5Pkt = new HashPacket - { - Context = _md5Ctx - }; - } + if(_enabled.HasFlag(EnableChecksum.Fletcher16)) + { + _f16Pkt.Data = data; + _f16Thread.Start(_f16Pkt); + } - if(enabled.HasFlag(EnableChecksum.Sha1)) - { - _sha1Ctx = new Sha1Context(); + if(_enabled.HasFlag(EnableChecksum.Fletcher32)) + { + _f32Pkt.Data = data; + _f32Thread.Start(_f32Pkt); + } - _sha1Pkt = new HashPacket - { - Context = _sha1Ctx - }; - } + while(_adlerThread.IsAlive || + _crc16Thread.IsAlive || + _crc32Thread.IsAlive || + _crc64Thread.IsAlive || + _md5Thread.IsAlive || + _sha1Thread.IsAlive || + _sha256Thread.IsAlive || + _sha384Thread.IsAlive || + _sha512Thread.IsAlive || + _spamsumThread.IsAlive || + _f16Thread.IsAlive || + _f32Thread.IsAlive) {} - if(enabled.HasFlag(EnableChecksum.Sha256)) - { - _sha256Ctx = new Sha256Context(); + if(_enabled.HasFlag(EnableChecksum.Adler32)) + _adlerThread = new Thread(UpdateHash); - _sha256Pkt = new HashPacket - { - Context = _sha256Ctx - }; - } + if(_enabled.HasFlag(EnableChecksum.Crc16)) + _crc16Thread = new Thread(UpdateHash); - if(enabled.HasFlag(EnableChecksum.Sha384)) - { - _sha384Ctx = new Sha384Context(); + if(_enabled.HasFlag(EnableChecksum.Crc32)) + _crc32Thread = new Thread(UpdateHash); - _sha384Pkt = new HashPacket - { - Context = _sha384Ctx - }; - } + if(_enabled.HasFlag(EnableChecksum.Crc16)) + _crc64Thread = new Thread(UpdateHash); - if(enabled.HasFlag(EnableChecksum.Sha512)) - { - _sha512Ctx = new Sha512Context(); + if(_enabled.HasFlag(EnableChecksum.Md5)) + _md5Thread = new Thread(UpdateHash); - _sha512Pkt = new HashPacket - { - Context = _sha512Ctx - }; - } + if(_enabled.HasFlag(EnableChecksum.Sha1)) + _sha1Thread = new Thread(UpdateHash); - if(enabled.HasFlag(EnableChecksum.SpamSum)) - { - _ssCtx = new SpamSumContext(); + if(_enabled.HasFlag(EnableChecksum.Sha256)) + _sha256Thread = new Thread(UpdateHash); - _spamsumPkt = new HashPacket - { - Context = _ssCtx - }; - } + if(_enabled.HasFlag(EnableChecksum.Sha384)) + _sha384Thread = new Thread(UpdateHash); - if(enabled.HasFlag(EnableChecksum.Fletcher16)) - { - _f16Ctx = new Fletcher16Context(); + if(_enabled.HasFlag(EnableChecksum.Sha512)) + _sha512Thread = new Thread(UpdateHash); - _f16Pkt = new HashPacket - { - Context = _f16Ctx - }; - } - - if(enabled.HasFlag(EnableChecksum.Fletcher32)) - { - _f32Ctx = new Fletcher32Context(); - - _f32Pkt = new HashPacket - { - Context = _f32Ctx - }; - } - - _adlerThread = new Thread(UpdateHash); - _crc16Thread = new Thread(UpdateHash); - _crc32Thread = new Thread(UpdateHash); - _crc64Thread = new Thread(UpdateHash); - _md5Thread = new Thread(UpdateHash); - _sha1Thread = new Thread(UpdateHash); - _sha256Thread = new Thread(UpdateHash); - _sha384Thread = new Thread(UpdateHash); - _sha512Thread = new Thread(UpdateHash); + if(_enabled.HasFlag(EnableChecksum.SpamSum)) _spamsumThread = new Thread(UpdateHash); - _f16Thread = new Thread(UpdateHash); - _f32Thread = new Thread(UpdateHash); - } - /// Updates the checksum with new data - /// New data - public void Update(byte[] data) + if(_enabled.HasFlag(EnableChecksum.Fletcher16)) + _f16Thread = new Thread(UpdateHash); + + if(_enabled.HasFlag(EnableChecksum.Fletcher32)) + _f32Thread = new Thread(UpdateHash); + } + + /// Finishes the checksums + /// Returns the checksum results + public List End() + { + List chks = new List(); + + ChecksumType chk; + + if(_enabled.HasFlag(EnableChecksum.All)) { - if(_enabled.HasFlag(EnableChecksum.Adler32)) - { - _adlerPkt.Data = data; - _adlerThread.Start(_adlerPkt); - } - - if(_enabled.HasFlag(EnableChecksum.Crc16)) - { - _crc16Pkt.Data = data; - _crc16Thread.Start(_crc16Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.Crc32)) - { - _crc32Pkt.Data = data; - _crc32Thread.Start(_crc32Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.Crc64)) - { - _crc64Pkt.Data = data; - _crc64Thread.Start(_crc64Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.Md5)) - { - _md5Pkt.Data = data; - _md5Thread.Start(_md5Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.Sha1)) - { - _sha1Pkt.Data = data; - _sha1Thread.Start(_sha1Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.Sha256)) - { - _sha256Pkt.Data = data; - _sha256Thread.Start(_sha256Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.Sha384)) - { - _sha384Pkt.Data = data; - _sha384Thread.Start(_sha384Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.Sha512)) - { - _sha512Pkt.Data = data; - _sha512Thread.Start(_sha512Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.SpamSum)) - { - _spamsumPkt.Data = data; - _spamsumThread.Start(_spamsumPkt); - } - - if(_enabled.HasFlag(EnableChecksum.Fletcher16)) - { - _f16Pkt.Data = data; - _f16Thread.Start(_f16Pkt); - } - - if(_enabled.HasFlag(EnableChecksum.Fletcher32)) - { - _f32Pkt.Data = data; - _f32Thread.Start(_f32Pkt); - } - - while(_adlerThread.IsAlive || - _crc16Thread.IsAlive || - _crc32Thread.IsAlive || - _crc64Thread.IsAlive || - _md5Thread.IsAlive || - _sha1Thread.IsAlive || - _sha256Thread.IsAlive || - _sha384Thread.IsAlive || - _sha512Thread.IsAlive || - _spamsumThread.IsAlive || - _f16Thread.IsAlive || - _f32Thread.IsAlive) {} - - if(_enabled.HasFlag(EnableChecksum.Adler32)) - _adlerThread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Crc16)) - _crc16Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Crc32)) - _crc32Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Crc16)) - _crc64Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Md5)) - _md5Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Sha1)) - _sha1Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Sha256)) - _sha256Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Sha384)) - _sha384Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Sha512)) - _sha512Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.SpamSum)) - _spamsumThread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Fletcher16)) - _f16Thread = new Thread(UpdateHash); - - if(_enabled.HasFlag(EnableChecksum.Fletcher32)) - _f32Thread = new Thread(UpdateHash); - } - - /// Finishes the checksums - /// Returns the checksum results - public List End() - { - List chks = new List(); - - ChecksumType chk; - - if(_enabled.HasFlag(EnableChecksum.All)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.adler32, - Value = _adler32Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Crc16)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.crc16, - Value = _crc16Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Crc32)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.crc32, - Value = _crc32Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Crc64)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.crc64, - Value = _crc64Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Md5)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.md5, - Value = _md5Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Sha1)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.sha1, - Value = _sha1Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Sha256)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.sha256, - Value = _sha256Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Sha384)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.sha384, - Value = _sha384Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Sha512)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.sha512, - Value = _sha512Ctx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.SpamSum)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.spamsum, - Value = _ssCtx.End() - }; - - chks.Add(chk); - } - - if(_enabled.HasFlag(EnableChecksum.Fletcher16)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.fletcher16, - Value = _f16Ctx.End() - }; - - chks.Add(chk); - } - - if(!_enabled.HasFlag(EnableChecksum.Fletcher32)) - return chks; - chk = new ChecksumType { - type = ChecksumTypeType.fletcher32, - Value = _f32Ctx.End() + type = ChecksumTypeType.adler32, + Value = _adler32Ctx.End() }; chks.Add(chk); + } + if(_enabled.HasFlag(EnableChecksum.Crc16)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.crc16, + Value = _crc16Ctx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.Crc32)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.crc32, + Value = _crc32Ctx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.Crc64)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.crc64, + Value = _crc64Ctx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.Md5)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.md5, + Value = _md5Ctx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.Sha1)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.sha1, + Value = _sha1Ctx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.Sha256)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.sha256, + Value = _sha256Ctx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.Sha384)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.sha384, + Value = _sha384Ctx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.Sha512)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.sha512, + Value = _sha512Ctx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.SpamSum)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.spamsum, + Value = _ssCtx.End() + }; + + chks.Add(chk); + } + + if(_enabled.HasFlag(EnableChecksum.Fletcher16)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.fletcher16, + Value = _f16Ctx.End() + }; + + chks.Add(chk); + } + + if(!_enabled.HasFlag(EnableChecksum.Fletcher32)) return chks; - } - internal static List GetChecksums(byte[] data, EnableChecksum enabled = EnableChecksum.All) + chk = new ChecksumType { - IChecksum adler32CtxData = null; - IChecksum crc16CtxData = null; - IChecksum crc32CtxData = null; - IChecksum crc64CtxData = null; - IChecksum md5CtxData = null; - IChecksum sha1CtxData = null; - IChecksum sha256CtxData = null; - IChecksum sha384CtxData = null; - IChecksum sha512CtxData = null; - IChecksum ssctxData = null; - IChecksum f16CtxData = null; - IChecksum f32CtxData = null; + type = ChecksumTypeType.fletcher32, + Value = _f32Ctx.End() + }; - var adlerThreadData = new Thread(UpdateHash); - var crc16ThreadData = new Thread(UpdateHash); - var crc32ThreadData = new Thread(UpdateHash); - var crc64ThreadData = new Thread(UpdateHash); - var md5ThreadData = new Thread(UpdateHash); - var sha1ThreadData = new Thread(UpdateHash); - var sha256ThreadData = new Thread(UpdateHash); - var sha384ThreadData = new Thread(UpdateHash); - var sha512ThreadData = new Thread(UpdateHash); - var spamsumThreadData = new Thread(UpdateHash); - var f16ThreadData = new Thread(UpdateHash); - var f32ThreadData = new Thread(UpdateHash); + chks.Add(chk); - if(enabled.HasFlag(EnableChecksum.Adler32)) - { - adler32CtxData = new Adler32Context(); - - var adlerPktData = new HashPacket - { - Context = adler32CtxData, - Data = data - }; - - adlerThreadData.Start(adlerPktData); - } - - if(enabled.HasFlag(EnableChecksum.Crc16)) - { - crc16CtxData = new CRC16IBMContext(); - - var crc16PktData = new HashPacket - { - Context = crc16CtxData, - Data = data - }; - - crc16ThreadData.Start(crc16PktData); - } - - if(enabled.HasFlag(EnableChecksum.Crc32)) - { - crc32CtxData = new Crc32Context(); - - var crc32PktData = new HashPacket - { - Context = crc32CtxData, - Data = data - }; - - crc32ThreadData.Start(crc32PktData); - } - - if(enabled.HasFlag(EnableChecksum.Crc64)) - { - crc64CtxData = new Crc64Context(); - - var crc64PktData = new HashPacket - { - Context = crc64CtxData, - Data = data - }; - - crc64ThreadData.Start(crc64PktData); - } - - if(enabled.HasFlag(EnableChecksum.Md5)) - { - md5CtxData = new Md5Context(); - - var md5PktData = new HashPacket - { - Context = md5CtxData, - Data = data - }; - - md5ThreadData.Start(md5PktData); - } - - if(enabled.HasFlag(EnableChecksum.Sha1)) - { - sha1CtxData = new Sha1Context(); - - var sha1PktData = new HashPacket - { - Context = sha1CtxData, - Data = data - }; - - sha1ThreadData.Start(sha1PktData); - } - - if(enabled.HasFlag(EnableChecksum.Sha256)) - { - sha256CtxData = new Sha256Context(); - - var sha256PktData = new HashPacket - { - Context = sha256CtxData, - Data = data - }; - - sha256ThreadData.Start(sha256PktData); - } - - if(enabled.HasFlag(EnableChecksum.Sha384)) - { - sha384CtxData = new Sha384Context(); - - var sha384PktData = new HashPacket - { - Context = sha384CtxData, - Data = data - }; - - sha384ThreadData.Start(sha384PktData); - } - - if(enabled.HasFlag(EnableChecksum.Sha512)) - { - sha512CtxData = new Sha512Context(); - - var sha512PktData = new HashPacket - { - Context = sha512CtxData, - Data = data - }; - - sha512ThreadData.Start(sha512PktData); - } - - if(enabled.HasFlag(EnableChecksum.SpamSum)) - { - ssctxData = new SpamSumContext(); - - var spamsumPktData = new HashPacket - { - Context = ssctxData, - Data = data - }; - - spamsumThreadData.Start(spamsumPktData); - } - - if(enabled.HasFlag(EnableChecksum.Fletcher16)) - { - f16CtxData = new Fletcher16Context(); - - var f16PktData = new HashPacket - { - Context = f16CtxData, - Data = data - }; - - f16ThreadData.Start(f16PktData); - } - - if(enabled.HasFlag(EnableChecksum.Fletcher32)) - { - f32CtxData = new Fletcher32Context(); - - var f32PktData = new HashPacket - { - Context = f32CtxData, - Data = data - }; - - f32ThreadData.Start(f32PktData); - } - - while(adlerThreadData.IsAlive || - crc16ThreadData.IsAlive || - crc32ThreadData.IsAlive || - crc64ThreadData.IsAlive || - md5ThreadData.IsAlive || - sha1ThreadData.IsAlive || - sha256ThreadData.IsAlive || - sha384ThreadData.IsAlive || - sha512ThreadData.IsAlive || - spamsumThreadData.IsAlive || - f16ThreadData.IsAlive || - f32ThreadData.IsAlive) {} - - List dataChecksums = new List(); - ChecksumType chk; - - if(enabled.HasFlag(EnableChecksum.Adler32)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.adler32, - Value = adler32CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Crc16)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.crc16, - Value = crc16CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Crc32)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.crc32, - Value = crc32CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Crc64)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.crc64, - Value = crc64CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Md5)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.md5, - Value = md5CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Sha1)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.sha1, - Value = sha1CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Sha256)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.sha256, - Value = sha256CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Sha384)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.sha384, - Value = sha384CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Sha512)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.sha512, - Value = sha512CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.SpamSum)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.spamsum, - Value = ssctxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Fletcher16)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.fletcher16, - Value = f16CtxData.End() - }; - - dataChecksums.Add(chk); - } - - if(enabled.HasFlag(EnableChecksum.Fletcher32)) - { - chk = new ChecksumType - { - type = ChecksumTypeType.fletcher32, - Value = f32CtxData.End() - }; - - dataChecksums.Add(chk); - } - - return dataChecksums; - } - - #region Threading helpers - struct HashPacket - { - public IChecksum Context; - public byte[] Data; - } - - static void UpdateHash(object packet) => ((HashPacket)packet).Context.Update(((HashPacket)packet).Data); - #endregion Threading helpers + return chks; } + + internal static List GetChecksums(byte[] data, EnableChecksum enabled = EnableChecksum.All) + { + IChecksum adler32CtxData = null; + IChecksum crc16CtxData = null; + IChecksum crc32CtxData = null; + IChecksum crc64CtxData = null; + IChecksum md5CtxData = null; + IChecksum sha1CtxData = null; + IChecksum sha256CtxData = null; + IChecksum sha384CtxData = null; + IChecksum sha512CtxData = null; + IChecksum ssctxData = null; + IChecksum f16CtxData = null; + IChecksum f32CtxData = null; + + var adlerThreadData = new Thread(UpdateHash); + var crc16ThreadData = new Thread(UpdateHash); + var crc32ThreadData = new Thread(UpdateHash); + var crc64ThreadData = new Thread(UpdateHash); + var md5ThreadData = new Thread(UpdateHash); + var sha1ThreadData = new Thread(UpdateHash); + var sha256ThreadData = new Thread(UpdateHash); + var sha384ThreadData = new Thread(UpdateHash); + var sha512ThreadData = new Thread(UpdateHash); + var spamsumThreadData = new Thread(UpdateHash); + var f16ThreadData = new Thread(UpdateHash); + var f32ThreadData = new Thread(UpdateHash); + + if(enabled.HasFlag(EnableChecksum.Adler32)) + { + adler32CtxData = new Adler32Context(); + + var adlerPktData = new HashPacket + { + Context = adler32CtxData, + Data = data + }; + + adlerThreadData.Start(adlerPktData); + } + + if(enabled.HasFlag(EnableChecksum.Crc16)) + { + crc16CtxData = new CRC16IBMContext(); + + var crc16PktData = new HashPacket + { + Context = crc16CtxData, + Data = data + }; + + crc16ThreadData.Start(crc16PktData); + } + + if(enabled.HasFlag(EnableChecksum.Crc32)) + { + crc32CtxData = new Crc32Context(); + + var crc32PktData = new HashPacket + { + Context = crc32CtxData, + Data = data + }; + + crc32ThreadData.Start(crc32PktData); + } + + if(enabled.HasFlag(EnableChecksum.Crc64)) + { + crc64CtxData = new Crc64Context(); + + var crc64PktData = new HashPacket + { + Context = crc64CtxData, + Data = data + }; + + crc64ThreadData.Start(crc64PktData); + } + + if(enabled.HasFlag(EnableChecksum.Md5)) + { + md5CtxData = new Md5Context(); + + var md5PktData = new HashPacket + { + Context = md5CtxData, + Data = data + }; + + md5ThreadData.Start(md5PktData); + } + + if(enabled.HasFlag(EnableChecksum.Sha1)) + { + sha1CtxData = new Sha1Context(); + + var sha1PktData = new HashPacket + { + Context = sha1CtxData, + Data = data + }; + + sha1ThreadData.Start(sha1PktData); + } + + if(enabled.HasFlag(EnableChecksum.Sha256)) + { + sha256CtxData = new Sha256Context(); + + var sha256PktData = new HashPacket + { + Context = sha256CtxData, + Data = data + }; + + sha256ThreadData.Start(sha256PktData); + } + + if(enabled.HasFlag(EnableChecksum.Sha384)) + { + sha384CtxData = new Sha384Context(); + + var sha384PktData = new HashPacket + { + Context = sha384CtxData, + Data = data + }; + + sha384ThreadData.Start(sha384PktData); + } + + if(enabled.HasFlag(EnableChecksum.Sha512)) + { + sha512CtxData = new Sha512Context(); + + var sha512PktData = new HashPacket + { + Context = sha512CtxData, + Data = data + }; + + sha512ThreadData.Start(sha512PktData); + } + + if(enabled.HasFlag(EnableChecksum.SpamSum)) + { + ssctxData = new SpamSumContext(); + + var spamsumPktData = new HashPacket + { + Context = ssctxData, + Data = data + }; + + spamsumThreadData.Start(spamsumPktData); + } + + if(enabled.HasFlag(EnableChecksum.Fletcher16)) + { + f16CtxData = new Fletcher16Context(); + + var f16PktData = new HashPacket + { + Context = f16CtxData, + Data = data + }; + + f16ThreadData.Start(f16PktData); + } + + if(enabled.HasFlag(EnableChecksum.Fletcher32)) + { + f32CtxData = new Fletcher32Context(); + + var f32PktData = new HashPacket + { + Context = f32CtxData, + Data = data + }; + + f32ThreadData.Start(f32PktData); + } + + while(adlerThreadData.IsAlive || + crc16ThreadData.IsAlive || + crc32ThreadData.IsAlive || + crc64ThreadData.IsAlive || + md5ThreadData.IsAlive || + sha1ThreadData.IsAlive || + sha256ThreadData.IsAlive || + sha384ThreadData.IsAlive || + sha512ThreadData.IsAlive || + spamsumThreadData.IsAlive || + f16ThreadData.IsAlive || + f32ThreadData.IsAlive) {} + + List dataChecksums = new List(); + ChecksumType chk; + + if(enabled.HasFlag(EnableChecksum.Adler32)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.adler32, + Value = adler32CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Crc16)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.crc16, + Value = crc16CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Crc32)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.crc32, + Value = crc32CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Crc64)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.crc64, + Value = crc64CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Md5)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.md5, + Value = md5CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Sha1)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.sha1, + Value = sha1CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Sha256)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.sha256, + Value = sha256CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Sha384)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.sha384, + Value = sha384CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Sha512)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.sha512, + Value = sha512CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.SpamSum)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.spamsum, + Value = ssctxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Fletcher16)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.fletcher16, + Value = f16CtxData.End() + }; + + dataChecksums.Add(chk); + } + + if(enabled.HasFlag(EnableChecksum.Fletcher32)) + { + chk = new ChecksumType + { + type = ChecksumTypeType.fletcher32, + Value = f32CtxData.End() + }; + + dataChecksums.Add(chk); + } + + return dataChecksums; + } + + #region Threading helpers + struct HashPacket + { + public IChecksum Context; + public byte[] Data; + } + + static void UpdateHash(object packet) => ((HashPacket)packet).Context.Update(((HashPacket)packet).Data); + #endregion Threading helpers } \ No newline at end of file diff --git a/Aaru.Core/DataFile.cs b/Aaru.Core/DataFile.cs index df6477aee..dcf40a524 100644 --- a/Aaru.Core/DataFile.cs +++ b/Aaru.Core/DataFile.cs @@ -34,127 +34,126 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Aaru.Console; -namespace Aaru.Core +namespace Aaru.Core; + +/// Abstracts a datafile with a block based interface +[SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global"), + SuppressMessage("ReSharper", "UnusedMember.Global")] +public sealed class DataFile { - /// Abstracts a datafile with a block based interface - [SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global"), - SuppressMessage("ReSharper", "UnusedMember.Global")] - public sealed class DataFile + readonly FileStream _dataFs; + + /// Opens, or create, a new file + /// File + public DataFile(string outputFile) => + _dataFs = new FileStream(outputFile, FileMode.OpenOrCreate, FileAccess.ReadWrite); + + /// Closes the file + public void Close() => _dataFs?.Close(); + + /// Reads bytes at current position + /// Array to place read data within + /// Offset of where data will be read + /// How many bytes to read + /// How many bytes were read + public int Read(byte[] array, int offset, int count) => _dataFs.Read(array, offset, count); + + /// Seeks to the specified block + /// Block to seek to + /// Block size in bytes + /// Position + public long Seek(ulong block, ulong blockSize) => _dataFs.Seek((long)(block * blockSize), SeekOrigin.Begin); + + /// Seeks to specified byte position + /// Byte position + /// Where to count for position + /// Position + public long Seek(ulong offset, SeekOrigin origin) => _dataFs.Seek((long)offset, origin); + + /// Seeks to specified byte position + /// Byte position + /// Where to count for position + /// Position + public long Seek(long offset, SeekOrigin origin) => _dataFs.Seek(offset, origin); + + /// Writes data at current position + /// Data + public void Write(byte[] data) => Write(data, 0, data.Length); + + /// Writes data at current position + /// Data + /// Offset of data from where to start taking data to write + /// How many bytes to write + public void Write(byte[] data, int offset, int count) => _dataFs.Write(data, offset, count); + + /// Writes data at specified block + /// Data + /// Block + /// Bytes per block + public void WriteAt(byte[] data, ulong block, uint blockSize) => + WriteAt(data, block, blockSize, 0, data.Length); + + /// Writes data at specified block + /// Data + /// Block + /// Bytes per block + /// Offset of data from where to start taking data to write + /// How many bytes to write + public void WriteAt(byte[] data, ulong block, uint blockSize, int offset, int count) { - readonly FileStream _dataFs; + _dataFs.Seek((long)(block * blockSize), SeekOrigin.Begin); + _dataFs.Write(data, offset, count); + } - /// Opens, or create, a new file - /// File - public DataFile(string outputFile) => - _dataFs = new FileStream(outputFile, FileMode.OpenOrCreate, FileAccess.ReadWrite); + /// Current file position + public long Position => _dataFs.Position; - /// Closes the file - public void Close() => _dataFs?.Close(); + /// Writes data to a newly created file + /// Who asked the file to be written (class, plugin, etc.) + /// Data to write + /// First part of the file name + /// Last part of the file name + /// What is the data about? + public static void WriteTo(string who, string outputPrefix, string outputSuffix, string whatWriting, + byte[] data) + { + if(!string.IsNullOrEmpty(outputPrefix) && + !string.IsNullOrEmpty(outputSuffix)) + WriteTo(who, outputPrefix + outputSuffix, data, whatWriting); + } - /// Reads bytes at current position - /// Array to place read data within - /// Offset of where data will be read - /// How many bytes to read - /// How many bytes were read - public int Read(byte[] array, int offset, int count) => _dataFs.Read(array, offset, count); + /// Writes data to a newly created file + /// Who asked the file to be written (class, plugin, etc.) + /// Filename to create + /// Data to write + /// What is the data about? + /// If set to true overwrites the file, does nothing otherwise + public static void WriteTo(string who, string filename, byte[] data, string whatWriting = null, + bool overwrite = false) + { + if(string.IsNullOrEmpty(filename)) + return; - /// Seeks to the specified block - /// Block to seek to - /// Block size in bytes - /// Position - public long Seek(ulong block, ulong blockSize) => _dataFs.Seek((long)(block * blockSize), SeekOrigin.Begin); + if(File.Exists(filename)) + if(overwrite) + File.Delete(filename); + else + { + AaruConsole.ErrorWriteLine("Not overwriting file {0}", filename); - /// Seeks to specified byte position - /// Byte position - /// Where to count for position - /// Position - public long Seek(ulong offset, SeekOrigin origin) => _dataFs.Seek((long)offset, origin); - - /// Seeks to specified byte position - /// Byte position - /// Where to count for position - /// Position - public long Seek(long offset, SeekOrigin origin) => _dataFs.Seek(offset, origin); - - /// Writes data at current position - /// Data - public void Write(byte[] data) => Write(data, 0, data.Length); - - /// Writes data at current position - /// Data - /// Offset of data from where to start taking data to write - /// How many bytes to write - public void Write(byte[] data, int offset, int count) => _dataFs.Write(data, offset, count); - - /// Writes data at specified block - /// Data - /// Block - /// Bytes per block - public void WriteAt(byte[] data, ulong block, uint blockSize) => - WriteAt(data, block, blockSize, 0, data.Length); - - /// Writes data at specified block - /// Data - /// Block - /// Bytes per block - /// Offset of data from where to start taking data to write - /// How many bytes to write - public void WriteAt(byte[] data, ulong block, uint blockSize, int offset, int count) - { - _dataFs.Seek((long)(block * blockSize), SeekOrigin.Begin); - _dataFs.Write(data, offset, count); - } - - /// Current file position - public long Position => _dataFs.Position; - - /// Writes data to a newly created file - /// Who asked the file to be written (class, plugin, etc.) - /// Data to write - /// First part of the file name - /// Last part of the file name - /// What is the data about? - public static void WriteTo(string who, string outputPrefix, string outputSuffix, string whatWriting, - byte[] data) - { - if(!string.IsNullOrEmpty(outputPrefix) && - !string.IsNullOrEmpty(outputSuffix)) - WriteTo(who, outputPrefix + outputSuffix, data, whatWriting); - } - - /// Writes data to a newly created file - /// Who asked the file to be written (class, plugin, etc.) - /// Filename to create - /// Data to write - /// What is the data about? - /// If set to true overwrites the file, does nothing otherwise - public static void WriteTo(string who, string filename, byte[] data, string whatWriting = null, - bool overwrite = false) - { - if(string.IsNullOrEmpty(filename)) return; - - if(File.Exists(filename)) - if(overwrite) - File.Delete(filename); - else - { - AaruConsole.ErrorWriteLine("Not overwriting file {0}", filename); - - return; - } - - try - { - AaruConsole.DebugWriteLine(who, "Writing " + whatWriting + " to {0}", filename); - var outputFs = new FileStream(filename, FileMode.CreateNew); - outputFs.Write(data, 0, data.Length); - outputFs.Close(); - } - catch - { - AaruConsole.ErrorWriteLine("Unable to write file {0}", filename); } + + try + { + AaruConsole.DebugWriteLine(who, "Writing " + whatWriting + " to {0}", filename); + var outputFs = new FileStream(filename, FileMode.CreateNew); + outputFs.Write(data, 0, data.Length); + outputFs.Close(); + } + catch + { + AaruConsole.ErrorWriteLine("Unable to write file {0}", filename); } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/ATA.cs b/Aaru.Core/Devices/Dumping/ATA.cs index a2e454b99..03f10035c 100644 --- a/Aaru.Core/Devices/Dumping/ATA.cs +++ b/Aaru.Core/Devices/Dumping/ATA.cs @@ -48,225 +48,331 @@ using Identify = Aaru.CommonTypes.Structs.Devices.ATA.Identify; using Tuple = Aaru.Decoders.PCMCIA.Tuple; using Version = Aaru.CommonTypes.Interop.Version; -namespace Aaru.Core.Devices.Dumping -{ - /// Implements dumping ATA devices - public partial class Dump - { - /// Dumps an ATA device - void Ata() - { - bool recoveredError; - var outputFormat = _outputPlugin as IWritableImage; +namespace Aaru.Core.Devices.Dumping; - if(_dumpRaw) +/// Implements dumping ATA devices +public partial class Dump +{ + /// Dumps an ATA device + void Ata() + { + bool recoveredError; + var outputFormat = _outputPlugin as IWritableImage; + + if(_dumpRaw) + { + if(_force) + ErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, continuing..."); + else { - if(_force) - ErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, continuing..."); - else + StoppingErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, aborting..."); + + return; + } + } + + const ushort ataProfile = 0x0001; + const uint timeout = 5; + double imageWriteDuration = 0; + MediaType mediaType = MediaType.Unknown; + + UpdateStatus?.Invoke("Requesting ATA IDENTIFY DEVICE."); + _dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE."); + bool sense = _dev.AtaIdentify(out byte[] cmdBuf, out AtaErrorRegistersChs errorChs); + + if(sense) + _errorLog?.WriteLine("ATA IDENTIFY DEVICE", _dev.Error, _dev.LastError, errorChs); + else if(Identify.Decode(cmdBuf).HasValue) + { + Identify.IdentifyDevice? ataIdNullable = Identify.Decode(cmdBuf); + + if(ataIdNullable != null) + { + Identify.IdentifyDevice ataId = ataIdNullable.Value; + byte[] ataIdentify = cmdBuf; + cmdBuf = Array.Empty(); + + DateTime start; + DateTime end; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + + // Initialize reader + UpdateStatus?.Invoke("Initializing reader."); + _dumpLog.WriteLine("Initializing reader."); + var ataReader = new Reader(_dev, timeout, ataIdentify, _errorLog); + + // Fill reader blocks + ulong blocks = ataReader.GetDeviceBlocks(); + + // Check block sizes + if(ataReader.GetBlockSize()) { - StoppingErrorMessage?.Invoke("Raw dumping not yet supported in ATA devices, aborting..."); + _dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage); + ErrorMessage(ataReader.ErrorMessage); return; } - } - const ushort ataProfile = 0x0001; - const uint timeout = 5; - double imageWriteDuration = 0; - MediaType mediaType = MediaType.Unknown; + uint blockSize = ataReader.LogicalBlockSize; + uint physicalSectorSize = ataReader.PhysicalBlockSize; - UpdateStatus?.Invoke("Requesting ATA IDENTIFY DEVICE."); - _dumpLog.WriteLine("Requesting ATA IDENTIFY DEVICE."); - bool sense = _dev.AtaIdentify(out byte[] cmdBuf, out AtaErrorRegistersChs errorChs); - - if(sense) - _errorLog?.WriteLine("ATA IDENTIFY DEVICE", _dev.Error, _dev.LastError, errorChs); - else if(Identify.Decode(cmdBuf).HasValue) - { - Identify.IdentifyDevice? ataIdNullable = Identify.Decode(cmdBuf); - - if(ataIdNullable != null) + if(ataReader.FindReadCommand()) { - Identify.IdentifyDevice ataId = ataIdNullable.Value; - byte[] ataIdentify = cmdBuf; - cmdBuf = Array.Empty(); + _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage); + ErrorMessage(ataReader.ErrorMessage); - DateTime start; - DateTime end; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; + return; + } - // Initialize reader - UpdateStatus?.Invoke("Initializing reader."); - _dumpLog.WriteLine("Initializing reader."); - var ataReader = new Reader(_dev, timeout, ataIdentify, _errorLog); + // Check how many blocks to read, if error show and return + if(ataReader.GetBlocksToRead(_maximumReadable)) + { + _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage); + ErrorMessage(ataReader.ErrorMessage); - // Fill reader blocks - ulong blocks = ataReader.GetDeviceBlocks(); + return; + } - // Check block sizes - if(ataReader.GetBlockSize()) + uint blocksToRead = ataReader.BlocksToRead; + ushort cylinders = ataReader.Cylinders; + byte heads = ataReader.Heads; + byte sectors = ataReader.Sectors; + + UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); + + UpdateStatus?. + Invoke($"Device reports {cylinders} cylinders {heads} heads {sectors} sectors per track."); + + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); + UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); + UpdateStatus?.Invoke($"Device reports {physicalSectorSize} bytes per physical block."); + _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); + + _dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders, + heads, sectors); + + _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); + _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); + _dumpLog.WriteLine("Device reports {0} bytes per physical block.", physicalSectorSize); + + bool removable = !_dev.IsCompactFlash && + ataId.GeneralConfiguration.HasFlag(Identify.GeneralConfigurationBit.Removable); + + DumpHardwareType currentTry = null; + ExtentsULong extents = null; + + ResumeSupport.Process(ataReader.IsLba, removable, blocks, _dev.Manufacturer, _dev.Model, + _dev.Serial, _dev.PlatformId, ref _resume, ref currentTry, ref extents, + _dev.FirmwareRevision, _private, _force); + + if(currentTry == null || + extents == null) + { + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + + return; + } + + MhddLog mhddLog; + IbgLog ibgLog; + double duration; + + bool ret = true; + + if(_dev.IsUsb && + _dev.UsbDescriptors != null && + !outputFormat.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) + { + ret = false; + _dumpLog.WriteLine("Output format does not support USB descriptors."); + ErrorMessage("Output format does not support USB descriptors."); + } + + if(_dev.IsPcmcia && + _dev.Cis != null && + !outputFormat.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS)) + { + ret = false; + _dumpLog.WriteLine("Output format does not support PCMCIA CIS descriptors."); + ErrorMessage("Output format does not support PCMCIA CIS descriptors."); + } + + if(!outputFormat.SupportedMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) + { + ret = false; + _dumpLog.WriteLine("Output format does not support ATA IDENTIFY."); + ErrorMessage("Output format does not support ATA IDENTIFY."); + } + + if(!ret) + { + _dumpLog.WriteLine("Several media tags not supported, {0}continuing...", _force ? "" : "not "); + + if(_force) + ErrorMessage("Several media tags not supported, continuing..."); + else { - _dumpLog.WriteLine("ERROR: Cannot get block size: {0}.", ataReader.ErrorMessage); - ErrorMessage(ataReader.ErrorMessage); + StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); return; } + } - uint blockSize = ataReader.LogicalBlockSize; - uint physicalSectorSize = ataReader.PhysicalBlockSize; + mediaType = MediaTypeFromDevice.GetFromAta(_dev.Manufacturer, _dev.Model, _dev.IsRemovable, + _dev.IsCompactFlash, _dev.IsPcmcia, blocks); - if(ataReader.FindReadCommand()) + ret = outputFormat.Create(_outputPath, mediaType, _formatOptions, blocks, blockSize); + + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputFormat.ErrorMessage); + + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + + Environment.NewLine + outputFormat.ErrorMessage); + + return; + } + + // Setting geometry + outputFormat.SetGeometry(cylinders, heads, sectors); + + if(ataReader.IsLba) + { + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + + if(_skip < blocksToRead) + _skip = blocksToRead; + + mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, + _private); + + ibgLog = new IbgLog(_outputPrefix + ".ibg", ataProfile); + + if(_resume.NextBlock > 0) { - _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", ataReader.ErrorMessage); - ErrorMessage(ataReader.ErrorMessage); - - return; + UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); + _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); } - // Check how many blocks to read, if error show and return - if(ataReader.GetBlocksToRead(_maximumReadable)) + bool newTrim = false; + + start = DateTime.UtcNow; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) { - _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", ataReader.ErrorMessage); - ErrorMessage(ataReader.ErrorMessage); + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - return; - } + break; + } - uint blocksToRead = ataReader.BlocksToRead; - ushort cylinders = ataReader.Cylinders; - byte heads = ataReader.Heads; - byte sectors = ataReader.Sectors; + if(blocks - i < blocksToRead) + blocksToRead = (byte)(blocks - i); - UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; - UpdateStatus?. - Invoke($"Device reports {cylinders} cylinders {heads} heads {sectors} sectors per track."); + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); - UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); - UpdateStatus?.Invoke($"Device reports {physicalSectorSize} bytes per physical block."); - _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", + (long)i, (long)blocks); - _dumpLog.WriteLine("Device reports {0} cylinders {1} heads {2} sectors per track.", cylinders, - heads, sectors); + bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration, out _, out _); - _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); - _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); - _dumpLog.WriteLine("Device reports {0} bytes per physical block.", physicalSectorSize); - - bool removable = !_dev.IsCompactFlash && - ataId.GeneralConfiguration.HasFlag(Identify.GeneralConfigurationBit.Removable); - - DumpHardwareType currentTry = null; - ExtentsULong extents = null; - - ResumeSupport.Process(ataReader.IsLba, removable, blocks, _dev.Manufacturer, _dev.Model, - _dev.Serial, _dev.PlatformId, ref _resume, ref currentTry, ref extents, - _dev.FirmwareRevision, _private, _force); - - if(currentTry == null || - extents == null) - { - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); - - return; - } - - MhddLog mhddLog; - IbgLog ibgLog; - double duration; - - bool ret = true; - - if(_dev.IsUsb && - _dev.UsbDescriptors != null && - !outputFormat.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) - { - ret = false; - _dumpLog.WriteLine("Output format does not support USB descriptors."); - ErrorMessage("Output format does not support USB descriptors."); - } - - if(_dev.IsPcmcia && - _dev.Cis != null && - !outputFormat.SupportedMediaTags.Contains(MediaTagType.PCMCIA_CIS)) - { - ret = false; - _dumpLog.WriteLine("Output format does not support PCMCIA CIS descriptors."); - ErrorMessage("Output format does not support PCMCIA CIS descriptors."); - } - - if(!outputFormat.SupportedMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) - { - ret = false; - _dumpLog.WriteLine("Output format does not support ATA IDENTIFY."); - ErrorMessage("Output format does not support ATA IDENTIFY."); - } - - if(!ret) - { - _dumpLog.WriteLine("Several media tags not supported, {0}continuing...", _force ? "" : "not "); - - if(_force) - ErrorMessage("Several media tags not supported, continuing..."); + if(!error) + { + mhddLog.Write(i, duration); + ibgLog.Write(i, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(cmdBuf, i, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(i, blocksToRead, true); + } else { - StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + if(i + _skip > blocks) + _skip = (uint)(blocks - i); - return; - } - } + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); - mediaType = MediaTypeFromDevice.GetFromAta(_dev.Manufacturer, _dev.Model, _dev.IsRemovable, - _dev.IsCompactFlash, _dev.IsPcmcia, blocks); + mhddLog.Write(i, duration < 500 ? 65535 : duration); - ret = outputFormat.Create(_outputPath, mediaType, _formatOptions, blocks, blockSize); - - // Cannot create image - if(!ret) - { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputFormat.ErrorMessage); - - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + - Environment.NewLine + outputFormat.ErrorMessage); - - return; - } - - // Setting geometry - outputFormat.SetGeometry(cylinders, heads, sectors); - - if(ataReader.IsLba) - { - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); - - if(_skip < blocksToRead) - _skip = blocksToRead; - - mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, - _private); - - ibgLog = new IbgLog(_outputPrefix + ".ibg", ataProfile); - - if(_resume.NextBlock > 0) - { - UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); - _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + ibgLog.Write(i, 0); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; } - bool newTrim = false; + sectorSpeedStart += blocksToRead; + _resume.NextBlock = i + blocksToRead; + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + + end = DateTime.Now; + EndProgress?.Invoke(); + mhddLog.Close(); + + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + + #region Trimming + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _trim && + newTrim) + { start = DateTime.UtcNow; - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; + UpdateStatus?.Invoke("Trimming skipped sectors"); + _dumpLog.WriteLine("Trimming skipped sectors"); + + ulong[] tmpArray = _resume.BadBlocks.ToArray(); InitProgress?.Invoke(); - for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + foreach(ulong badSector in tmpArray) { if(_aborted) { @@ -277,147 +383,111 @@ namespace Aaru.Core.Devices.Dumping break; } - if(blocks - i < blocksToRead) - blocksToRead = (byte)(blocks - i); + PulseProgress?.Invoke($"Trimming sector {badSector}"); - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; + bool error = + ataReader.ReadBlock(out cmdBuf, badSector, out duration, out recoveredError, out _); - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; + totalDuration += duration; - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", - (long)i, (long)blocks); - - bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration, out _, out _); - - if(!error) - { - mhddLog.Write(i, duration); - ibgLog.Write(i, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(cmdBuf, i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(i, blocksToRead, true); - } - else - { - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - mhddLog.Write(i, duration < 500 ? 65535 : duration); - - ibgLog.Write(i, 0); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; - } - - sectorSpeedStart += blocksToRead; - _resume.NextBlock = i + blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) + if(error && !recoveredError) continue; - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(cmdBuf, badSector); } - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - - end = DateTime.Now; EndProgress?.Invoke(); - mhddLog.Close(); + end = DateTime.UtcNow; + UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); + _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + } + #endregion Trimming - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + #region Error handling + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _retryPasses > 0) + { + int pass = 1; + bool forward = true; - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + InitProgress?.Invoke(); + repeatRetryLba: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - #region Trimming - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _trim && - newTrim) + foreach(ulong badSector in tmpArray) { - start = DateTime.UtcNow; - UpdateStatus?.Invoke("Trimming skipped sectors"); - _dumpLog.WriteLine("Trimming skipped sectors"); - - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - InitProgress?.Invoke(); - - foreach(ulong badSector in tmpArray) + if(_aborted) { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - break; - } + break; + } - PulseProgress?.Invoke($"Trimming sector {badSector}"); + PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, + pass, forward ? "forward" : "reverse", + _persistent ? "recovering partial data, " : "")); - bool error = - ataReader.ReadBlock(out cmdBuf, badSector, out duration, out recoveredError, out _); + bool error = + ataReader.ReadBlock(out cmdBuf, badSector, out duration, out recoveredError, out _); - totalDuration += duration; - - if(error && !recoveredError) - continue; + totalDuration += duration; + if(!error || recoveredError) + { _resume.BadBlocks.Remove(badSector); extents.Add(badSector); outputFormat.WriteSector(cmdBuf, badSector); + UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } - - EndProgress?.Invoke(); - end = DateTime.UtcNow; - UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); - _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + else if(_persistent) + outputFormat.WriteSector(cmdBuf, badSector); } - #endregion Trimming - #region Error handling - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _retryPasses > 0) + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) { - int pass = 1; - bool forward = true; + pass++; + forward = !forward; + _resume.BadBlocks.Sort(); - InitProgress?.Invoke(); - repeatRetryLba: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); + if(!forward) + _resume.BadBlocks.Reverse(); - foreach(ulong badSector in tmpArray) + goto repeatRetryLba; + } + + EndProgress?.Invoke(); + } + #endregion Error handling LBA + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + } + else + { + mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, + _private); + + ibgLog = new IbgLog(_outputPrefix + ".ibg", ataProfile); + + ulong currentBlock = 0; + blocks = (ulong)(cylinders * heads * sectors); + start = DateTime.UtcNow; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(ushort cy = 0; cy < cylinders; cy++) + { + for(byte hd = 0; hd < heads; hd++) + { + for(byte sc = 1; sc < sectors; sc++) { if(_aborted) { @@ -428,436 +498,365 @@ namespace Aaru.Core.Devices.Dumping break; } - PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, - pass, forward ? "forward" : "reverse", - _persistent ? "recovering partial data, " : "")); + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + PulseProgress?. + Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)"); bool error = - ataReader.ReadBlock(out cmdBuf, badSector, out duration, out recoveredError, out _); + ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration, out recoveredError); totalDuration += duration; if(!error || recoveredError) { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(cmdBuf, badSector); - UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); + mhddLog.Write(currentBlock, duration); + ibgLog.Write(currentBlock, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + + outputFormat.WriteSector(cmdBuf, + (ulong)((((cy * heads) + hd) * sectors) + (sc - 1))); + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(currentBlock); + + _dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd, + sc); } - else if(_persistent) - outputFormat.WriteSector(cmdBuf, badSector); - } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto repeatRetryLba; - } - - EndProgress?.Invoke(); - } - #endregion Error handling LBA - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - } - else - { - mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, - _private); - - ibgLog = new IbgLog(_outputPrefix + ".ibg", ataProfile); - - ulong currentBlock = 0; - blocks = (ulong)(cylinders * heads * sectors); - start = DateTime.UtcNow; - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; - InitProgress?.Invoke(); - - for(ushort cy = 0; cy < cylinders; cy++) - { - for(byte hd = 0; hd < heads; hd++) - { - for(byte sc = 1; sc < sectors; sc++) + else { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + _resume.BadBlocks.Add(currentBlock); + mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration); - break; - } + ibgLog.Write(currentBlock, 0); + DateTime writeStart = DateTime.Now; - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; + outputFormat.WriteSector(new byte[blockSize], + (ulong)((((cy * heads) + hd) * sectors) + (sc - 1))); - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - PulseProgress?. - Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)"); - - bool error = - ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration, out recoveredError); - - totalDuration += duration; - - if(!error || recoveredError) - { - mhddLog.Write(currentBlock, duration); - ibgLog.Write(currentBlock, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - - outputFormat.WriteSector(cmdBuf, - (ulong)((((cy * heads) + hd) * sectors) + (sc - 1))); - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(currentBlock); - - _dumpLog.WriteLine("Error reading cylinder {0} head {1} sector {2}.", cy, hd, - sc); - } - else - { - _resume.BadBlocks.Add(currentBlock); - mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration); - - ibgLog.Write(currentBlock, 0); - DateTime writeStart = DateTime.Now; - - outputFormat.WriteSector(new byte[blockSize], - (ulong)((((cy * heads) + hd) * sectors) + (sc - 1))); - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - } - - sectorSpeedStart++; - currentBlock++; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } + + sectorSpeedStart++; + currentBlock++; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; } } - - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - - end = DateTime.Now; - EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / (imageWriteDuration / 1000):F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (imageWriteDuration / 1000)); } - foreach(ulong bad in _resume.BadBlocks) - _dumpLog.WriteLine("Sector {0} could not be read.", bad); + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - outputFormat.SetDumpHardware(_resume.Tries); + end = DateTime.Now; + EndProgress?.Invoke(); + mhddLog.Close(); - // TODO: Non-removable - var metadata = new CommonTypes.Structs.ImageInfo + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / (imageWriteDuration / 1000):F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (imageWriteDuration / 1000)); + } + + foreach(ulong bad in _resume.BadBlocks) + _dumpLog.WriteLine("Sector {0} could not be read.", bad); + + outputFormat.SetDumpHardware(_resume.Tries); + + // TODO: Non-removable + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion() + }; + + if(!outputFormat.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputFormat.ErrorMessage); + + if(_preSidecar != null) + outputFormat.SetCicmMetadata(_preSidecar); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputFormat.Close(); + DateTime closeEnd = DateTime.Now; + UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); + _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + + if(_aborted) + { + _dumpLog.WriteLine("Aborted!"); + UpdateStatus?.Invoke("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + outputFormat.WriteMediaTag(ataIdentify, MediaTagType.ATA_IDENTIFY); + + if(_dev.IsUsb && + _dev.UsbDescriptors != null) + outputFormat.WriteMediaTag(_dev.UsbDescriptors, MediaTagType.USB_Descriptors); + + if(_dev.IsPcmcia && + _dev.Cis != null) + outputFormat.WriteMediaTag(_dev.Cis, MediaTagType.PCMCIA_CIS); + + if(_metadata) + { + _dumpLog.WriteLine("Creating sidecar."); + UpdateStatus?.Invoke("Creating sidecar."); + var filters = new FiltersList(); + IFilter filter = filters.GetFilter(_outputPath); + IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; + ErrorNumber opened = inputPlugin.Open(filter); + + if(opened != ErrorNumber.NoError) { - Application = "Aaru", - ApplicationVersion = Version.GetVersion() - }; - - if(!outputFormat.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputFormat.ErrorMessage); - - if(_preSidecar != null) - outputFormat.SetCicmMetadata(_preSidecar); - - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputFormat.Close(); - DateTime closeEnd = DateTime.Now; - UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); - - if(_aborted) - { - _dumpLog.WriteLine("Aborted!"); - UpdateStatus?.Invoke("Aborted!"); + StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); return; } - double totalChkDuration = 0; + DateTime chkStart = DateTime.UtcNow; - outputFormat.WriteMediaTag(ataIdentify, MediaTagType.ATA_IDENTIFY); + _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); - if(_dev.IsUsb && - _dev.UsbDescriptors != null) - outputFormat.WriteMediaTag(_dev.UsbDescriptors, MediaTagType.USB_Descriptors); + _sidecarClass.InitProgressEvent += InitProgress; + _sidecarClass.UpdateProgressEvent += UpdateProgress; + _sidecarClass.EndProgressEvent += EndProgress; + _sidecarClass.InitProgressEvent2 += InitProgress2; + _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; + _sidecarClass.EndProgressEvent2 += EndProgress2; + _sidecarClass.UpdateStatusEvent += UpdateStatus; + CICMMetadataType sidecar = _sidecarClass.Create(); - if(_dev.IsPcmcia && - _dev.Cis != null) - outputFormat.WriteMediaTag(_dev.Cis, MediaTagType.PCMCIA_CIS); - - if(_metadata) + if(!_aborted) { - _dumpLog.WriteLine("Creating sidecar."); - UpdateStatus?.Invoke("Creating sidecar."); - var filters = new FiltersList(); - IFilter filter = filters.GetFilter(_outputPath); - IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; - ErrorNumber opened = inputPlugin.Open(filter); - - if(opened != ErrorNumber.NoError) + if(_preSidecar != null) { - StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); - - return; + _preSidecar.BlockMedia = sidecar.BlockMedia; + sidecar = _preSidecar; } - DateTime chkStart = DateTime.UtcNow; - - _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); - - _sidecarClass.InitProgressEvent += InitProgress; - _sidecarClass.UpdateProgressEvent += UpdateProgress; - _sidecarClass.EndProgressEvent += EndProgress; - _sidecarClass.InitProgressEvent2 += InitProgress2; - _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; - _sidecarClass.EndProgressEvent2 += EndProgress2; - _sidecarClass.UpdateStatusEvent += UpdateStatus; - CICMMetadataType sidecar = _sidecarClass.Create(); - - if(!_aborted) + if(_dev.IsUsb && + _dev.UsbDescriptors != null) { - if(_preSidecar != null) + _dumpLog.WriteLine("Reading USB descriptors."); + UpdateStatus?.Invoke("Reading USB descriptors."); + + sidecar.BlockMedia[0].USB = new USBType { - _preSidecar.BlockMedia = sidecar.BlockMedia; - sidecar = _preSidecar; - } - - if(_dev.IsUsb && - _dev.UsbDescriptors != null) - { - _dumpLog.WriteLine("Reading USB descriptors."); - UpdateStatus?.Invoke("Reading USB descriptors."); - - sidecar.BlockMedia[0].USB = new USBType - { - ProductID = _dev.UsbProductId, - VendorID = _dev.UsbVendorId, - Descriptors = new DumpType - { - Image = _outputPath, - Size = (ulong)_dev.UsbDescriptors.Length, - Checksums = Checksum.GetChecksums(_dev.UsbDescriptors).ToArray() - } - }; - } - - if(_dev.IsPcmcia && - _dev.Cis != null) - { - _dumpLog.WriteLine("Reading PCMCIA CIS."); - UpdateStatus?.Invoke("Reading PCMCIA CIS."); - - sidecar.BlockMedia[0].PCMCIA = new PCMCIAType - { - CIS = new DumpType - { - Image = _outputPath, - Size = (ulong)_dev.Cis.Length, - Checksums = Checksum.GetChecksums(_dev.Cis).ToArray() - } - }; - - _dumpLog.WriteLine("Decoding PCMCIA CIS."); - UpdateStatus?.Invoke("Decoding PCMCIA CIS."); - Tuple[] tuples = CIS.GetTuples(_dev.Cis); - - if(tuples != null) - foreach(Tuple tuple in tuples) - switch(tuple.Code) - { - case TupleCodes.CISTPL_MANFID: - ManufacturerIdentificationTuple manufacturerId = - CIS.DecodeManufacturerIdentificationTuple(tuple); - - if(manufacturerId != null) - { - sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = - manufacturerId.ManufacturerID; - - sidecar.BlockMedia[0].PCMCIA.CardCode = manufacturerId.CardID; - sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true; - sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified = true; - } - - break; - case TupleCodes.CISTPL_VERS_1: - Level1VersionTuple version = CIS.DecodeLevel1VersionTuple(tuple); - - if(version != null) - { - sidecar.BlockMedia[0].PCMCIA.Manufacturer = version.Manufacturer; - sidecar.BlockMedia[0].PCMCIA.ProductName = version.Product; - - sidecar.BlockMedia[0].PCMCIA.Compliance = - $"{version.MajorVersion}.{version.MinorVersion}"; - - sidecar.BlockMedia[0].PCMCIA.AdditionalInformation = - version.AdditionalInformation; - } - - break; - } - } - - if(_private) - DeviceReport.ClearIdentify(ataIdentify); - - sidecar.BlockMedia[0].ATA = new ATAType - { - Identify = new DumpType + ProductID = _dev.UsbProductId, + VendorID = _dev.UsbVendorId, + Descriptors = new DumpType { Image = _outputPath, - Size = (ulong)cmdBuf.Length, - Checksums = Checksum.GetChecksums(cmdBuf).ToArray() + Size = (ulong)_dev.UsbDescriptors.Length, + Checksums = Checksum.GetChecksums(_dev.UsbDescriptors).ToArray() + } + }; + } + + if(_dev.IsPcmcia && + _dev.Cis != null) + { + _dumpLog.WriteLine("Reading PCMCIA CIS."); + UpdateStatus?.Invoke("Reading PCMCIA CIS."); + + sidecar.BlockMedia[0].PCMCIA = new PCMCIAType + { + CIS = new DumpType + { + Image = _outputPath, + Size = (ulong)_dev.Cis.Length, + Checksums = Checksum.GetChecksums(_dev.Cis).ToArray() } }; - DateTime chkEnd = DateTime.UtcNow; + _dumpLog.WriteLine("Decoding PCMCIA CIS."); + UpdateStatus?.Invoke("Decoding PCMCIA CIS."); + Tuple[] tuples = CIS.GetTuples(_dev.Cis); - totalChkDuration = (chkEnd - chkStart).TotalMilliseconds; - UpdateStatus?.Invoke($"Sidecar created in {(chkEnd - chkStart).TotalSeconds} seconds."); + if(tuples != null) + foreach(Tuple tuple in tuples) + switch(tuple.Code) + { + case TupleCodes.CISTPL_MANFID: + ManufacturerIdentificationTuple manufacturerId = + CIS.DecodeManufacturerIdentificationTuple(tuple); - UpdateStatus?. - Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + if(manufacturerId != null) + { + sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = + manufacturerId.ManufacturerID; - _dumpLog.WriteLine("Sidecar created in {0} seconds.", (chkEnd - chkStart).TotalSeconds); + sidecar.BlockMedia[0].PCMCIA.CardCode = manufacturerId.CardID; + sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true; + sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified = true; + } - _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + break; + case TupleCodes.CISTPL_VERS_1: + Level1VersionTuple version = CIS.DecodeLevel1VersionTuple(tuple); - List<(ulong start, string type)> filesystems = new(); + if(version != null) + { + sidecar.BlockMedia[0].PCMCIA.Manufacturer = version.Manufacturer; + sidecar.BlockMedia[0].PCMCIA.ProductName = version.Product; - if(sidecar.BlockMedia[0].FileSystemInformation != null) - filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation - where partition.FileSystems != null - from fileSystem in partition.FileSystems - select (partition.StartSector, fileSystem.Type)); + sidecar.BlockMedia[0].PCMCIA.Compliance = + $"{version.MajorVersion}.{version.MinorVersion}"; - if(filesystems.Count > 0) - foreach(var filesystem in filesystems.Select(o => new - { - o.start, - o.type - }).Distinct()) - { - UpdateStatus?. - Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); + sidecar.BlockMedia[0].PCMCIA.AdditionalInformation = + version.AdditionalInformation; + } - _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, - filesystem.start); - } + break; + } + } - (string type, string subType) = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType); + if(_private) + DeviceReport.ClearIdentify(ataIdentify); - sidecar.BlockMedia[0].DiskType = type; - sidecar.BlockMedia[0].DiskSubType = subType; - sidecar.BlockMedia[0].Interface = "ATA"; - sidecar.BlockMedia[0].LogicalBlocks = blocks; - sidecar.BlockMedia[0].PhysicalBlockSize = physicalSectorSize; - sidecar.BlockMedia[0].LogicalBlockSize = blockSize; - sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; - sidecar.BlockMedia[0].Model = _dev.Model; - - if(!_private) - sidecar.BlockMedia[0].Serial = _dev.Serial; - - sidecar.BlockMedia[0].Size = blocks * blockSize; - - if(cylinders > 0 && - heads > 0 && - sectors > 0) + sidecar.BlockMedia[0].ATA = new ATAType + { + Identify = new DumpType { - sidecar.BlockMedia[0].Cylinders = cylinders; - sidecar.BlockMedia[0].CylindersSpecified = true; - sidecar.BlockMedia[0].Heads = heads; - sidecar.BlockMedia[0].HeadsSpecified = true; - sidecar.BlockMedia[0].SectorsPerTrack = sectors; - sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; + Image = _outputPath, + Size = (ulong)cmdBuf.Length, + Checksums = Checksum.GetChecksums(cmdBuf).ToArray() + } + }; + + DateTime chkEnd = DateTime.UtcNow; + + totalChkDuration = (chkEnd - chkStart).TotalMilliseconds; + UpdateStatus?.Invoke($"Sidecar created in {(chkEnd - chkStart).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + + _dumpLog.WriteLine("Sidecar created in {0} seconds.", (chkEnd - chkStart).TotalSeconds); + + _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + + List<(ulong start, string type)> filesystems = new(); + + if(sidecar.BlockMedia[0].FileSystemInformation != null) + filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation + where partition.FileSystems != null + from fileSystem in partition.FileSystems + select (partition.StartSector, fileSystem.Type)); + + if(filesystems.Count > 0) + foreach(var filesystem in filesystems.Select(o => new + { + o.start, + o.type + }).Distinct()) + { + UpdateStatus?. + Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); + + _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, + filesystem.start); } - UpdateStatus?.Invoke("Writing metadata sidecar"); + (string type, string subType) = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType); - var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); + sidecar.BlockMedia[0].DiskType = type; + sidecar.BlockMedia[0].DiskSubType = subType; + sidecar.BlockMedia[0].Interface = "ATA"; + sidecar.BlockMedia[0].LogicalBlocks = blocks; + sidecar.BlockMedia[0].PhysicalBlockSize = physicalSectorSize; + sidecar.BlockMedia[0].LogicalBlockSize = blockSize; + sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; + sidecar.BlockMedia[0].Model = _dev.Model; - var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); - xmlSer.Serialize(xmlFs, sidecar); - xmlFs.Close(); + if(!_private) + sidecar.BlockMedia[0].Serial = _dev.Serial; + + sidecar.BlockMedia[0].Size = blocks * blockSize; + + if(cylinders > 0 && + heads > 0 && + sectors > 0) + { + sidecar.BlockMedia[0].Cylinders = cylinders; + sidecar.BlockMedia[0].CylindersSpecified = true; + sidecar.BlockMedia[0].Heads = heads; + sidecar.BlockMedia[0].HeadsSpecified = true; + sidecar.BlockMedia[0].SectorsPerTrack = sectors; + sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; } + + UpdateStatus?.Invoke("Writing metadata sidecar"); + + var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); + + var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); } - - UpdateStatus?.Invoke(""); - - UpdateStatus?. - Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); - - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); - - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); - - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); - - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - - if(_resume.BadBlocks.Count > 0) - _resume.BadBlocks.Sort(); - - UpdateStatus?.Invoke(""); } - Statistics.AddMedia(mediaType, true); + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + + if(_resume.BadBlocks.Count > 0) + _resume.BadBlocks.Sort(); + + UpdateStatus?.Invoke(""); } - else - StoppingErrorMessage?.Invoke("Unable to communicate with ATA device."); + + Statistics.AddMedia(mediaType, true); } + else + StoppingErrorMessage?.Invoke("Unable to communicate with ATA device."); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs b/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs index 7bf84424b..c567b7b81 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/CdiReady.cs @@ -45,373 +45,372 @@ using Schemas; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Detects if a sector contains data + /// Sector contents + /// true if it contains Yellow Book data, false otherwise + static bool IsData(byte[] sector) { - /// Detects if a sector contains data - /// Sector contents - /// true if it contains Yellow Book data, false otherwise - static bool IsData(byte[] sector) + if(sector?.Length != 2352) + return false; + + byte[] syncMark = { - if(sector?.Length != 2352) - return false; + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 + }; - byte[] syncMark = - { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 - }; + byte[] testMark = new byte[12]; + Array.Copy(sector, 0, testMark, 0, 12); - byte[] testMark = new byte[12]; - Array.Copy(sector, 0, testMark, 0, 12); + return syncMark.SequenceEqual(testMark) && (sector[0xF] == 0 || sector[0xF] == 1 || sector[0xF] == 2); + } - return syncMark.SequenceEqual(testMark) && (sector[0xF] == 0 || sector[0xF] == 1 || sector[0xF] == 2); + /// Detects if a sector contains scrambled data + /// Sector contents + /// What LBA we intended to read + /// Offset in bytes, if found + /// true if it contains Yellow Book data, false otherwise + static bool IsScrambledData(byte[] sector, int wantedLba, out int? offset) + { + offset = 0; + + if(sector?.Length != 2352) + return false; + + byte[] syncMark = + { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 + }; + + byte[] testMark = new byte[12]; + + for(int i = 0; i <= 2336; i++) + { + Array.Copy(sector, i, testMark, 0, 12); + + if(!syncMark.SequenceEqual(testMark) || + (sector[i + 0xF] != 0x60 && sector[i + 0xF] != 0x61 && sector[i + 0xF] != 0x62)) + continue; + + // De-scramble M and S + int minute = sector[i + 12] ^ 0x01; + int second = sector[i + 13] ^ 0x80; + int frame = sector[i + 14]; + + // Convert to binary + minute = (minute / 16 * 10) + (minute & 0x0F); + second = (second / 16 * 10) + (second & 0x0F); + frame = (frame / 16 * 10) + (frame & 0x0F); + + // Calculate the first found LBA + int lba = (minute * 60 * 75) + (second * 75) + frame - 150; + + // Calculate the difference between the found LBA and the requested one + int diff = wantedLba - lba; + + offset = i + (2352 * diff); + + return true; } - /// Detects if a sector contains scrambled data - /// Sector contents - /// What LBA we intended to read - /// Offset in bytes, if found - /// true if it contains Yellow Book data, false otherwise - static bool IsScrambledData(byte[] sector, int wantedLba, out int? offset) + return false; + } + + // TODO: Set pregap for Track 1 + // TODO: Detect errors in sectors + /// Reads all the hidden track in CD-i Ready discs + /// Total number of positive sectors + /// Size of the read sector in bytes + /// Current read speed + /// Current dump hardware try + /// Extents + /// IMGBurn log + /// Duration of image write + /// Lead-out extents + /// Maximum speed + /// MHDD log + /// Minimum speed + /// Read offset + /// Sectors needed to fix offset + /// Subchannel size in bytes + /// Drive's maximum supported subchannel + /// Total commands duration + /// Is the drive returning CD-i Ready hidden track as audio? + /// Disc tracks + /// Subchannel log + /// Subchannel desired to save + /// List of disc ISRCs + /// Disc media catalogue number + /// List of subchannels not yet dumped correctly + /// List of smallest pregap relative address per track + void ReadCdiReady(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents, + IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, + ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, uint subSize, + MmcSubchannel supportedSubchannel, ref double totalDuration, Track[] tracks, + SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary isrcs, + ref string mcn, HashSet subchannelExtents, ulong blocks, bool cdiReadyReadAsAudio, + int offsetBytes, int sectorsForOffset, Dictionary smallestPregapLbaPerTrack) + { + ulong sectorSpeedStart = 0; // Used to calculate correct speed + DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation + bool sense; // Sense indicator + byte[] cmdBuf; // Data buffer + byte[] senseBuf; // Sense buffer + double cmdDuration; // Command execution time + const uint sectorSize = 2352; // Full sector size + Track firstTrack = tracks.FirstOrDefault(t => t.Sequence == 1); + uint blocksToRead; // How many sectors to read at once + var outputOptical = _outputPlugin as IWritableOpticalImage; + + if(firstTrack is null) + return; + + if(cdiReadyReadAsAudio) { - offset = 0; + _dumpLog.WriteLine("Setting speed to 8x for CD-i Ready reading as audio."); + UpdateStatus?.Invoke("Setting speed to 8x for CD-i Ready reading as audio."); - if(sector?.Length != 2352) - return false; + _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, 1416, 0, _dev.Timeout, out _); + } - byte[] syncMark = + InitProgress?.Invoke(); + + for(ulong i = _resume.NextBlock; i < firstTrack.StartSector; i += blocksToRead) + { + if(_aborted) { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 - }; + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - byte[] testMark = new byte[12]; - - for(int i = 0; i <= 2336; i++) - { - Array.Copy(sector, i, testMark, 0, 12); - - if(!syncMark.SequenceEqual(testMark) || - (sector[i + 0xF] != 0x60 && sector[i + 0xF] != 0x61 && sector[i + 0xF] != 0x62)) - continue; - - // De-scramble M and S - int minute = sector[i + 12] ^ 0x01; - int second = sector[i + 13] ^ 0x80; - int frame = sector[i + 14]; - - // Convert to binary - minute = (minute / 16 * 10) + (minute & 0x0F); - second = (second / 16 * 10) + (second & 0x0F); - frame = (frame / 16 * 10) + (frame & 0x0F); - - // Calculate the first found LBA - int lba = (minute * 60 * 75) + (second * 75) + frame - 150; - - // Calculate the difference between the found LBA and the requested one - int diff = wantedLba - lba; - - offset = i + (2352 * diff); - - return true; + break; } - return false; - } + uint firstSectorToRead = (uint)i; - // TODO: Set pregap for Track 1 - // TODO: Detect errors in sectors - /// Reads all the hidden track in CD-i Ready discs - /// Total number of positive sectors - /// Size of the read sector in bytes - /// Current read speed - /// Current dump hardware try - /// Extents - /// IMGBurn log - /// Duration of image write - /// Lead-out extents - /// Maximum speed - /// MHDD log - /// Minimum speed - /// Read offset - /// Sectors needed to fix offset - /// Subchannel size in bytes - /// Drive's maximum supported subchannel - /// Total commands duration - /// Is the drive returning CD-i Ready hidden track as audio? - /// Disc tracks - /// Subchannel log - /// Subchannel desired to save - /// List of disc ISRCs - /// Disc media catalogue number - /// List of subchannels not yet dumped correctly - /// List of smallest pregap relative address per track - void ReadCdiReady(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents, - IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, - ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, uint subSize, - MmcSubchannel supportedSubchannel, ref double totalDuration, Track[] tracks, - SubchannelLog subLog, MmcSubchannel desiredSubchannel, Dictionary isrcs, - ref string mcn, HashSet subchannelExtents, ulong blocks, bool cdiReadyReadAsAudio, - int offsetBytes, int sectorsForOffset, Dictionary smallestPregapLbaPerTrack) - { - ulong sectorSpeedStart = 0; // Used to calculate correct speed - DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation - bool sense; // Sense indicator - byte[] cmdBuf; // Data buffer - byte[] senseBuf; // Sense buffer - double cmdDuration; // Command execution time - const uint sectorSize = 2352; // Full sector size - Track firstTrack = tracks.FirstOrDefault(t => t.Sequence == 1); - uint blocksToRead; // How many sectors to read at once - var outputOptical = _outputPlugin as IWritableOpticalImage; + blocksToRead = _maximumReadable; - if(firstTrack is null) - return; + if(blocksToRead == 1 && cdiReadyReadAsAudio) + blocksToRead += (uint)sectorsForOffset; if(cdiReadyReadAsAudio) { - _dumpLog.WriteLine("Setting speed to 8x for CD-i Ready reading as audio."); - UpdateStatus?.Invoke("Setting speed to 8x for CD-i Ready reading as audio."); - - _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, 1416, 0, _dev.Timeout, out _); + if(offsetBytes < 0) + { + if(i == 0) + firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1 + else + firstSectorToRead -= (uint)sectorsForOffset; + } } - InitProgress?.Invoke(); + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; - for(ulong i = _resume.NextBlock; i < firstTrack.StartSector; i += blocksToRead) + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)blocks); + + sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); + + totalDuration += cmdDuration; + + double elapsed; + + // Overcome the track mode change drive error + if(sense) { - if(_aborted) + for(uint r = 0; r < _maximumReadable; r++) { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + UpdateProgress?.Invoke($"Reading sector {i + r} of {blocks} ({currentSpeed:F3} MiB/sec.)", + (long)i + r, (long)blocks); - break; - } + sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, + (uint)sectorsForOffset + 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, _dev.Timeout, out cmdDuration); - uint firstSectorToRead = (uint)i; + totalDuration += cmdDuration; - blocksToRead = _maximumReadable; - - if(blocksToRead == 1 && cdiReadyReadAsAudio) - blocksToRead += (uint)sectorsForOffset; - - if(cdiReadyReadAsAudio) - { - if(offsetBytes < 0) + if(!sense && + !_dev.Error) { - if(i == 0) - firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1 - else - firstSectorToRead -= (uint)sectorsForOffset; - } - } + mhddLog.Write(i + r, cmdDuration); + ibgLog.Write(i + r, currentSpeed * 1024); + extents.Add(i + r, 1, true); + DateTime writeStart = DateTime.Now; - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; + if(cdiReadyReadAsAudio) + FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, + ref blocksToRead, subSize, ref cmdBuf, blockSize, false); - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)blocks); - - sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - - totalDuration += cmdDuration; - - double elapsed; - - // Overcome the track mode change drive error - if(sense) - { - for(uint r = 0; r < _maximumReadable; r++) - { - UpdateProgress?.Invoke($"Reading sector {i + r} of {blocks} ({currentSpeed:F3} MiB/sec.)", - (long)i + r, (long)blocks); - - sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, - (uint)sectorsForOffset + 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, _dev.Timeout, out cmdDuration); - - totalDuration += cmdDuration; - - if(!sense && - !_dev.Error) + if(supportedSubchannel != MmcSubchannel.None) { - mhddLog.Write(i + r, cmdDuration); - ibgLog.Write(i + r, currentSpeed * 1024); - extents.Add(i + r, 1, true); - DateTime writeStart = DateTime.Now; + byte[] data = new byte[sectorSize]; + byte[] sub = new byte[subSize]; + + Array.Copy(cmdBuf, 0, data, 0, sectorSize); + + Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); if(cdiReadyReadAsAudio) - FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, - ref blocksToRead, subSize, ref cmdBuf, blockSize, false); + data = Sector.Scramble(data); - if(supportedSubchannel != MmcSubchannel.None) + outputOptical.WriteSectorsLong(data, i + r, 1); + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i + r, 1, subLog, isrcs, 1, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); + + // Set tracks and go back + if(indexesChanged) { - byte[] data = new byte[sectorSize]; - byte[] sub = new byte[subSize]; + (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + i -= _maximumReadable; - Array.Copy(cmdBuf, 0, data, 0, sectorSize); - - Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); - - if(cdiReadyReadAsAudio) - data = Sector.Scramble(data); - - outputOptical.WriteSectorsLong(data, i + r, 1); - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, i + r, 1, subLog, isrcs, 1, ref mcn, tracks, - subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, - _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); - - // Set tracks and go back - if(indexesChanged) - { - (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - i -= _maximumReadable; - - continue; - } + continue; } - else - { - outputOptical.WriteSectorsLong(cmdBuf, i + r, 1); - } - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } else { - _errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf); - - leadOutExtents.Add(i + r, firstTrack.StartSector - 1); - - UpdateStatus?. - Invoke($"Adding CD-i Ready hole from LBA {i + r} to {firstTrack.StartSector - 1} inclusive."); - - _dumpLog.WriteLine("Adding CD-i Ready hole from LBA {0} to {1} inclusive.", i + r, - firstTrack.StartSector - 1); - - break; + outputOptical.WriteSectorsLong(cmdBuf, i + r, 1); } - sectorSpeedStart += r; - - _resume.NextBlock = i + r; - - elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - } - - if(!sense && - !_dev.Error) - { - if(cdiReadyReadAsAudio) - FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, - subSize, ref cmdBuf, blockSize, false); - - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - extents.Add(i, blocksToRead, true); - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) - { - byte[] data = new byte[sectorSize * blocksToRead]; - byte[] sub = new byte[subSize * blocksToRead]; - byte[] tmpData = new byte[sectorSize]; - - for(int b = 0; b < blocksToRead; b++) - { - if(cdiReadyReadAsAudio) - { - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), tmpData, 0, sectorSize); - tmpData = Sector.Scramble(tmpData); - Array.Copy(tmpData, 0, data, sectorSize * b, sectorSize); - } - else - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); - - Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); - } - - outputOptical.WriteSectorsLong(data, i, blocksToRead); - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, i, blocksToRead, subLog, isrcs, 1, ref mcn, tracks, - subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, - _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); - - // Set tracks and go back - if(indexesChanged) - { - (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - i -= blocksToRead; - - continue; - } + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } else { - if(cdiReadyReadAsAudio) - { - byte[] tmpData = new byte[sectorSize]; - byte[] data = new byte[sectorSize * blocksToRead]; + _errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf); - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(cmdBuf, (int)(b * sectorSize), tmpData, 0, sectorSize); - tmpData = Sector.Scramble(tmpData); - Array.Copy(tmpData, 0, data, sectorSize * b, sectorSize); - } + leadOutExtents.Add(i + r, firstTrack.StartSector - 1); - outputOptical.WriteSectorsLong(data, i, blocksToRead); - } - else - outputOptical.WriteSectorsLong(cmdBuf, i, blocksToRead); + UpdateStatus?. + Invoke($"Adding CD-i Ready hole from LBA {i + r} to {firstTrack.StartSector - 1} inclusive."); + + _dumpLog.WriteLine("Adding CD-i Ready hole from LBA {0} to {1} inclusive.", i + r, + firstTrack.StartSector - 1); + + break; } - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + sectorSpeedStart += r; + + _resume.NextBlock = i + r; + + elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + } + + if(!sense && + !_dev.Error) + { + if(cdiReadyReadAsAudio) + FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, + subSize, ref cmdBuf, blockSize, false); + + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + extents.Add(i, blocksToRead, true); + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) + { + byte[] data = new byte[sectorSize * blocksToRead]; + byte[] sub = new byte[subSize * blocksToRead]; + byte[] tmpData = new byte[sectorSize]; + + for(int b = 0; b < blocksToRead; b++) + { + if(cdiReadyReadAsAudio) + { + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), tmpData, 0, sectorSize); + tmpData = Sector.Scramble(tmpData); + Array.Copy(tmpData, 0, data, sectorSize * b, sectorSize); + } + else + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); + + Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); + } + + outputOptical.WriteSectorsLong(data, i, blocksToRead); + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i, blocksToRead, subLog, isrcs, 1, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); + + // Set tracks and go back + if(indexesChanged) + { + (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + i -= blocksToRead; + + continue; + } } else { - _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); + if(cdiReadyReadAsAudio) + { + byte[] tmpData = new byte[sectorSize]; + byte[] data = new byte[sectorSize * blocksToRead]; - _resume.NextBlock = firstTrack.StartSector; + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(cmdBuf, (int)(b * sectorSize), tmpData, 0, sectorSize); + tmpData = Sector.Scramble(tmpData); + Array.Copy(tmpData, 0, data, sectorSize * b, sectorSize); + } - break; + outputOptical.WriteSectorsLong(data, i, blocksToRead); + } + else + outputOptical.WriteSectorsLong(cmdBuf, i, blocksToRead); } - sectorSpeedStart += blocksToRead; + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + } + else + { + _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); - _resume.NextBlock = i + blocksToRead; + _resume.NextBlock = firstTrack.StartSector; - elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + break; } - EndProgress?.Invoke(); + sectorSpeedStart += blocksToRead; + + _resume.NextBlock = i + blocksToRead; + + elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; } + + EndProgress?.Invoke(); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs index e6f678193..18ac308d5 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Data.cs @@ -49,727 +49,726 @@ using Schemas; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Reads all CD user data + /// Extents with audio sectors + /// Total number of positive sectors + /// Size of the read sector in bytes + /// Current read speed + /// Current dump hardware try + /// Extents + /// IMGBurn log + /// Duration of image write + /// Last sector number + /// Lead-out extents + /// Maximum speed + /// MHDD log + /// Minimum speed + /// Is trim a new one? + /// Next cluster of sectors is all data + /// Read offset + /// Device supports READ(6) + /// Device supports READ(10) + /// Device supports READ(12) + /// Device supports READ(16) + /// Device supports READ CD + /// Sectors needed to fix offset + /// Subchannel size in bytes + /// Drive's maximum supported subchannel + /// Supports reading EDC and ECC + /// Total commands duration + /// Disc tracks + /// Subchannel log + /// Subchannel desired to save + /// List of disc ISRCs + /// Disc media catalogue number + /// List of subchannels not yet dumped correctly + /// List of smallest pregap relative address per track + void ReadCdData(ExtentsULong audioExtents, ulong blocks, uint blockSize, ref double currentSpeed, + DumpHardwareType currentTry, ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration, + long lastSector, ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog, + ref double minSpeed, out bool newTrim, bool nextData, int offsetBytes, bool read6, bool read10, + bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize, + MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration, + Track[] tracks, SubchannelLog subLog, MmcSubchannel desiredSubchannel, + Dictionary isrcs, ref string mcn, HashSet subchannelExtents, + Dictionary smallestPregapLbaPerTrack) { - /// Reads all CD user data - /// Extents with audio sectors - /// Total number of positive sectors - /// Size of the read sector in bytes - /// Current read speed - /// Current dump hardware try - /// Extents - /// IMGBurn log - /// Duration of image write - /// Last sector number - /// Lead-out extents - /// Maximum speed - /// MHDD log - /// Minimum speed - /// Is trim a new one? - /// Next cluster of sectors is all data - /// Read offset - /// Device supports READ(6) - /// Device supports READ(10) - /// Device supports READ(12) - /// Device supports READ(16) - /// Device supports READ CD - /// Sectors needed to fix offset - /// Subchannel size in bytes - /// Drive's maximum supported subchannel - /// Supports reading EDC and ECC - /// Total commands duration - /// Disc tracks - /// Subchannel log - /// Subchannel desired to save - /// List of disc ISRCs - /// Disc media catalogue number - /// List of subchannels not yet dumped correctly - /// List of smallest pregap relative address per track - void ReadCdData(ExtentsULong audioExtents, ulong blocks, uint blockSize, ref double currentSpeed, - DumpHardwareType currentTry, ExtentsULong extents, IbgLog ibgLog, ref double imageWriteDuration, - long lastSector, ExtentsULong leadOutExtents, ref double maxSpeed, MhddLog mhddLog, - ref double minSpeed, out bool newTrim, bool nextData, int offsetBytes, bool read6, bool read10, - bool read12, bool read16, bool readcd, int sectorsForOffset, uint subSize, - MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration, - Track[] tracks, SubchannelLog subLog, MmcSubchannel desiredSubchannel, - Dictionary isrcs, ref string mcn, HashSet subchannelExtents, - Dictionary smallestPregapLbaPerTrack) + ulong sectorSpeedStart = 0; // Used to calculate correct speed + DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation + uint blocksToRead; // How many sectors to read at once + bool sense = true; // Sense indicator + byte[] cmdBuf = null; // Data buffer + byte[] senseBuf = null; // Sense buffer + double cmdDuration = 0; // Command execution time + const uint sectorSize = 2352; // Full sector size + newTrim = false; + PlextorSubchannel supportedPlextorSubchannel; + var outputFormat = _outputPlugin as IWritableImage; + + switch(supportedSubchannel) { - ulong sectorSpeedStart = 0; // Used to calculate correct speed - DateTime timeSpeedStart = DateTime.UtcNow; // Time of start for speed calculation - uint blocksToRead; // How many sectors to read at once - bool sense = true; // Sense indicator - byte[] cmdBuf = null; // Data buffer - byte[] senseBuf = null; // Sense buffer - double cmdDuration = 0; // Command execution time - const uint sectorSize = 2352; // Full sector size - newTrim = false; - PlextorSubchannel supportedPlextorSubchannel; - var outputFormat = _outputPlugin as IWritableImage; + case MmcSubchannel.None: + supportedPlextorSubchannel = PlextorSubchannel.None; - switch(supportedSubchannel) + break; + case MmcSubchannel.Raw: + supportedPlextorSubchannel = PlextorSubchannel.Pack; + + break; + case MmcSubchannel.Q16: + supportedPlextorSubchannel = PlextorSubchannel.Q16; + + break; + default: + supportedPlextorSubchannel = PlextorSubchannel.None; + + break; + } + + InitProgress?.Invoke(); + + int currentReadSpeed = _speed; + bool crossingLeadOut = false; + bool failedCrossingLeadOut = false; + bool skippingLead = false; + + for(ulong i = _resume.NextBlock; (long)i <= lastSector; i += blocksToRead) + { + if(_aborted) { - case MmcSubchannel.None: - supportedPlextorSubchannel = PlextorSubchannel.None; + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - break; - case MmcSubchannel.Raw: - supportedPlextorSubchannel = PlextorSubchannel.Pack; - - break; - case MmcSubchannel.Q16: - supportedPlextorSubchannel = PlextorSubchannel.Q16; - - break; - default: - supportedPlextorSubchannel = PlextorSubchannel.None; - - break; + break; } - InitProgress?.Invoke(); - - int currentReadSpeed = _speed; - bool crossingLeadOut = false; - bool failedCrossingLeadOut = false; - bool skippingLead = false; - - for(ulong i = _resume.NextBlock; (long)i <= lastSector; i += blocksToRead) + while(leadOutExtents.Contains(i)) { - if(_aborted) + skippingLead = true; + i++; + } + + if((long)i > lastSector) + break; + + uint firstSectorToRead = (uint)i; + + Track track = tracks.OrderBy(t => t.StartSector).LastOrDefault(t => i >= t.StartSector); + + blocksToRead = 0; + bool inData = nextData; + + for(ulong j = i; j < i + _maximumReadable; j++) + { + if(j > (ulong)lastSector) { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + if(!failedCrossingLeadOut && + !inData) + blocksToRead += (uint)sectorsForOffset; + + if(sectorsForOffset > 0 && + !inData) + crossingLeadOut = true; break; } - while(leadOutExtents.Contains(i)) + if(nextData) { - skippingLead = true; - i++; - } - - if((long)i > lastSector) - break; - - uint firstSectorToRead = (uint)i; - - Track track = tracks.OrderBy(t => t.StartSector).LastOrDefault(t => i >= t.StartSector); - - blocksToRead = 0; - bool inData = nextData; - - for(ulong j = i; j < i + _maximumReadable; j++) - { - if(j > (ulong)lastSector) + if(audioExtents.Contains(j)) { - if(!failedCrossingLeadOut && - !inData) - blocksToRead += (uint)sectorsForOffset; - - if(sectorsForOffset > 0 && - !inData) - crossingLeadOut = true; + nextData = false; break; } - if(nextData) + blocksToRead++; + } + else + { + if(!audioExtents.Contains(j)) { - if(audioExtents.Contains(j)) - { - nextData = false; + nextData = true; - break; - } - - blocksToRead++; + break; } + + blocksToRead++; + } + } + + if(track.Sequence != 0 && + i + blocksToRead - (ulong)sectorsForOffset > track.EndSector + 1) + blocksToRead = (uint)(track.EndSector + 1 - i + (ulong)sectorsForOffset); + + if(blocksToRead == 1 && + !inData) + blocksToRead += (uint)sectorsForOffset; + + if(blocksToRead == 0) + { + if(!skippingLead) + i += (ulong)sectorsForOffset; + + skippingLead = false; + + continue; + } + + if(_fixOffset && !inData) + { + if(offsetBytes < 0) + { + if(i == 0) + firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1 else + firstSectorToRead -= (uint)sectorsForOffset; + + if(blocksToRead <= sectorsForOffset) + blocksToRead += (uint)sectorsForOffset; + } + } + + if(!inData && + currentReadSpeed == 0xFFFF) + { + _dumpLog.WriteLine("Setting speed to 8x for audio reading."); + UpdateStatus?.Invoke("Setting speed to 8x for audio reading."); + + _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, 1416, 0, _dev.Timeout, out _); + + currentReadSpeed = 1200; + } + + if(inData && currentReadSpeed != _speed) + { + _dumpLog.WriteLine($"Setting speed to {(_speed == 0xFFFF ? "MAX for data reading" : $"{_speed}x")}."); + + UpdateStatus?. + Invoke($"Setting speed to {(_speed == 0xFFFF ? "MAX for data reading" : $"{_speed}x")}."); + + _speed *= _speedMultiplier; + + if(_speed == 0 || + _speed > 0xFFFF) + _speed = 0xFFFF; + + currentReadSpeed = _speed; + + _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _); + } + + if(inData && crossingLeadOut) + { + firstSectorToRead = (uint)i; + blocksToRead = (uint)(lastSector - firstSectorToRead) + 1; + crossingLeadOut = false; + } + + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)blocks); + + if(crossingLeadOut && + failedCrossingLeadOut && + blocksToRead > 1) + blocksToRead--; + + if(_supportsPlextorD8 && !inData) + { + sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, + blocksToRead, supportedPlextorSubchannel, out cmdDuration); + + totalDuration += cmdDuration; + } + else if(readcd) + { + if(inData) + { + sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, + true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, + out cmdDuration); + + if(sense) { - if(!audioExtents.Contains(j)) + DecodedSense? decSense = Sense.Decode(senseBuf); + + // Try to workaround firmware + if(decSense?.ASC == 0x64) { - nextData = true; + bool goBackTrackTypeChange = false; - break; - } - - blocksToRead++; - } - } - - if(track.Sequence != 0 && - i + blocksToRead - (ulong)sectorsForOffset > track.EndSector + 1) - blocksToRead = (uint)(track.EndSector + 1 - i + (ulong)sectorsForOffset); - - if(blocksToRead == 1 && - !inData) - blocksToRead += (uint)sectorsForOffset; - - if(blocksToRead == 0) - { - if(!skippingLead) - i += (ulong)sectorsForOffset; - - skippingLead = false; - - continue; - } - - if(_fixOffset && !inData) - { - if(offsetBytes < 0) - { - if(i == 0) - firstSectorToRead = uint.MaxValue - (uint)(sectorsForOffset - 1); // -1 - else - firstSectorToRead -= (uint)sectorsForOffset; - - if(blocksToRead <= sectorsForOffset) - blocksToRead += (uint)sectorsForOffset; - } - } - - if(!inData && - currentReadSpeed == 0xFFFF) - { - _dumpLog.WriteLine("Setting speed to 8x for audio reading."); - UpdateStatus?.Invoke("Setting speed to 8x for audio reading."); - - _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, 1416, 0, _dev.Timeout, out _); - - currentReadSpeed = 1200; - } - - if(inData && currentReadSpeed != _speed) - { - _dumpLog.WriteLine($"Setting speed to {(_speed == 0xFFFF ? "MAX for data reading" : $"{_speed}x")}."); - - UpdateStatus?. - Invoke($"Setting speed to {(_speed == 0xFFFF ? "MAX for data reading" : $"{_speed}x")}."); - - _speed *= _speedMultiplier; - - if(_speed == 0 || - _speed > 0xFFFF) - _speed = 0xFFFF; - - currentReadSpeed = _speed; - - _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _); - } - - if(inData && crossingLeadOut) - { - firstSectorToRead = (uint)i; - blocksToRead = (uint)(lastSector - firstSectorToRead) + 1; - crossingLeadOut = false; - } - - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; - - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)blocks); - - if(crossingLeadOut && - failedCrossingLeadOut && - blocksToRead > 1) - blocksToRead--; - - if(_supportsPlextorD8 && !inData) - { - sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, - blocksToRead, supportedPlextorSubchannel, out cmdDuration); - - totalDuration += cmdDuration; - } - else if(readcd) - { - if(inData) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, - true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, - out cmdDuration); - - if(sense) - { - DecodedSense? decSense = Sense.Decode(senseBuf); - - // Try to workaround firmware - if(decSense?.ASC == 0x64) + // Go one for one as the drive does not tell us which one failed + for(int bi = 0; bi < blocksToRead; bi++) { - bool goBackTrackTypeChange = false; - - // Go one for one as the drive does not tell us which one failed - for(int bi = 0; bi < blocksToRead; bi++) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(firstSectorToRead + bi), - blockSize, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, _dev.Timeout, out double cmdDuration2); - - cmdDuration += cmdDuration2; - - if(!sense && - cmdBuf[0] == 0x00 && - cmdBuf[1] == 0xFF && - cmdBuf[2] == 0xFF && - cmdBuf[3] == 0xFF && - cmdBuf[4] == 0xFF && - cmdBuf[5] == 0xFF && - cmdBuf[6] == 0xFF && - cmdBuf[7] == 0xFF && - cmdBuf[8] == 0xFF && - cmdBuf[9] == 0xFF && - cmdBuf[10] == 0xFF && - cmdBuf[11] == 0x00) - continue; - - // Set those sectors as audio - for(int bip = bi; bip < blocksToRead; bip++) - audioExtents.Add((ulong)(firstSectorToRead + bip)); - - goBackTrackTypeChange = true; - - break; - } - - // Go back to read again - if(goBackTrackTypeChange) - { - blocksToRead = 0; - nextData = true; - - continue; - } - } - } - } - else - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, - MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - - if(sense) - { - DecodedSense? decSense = Sense.Decode(senseBuf); - - // Try to workaround firmware - if((decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05) || - decSense?.ASC == 0x64) - { - sense = _dev.ReadCd(out cmdBuf, out _, firstSectorToRead, blockSize, blocksToRead, - MmcSectorTypes.AllTypes, false, false, true, + sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(firstSectorToRead + bi), + blockSize, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out double cmdDuration2); cmdDuration += cmdDuration2; - } - } - } - - totalDuration += cmdDuration; - } - else if(read16) - { - sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, false, false, firstSectorToRead, blockSize, - 0, blocksToRead, false, _dev.Timeout, out cmdDuration); - } - else if(read12) - { - sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, false, false, false, firstSectorToRead, - blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration); - } - else if(read10) - { - sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, false, false, false, firstSectorToRead, - blockSize, 0, (ushort)blocksToRead, _dev.Timeout, out cmdDuration); - } - else if(read6) - { - sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)blocksToRead, - _dev.Timeout, out cmdDuration); - } - - double elapsed; - - // Overcome the track mode change drive error - if(inData && - !nextData && - sense) - { - for(uint r = 0; r < blocksToRead; r++) - { - UpdateProgress?.Invoke($"Reading sector {i + r} of {blocks} ({currentSpeed:F3} MiB/sec.)", - (long)i + r, (long)blocks); - - if(_supportsPlextorD8) - { - int adjustment = 0; - - if(offsetBytes < 0) - adjustment = -sectorsForOffset; - - sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, - (uint)(firstSectorToRead + r + adjustment), blockSize, - (uint)sectorsForOffset + 1, supportedPlextorSubchannel, - out cmdDuration); - - totalDuration += cmdDuration; - - if(!sense) - { - uint sectorsForFix = (uint)(1 + sectorsForOffset); - - FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, - ref sectorsForFix, subSize, ref cmdBuf, blockSize, false); - - // TODO: Implement sector validity - cmdBuf = Sector.Scramble(cmdBuf); - } - } - else if(readcd) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, - true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, - out cmdDuration); - - totalDuration += cmdDuration; - } - else if(read16) - { - sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i + r, blockSize, 0, 1, - false, _dev.Timeout, out cmdDuration); - } - else if(read12) - { - sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)(i + r), - blockSize, 0, 1, false, _dev.Timeout, out cmdDuration); - } - else if(read10) - { - sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)(i + r), - blockSize, 0, 1, _dev.Timeout, out cmdDuration); - } - else if(read6) - { - sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, 1, _dev.Timeout, - out cmdDuration); - } - - if(!sense && - !_dev.Error) - { - mhddLog.Write(i + r, cmdDuration); - ibgLog.Write(i + r, currentSpeed * 1024); - extents.Add(i + r, 1, true); - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) - { - byte[] data = new byte[sectorSize]; - byte[] sub = new byte[subSize]; - - Array.Copy(cmdBuf, 0, data, 0, sectorSize); - - Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); - - if(supportsLongSectors) - outputFormat.WriteSectorsLong(data, i + r, 1); - else - { - var cooked = new MemoryStream(); - byte[] sector = new byte[sectorSize]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), sector, 0, sectorSize); - byte[] cookedSector = Sector.GetUserData(sector); - cooked.Write(cookedSector, 0, cookedSector.Length); - } - - outputFormat.WriteSectors(cooked.ToArray(), i, blocksToRead); - } - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, i + r, 1, subLog, isrcs, (byte)track.Sequence, - ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, outputFormat as IWritableOpticalImage, - _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, - smallestPregapLbaPerTrack, true); - - // Set tracks and go back - if(indexesChanged) - { - (outputFormat as IWritableOpticalImage).SetTracks(tracks.ToList()); - i -= blocksToRead; + if(!sense && + cmdBuf[0] == 0x00 && + cmdBuf[1] == 0xFF && + cmdBuf[2] == 0xFF && + cmdBuf[3] == 0xFF && + cmdBuf[4] == 0xFF && + cmdBuf[5] == 0xFF && + cmdBuf[6] == 0xFF && + cmdBuf[7] == 0xFF && + cmdBuf[8] == 0xFF && + cmdBuf[9] == 0xFF && + cmdBuf[10] == 0xFF && + cmdBuf[11] == 0x00) continue; - } + + // Set those sectors as audio + for(int bip = bi; bip < blocksToRead; bip++) + audioExtents.Add((ulong)(firstSectorToRead + bip)); + + goBackTrackTypeChange = true; + + break; } - else + + // Go back to read again + if(goBackTrackTypeChange) { - if(supportsLongSectors) - outputFormat.WriteSectorsLong(cmdBuf, i + r, 1); - else - { - var cooked = new MemoryStream(); - byte[] sector = new byte[sectorSize]; + blocksToRead = 0; + nextData = true; - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(cmdBuf, (int)(b * sectorSize), sector, 0, sectorSize); - byte[] cookedSector = Sector.GetUserData(sector); - cooked.Write(cookedSector, 0, cookedSector.Length); - } - - outputFormat.WriteSectors(cooked.ToArray(), i, blocksToRead); - } + continue; } - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - } - else - { - _errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf); - - // Write empty data - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) - { - outputFormat.WriteSectorsLong(new byte[sectorSize], i + r, 1); - - if(desiredSubchannel != MmcSubchannel.None) - outputFormat.WriteSectorsTag(new byte[subSize], i + r, 1, - SectorTagType.CdSectorSubchannel); - } - else - { - if(supportsLongSectors) - outputFormat.WriteSectorsLong(new byte[blockSize], i + r, 1); - else - { - if(cmdBuf.Length % sectorSize == 0) - outputFormat.WriteSectors(new byte[2048], i + r, 1); - else - outputFormat.WriteSectorsLong(new byte[blockSize], i + r, 1); - } - } - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - _resume.BadBlocks.Add(i + r); - - AaruConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); - mhddLog.Write(i + r, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i + r, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", 1, i + r); - newTrim = true; - } - - sectorSpeedStart += r; - - _resume.NextBlock = i + r; - - elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - continue; - } - - if(!sense && - !_dev.Error) - { - if(crossingLeadOut && failedCrossingLeadOut) - { - byte[] tmp = new byte[cmdBuf.Length + blockSize]; - Array.Copy(cmdBuf, 0, tmp, 0, cmdBuf.Length); - } - - // Because one block has been partially used to fix the offset - if(_fixOffset && - !inData && - offsetBytes != 0) - FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, - subSize, ref cmdBuf, blockSize, failedCrossingLeadOut); - - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - extents.Add(i, blocksToRead, true); - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) - { - byte[] data = new byte[sectorSize * blocksToRead]; - byte[] sub = new byte[subSize * blocksToRead]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); - - Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); - } - - if(supportsLongSectors) - outputFormat.WriteSectorsLong(data, i, blocksToRead); - else - { - var cooked = new MemoryStream(); - byte[] sector = new byte[sectorSize]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), sector, 0, sectorSize); - byte[] cookedSector = Sector.GetUserData(sector); - cooked.Write(cookedSector, 0, cookedSector.Length); - } - - outputFormat.WriteSectors(cooked.ToArray(), i, blocksToRead); - } - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, i, blocksToRead, subLog, isrcs, (byte)track.Sequence, - ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, outputFormat as IWritableOpticalImage, - _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, - true); - - // Set tracks and go back - if(indexesChanged) - { - (outputFormat as IWritableOpticalImage).SetTracks(tracks.ToList()); - i -= blocksToRead; - - continue; } } - else - { - if(supportsLongSectors) - outputFormat.WriteSectorsLong(cmdBuf, i, blocksToRead); - else - { - var cooked = new MemoryStream(); - byte[] sector = new byte[sectorSize]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(cmdBuf, (int)(b * sectorSize), sector, 0, sectorSize); - byte[] cookedSector = Sector.GetUserData(sector); - cooked.Write(cookedSector, 0, cookedSector.Length); - } - - outputFormat.WriteSectors(cooked.ToArray(), i, blocksToRead); - } - } - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } else { - if(crossingLeadOut && Sense.Decode(senseBuf)?.ASC == 0x21) + sense = _dev.ReadCd(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, + MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); + + if(sense) { - if(failedCrossingLeadOut) - break; + DecodedSense? decSense = Sense.Decode(senseBuf); - failedCrossingLeadOut = true; - blocksToRead = 0; - - continue; - } - - _errorLog?.WriteLine(firstSectorToRead, _dev.Error, _dev.LastError, senseBuf); - - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly - - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - // Write empty data - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) - { - outputFormat.WriteSectorsLong(new byte[sectorSize * _skip], i, _skip); - - if(desiredSubchannel != MmcSubchannel.None) - outputFormat.WriteSectorsTag(new byte[subSize * _skip], i, _skip, - SectorTagType.CdSectorSubchannel); - } - else - { - if(supportsLongSectors) + // Try to workaround firmware + if((decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05) || + decSense?.ASC == 0x64) { - outputFormat.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); + sense = _dev.ReadCd(out cmdBuf, out _, firstSectorToRead, blockSize, blocksToRead, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, _dev.Timeout, out double cmdDuration2); + + cmdDuration += cmdDuration2; + } + } + } + + totalDuration += cmdDuration; + } + else if(read16) + { + sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, false, false, firstSectorToRead, blockSize, + 0, blocksToRead, false, _dev.Timeout, out cmdDuration); + } + else if(read12) + { + sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, false, false, false, firstSectorToRead, + blockSize, 0, blocksToRead, false, _dev.Timeout, out cmdDuration); + } + else if(read10) + { + sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, false, false, false, firstSectorToRead, + blockSize, 0, (ushort)blocksToRead, _dev.Timeout, out cmdDuration); + } + else if(read6) + { + sense = _dev.Read6(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, (byte)blocksToRead, + _dev.Timeout, out cmdDuration); + } + + double elapsed; + + // Overcome the track mode change drive error + if(inData && + !nextData && + sense) + { + for(uint r = 0; r < blocksToRead; r++) + { + UpdateProgress?.Invoke($"Reading sector {i + r} of {blocks} ({currentSpeed:F3} MiB/sec.)", + (long)i + r, (long)blocks); + + if(_supportsPlextorD8) + { + int adjustment = 0; + + if(offsetBytes < 0) + adjustment = -sectorsForOffset; + + sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, + (uint)(firstSectorToRead + r + adjustment), blockSize, + (uint)sectorsForOffset + 1, supportedPlextorSubchannel, + out cmdDuration); + + totalDuration += cmdDuration; + + if(!sense) + { + uint sectorsForFix = (uint)(1 + sectorsForOffset); + + FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, + ref sectorsForFix, subSize, ref cmdBuf, blockSize, false); + + // TODO: Implement sector validity + cmdBuf = Sector.Scramble(cmdBuf); + } + } + else if(readcd) + { + sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, + true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, + out cmdDuration); + + totalDuration += cmdDuration; + } + else if(read16) + { + sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i + r, blockSize, 0, 1, + false, _dev.Timeout, out cmdDuration); + } + else if(read12) + { + sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)(i + r), + blockSize, 0, 1, false, _dev.Timeout, out cmdDuration); + } + else if(read10) + { + sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)(i + r), + blockSize, 0, 1, _dev.Timeout, out cmdDuration); + } + else if(read6) + { + sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)(i + r), blockSize, 1, _dev.Timeout, + out cmdDuration); + } + + if(!sense && + !_dev.Error) + { + mhddLog.Write(i + r, cmdDuration); + ibgLog.Write(i + r, currentSpeed * 1024); + extents.Add(i + r, 1, true); + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) + { + byte[] data = new byte[sectorSize]; + byte[] sub = new byte[subSize]; + + Array.Copy(cmdBuf, 0, data, 0, sectorSize); + + Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); + + if(supportsLongSectors) + outputFormat.WriteSectorsLong(data, i + r, 1); + else + { + var cooked = new MemoryStream(); + byte[] sector = new byte[sectorSize]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), sector, 0, sectorSize); + byte[] cookedSector = Sector.GetUserData(sector); + cooked.Write(cookedSector, 0, cookedSector.Length); + } + + outputFormat.WriteSectors(cooked.ToArray(), i, blocksToRead); + } + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i + r, 1, subLog, isrcs, (byte)track.Sequence, + ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, outputFormat as IWritableOpticalImage, + _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, + smallestPregapLbaPerTrack, true); + + // Set tracks and go back + if(indexesChanged) + { + (outputFormat as IWritableOpticalImage).SetTracks(tracks.ToList()); + i -= blocksToRead; + + continue; + } } else { - if(cmdBuf.Length % sectorSize == 0) - outputFormat.WriteSectors(new byte[2048 * _skip], i, _skip); + if(supportsLongSectors) + outputFormat.WriteSectorsLong(cmdBuf, i + r, 1); else - outputFormat.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); + { + var cooked = new MemoryStream(); + byte[] sector = new byte[sectorSize]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(cmdBuf, (int)(b * sectorSize), sector, 0, sectorSize); + byte[] cookedSector = Sector.GetUserData(sector); + cooked.Write(cookedSector, 0, cookedSector.Length); + } + + outputFormat.WriteSectors(cooked.ToArray(), i, blocksToRead); + } } + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + } + else + { + _errorLog?.WriteLine(i + r, _dev.Error, _dev.LastError, senseBuf); + + // Write empty data + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) + { + outputFormat.WriteSectorsLong(new byte[sectorSize], i + r, 1); + + if(desiredSubchannel != MmcSubchannel.None) + outputFormat.WriteSectorsTag(new byte[subSize], i + r, 1, + SectorTagType.CdSectorSubchannel); + } + else + { + if(supportsLongSectors) + outputFormat.WriteSectorsLong(new byte[blockSize], i + r, 1); + else + { + if(cmdBuf.Length % sectorSize == 0) + outputFormat.WriteSectors(new byte[2048], i + r, 1); + else + outputFormat.WriteSectorsLong(new byte[blockSize], i + r, 1); + } + } + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + _resume.BadBlocks.Add(i + r); + + AaruConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); + mhddLog.Write(i + r, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i + r, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", 1, i + r); + newTrim = true; } - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + sectorSpeedStart += r; - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); + _resume.NextBlock = i + r; - AaruConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - ibgLog.Write(i, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; } - sectorSpeedStart += blocksToRead; - - _resume.NextBlock = i + blocksToRead; - - elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + continue; } - EndProgress?.Invoke(); + if(!sense && + !_dev.Error) + { + if(crossingLeadOut && failedCrossingLeadOut) + { + byte[] tmp = new byte[cmdBuf.Length + blockSize]; + Array.Copy(cmdBuf, 0, tmp, 0, cmdBuf.Length); + } - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + // Because one block has been partially used to fix the offset + if(_fixOffset && + !inData && + offsetBytes != 0) + FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, + subSize, ref cmdBuf, blockSize, failedCrossingLeadOut); - if(!failedCrossingLeadOut) - return; + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + extents.Add(i, blocksToRead, true); + DateTime writeStart = DateTime.Now; - _dumpLog.WriteLine("Failed crossing into Lead-Out, dump may not be correct."); - UpdateStatus?.Invoke("Failed crossing into Lead-Out, dump may not be correct."); + if(supportedSubchannel != MmcSubchannel.None) + { + byte[] data = new byte[sectorSize * blocksToRead]; + byte[] sub = new byte[subSize * blocksToRead]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); + + Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); + } + + if(supportsLongSectors) + outputFormat.WriteSectorsLong(data, i, blocksToRead); + else + { + var cooked = new MemoryStream(); + byte[] sector = new byte[sectorSize]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), sector, 0, sectorSize); + byte[] cookedSector = Sector.GetUserData(sector); + cooked.Write(cookedSector, 0, cookedSector.Length); + } + + outputFormat.WriteSectors(cooked.ToArray(), i, blocksToRead); + } + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i, blocksToRead, subLog, isrcs, (byte)track.Sequence, + ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, outputFormat as IWritableOpticalImage, + _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, + true); + + // Set tracks and go back + if(indexesChanged) + { + (outputFormat as IWritableOpticalImage).SetTracks(tracks.ToList()); + i -= blocksToRead; + + continue; + } + } + else + { + if(supportsLongSectors) + outputFormat.WriteSectorsLong(cmdBuf, i, blocksToRead); + else + { + var cooked = new MemoryStream(); + byte[] sector = new byte[sectorSize]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(cmdBuf, (int)(b * sectorSize), sector, 0, sectorSize); + byte[] cookedSector = Sector.GetUserData(sector); + cooked.Write(cookedSector, 0, cookedSector.Length); + } + + outputFormat.WriteSectors(cooked.ToArray(), i, blocksToRead); + } + } + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + } + else + { + if(crossingLeadOut && Sense.Decode(senseBuf)?.ASC == 0x21) + { + if(failedCrossingLeadOut) + break; + + failedCrossingLeadOut = true; + blocksToRead = 0; + + continue; + } + + _errorLog?.WriteLine(firstSectorToRead, _dev.Error, _dev.LastError, senseBuf); + + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + if(i + _skip > blocks) + _skip = (uint)(blocks - i); + + // Write empty data + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) + { + outputFormat.WriteSectorsLong(new byte[sectorSize * _skip], i, _skip); + + if(desiredSubchannel != MmcSubchannel.None) + outputFormat.WriteSectorsTag(new byte[subSize * _skip], i, _skip, + SectorTagType.CdSectorSubchannel); + } + else + { + if(supportsLongSectors) + { + outputFormat.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); + } + else + { + if(cmdBuf.Length % sectorSize == 0) + outputFormat.WriteSectors(new byte[2048 * _skip], i, _skip); + else + outputFormat.WriteSectorsLong(new byte[blockSize * _skip], i, _skip); + } + } + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); + + AaruConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; + } + + sectorSpeedStart += blocksToRead; + + _resume.NextBlock = i + blocksToRead; + + elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; } + + EndProgress?.Invoke(); + + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + + if(!failedCrossingLeadOut) + return; + + _dumpLog.WriteLine("Failed crossing into Lead-Out, dump may not be correct."); + UpdateStatus?.Invoke("Failed crossing into Lead-Out, dump may not be correct."); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs index eb931d17a..5039351b6 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Dump.cs @@ -53,1006 +53,994 @@ using Version = Aaru.CommonTypes.Interop.Version; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Implement dumping Compact Discs + +// TODO: Barcode +sealed partial class Dump { - /// Implement dumping Compact Discs - - // TODO: Barcode - sealed partial class Dump + /// Dumps a compact disc + void CompactDisc() { - /// Dumps a compact disc - void CompactDisc() + ExtentsULong audioExtents; // Extents with audio sectors + ulong blocks; // Total number of positive sectors + uint blockSize; // Size of the read sector in bytes + CdOffset cdOffset; // Read offset from database + byte[] cmdBuf; // Data buffer + DumpHardwareType currentTry = null; // Current dump hardware try + double currentSpeed = 0; // Current read speed + int? discOffset = null; // Disc write offset + DateTime dumpStart = DateTime.UtcNow; // Time of dump start + DateTime end; // Time of operation end + ExtentsULong extents = null; // Extents + bool hiddenData; // Hidden track is data + IbgLog ibgLog; // IMGBurn log + double imageWriteDuration = 0; // Duration of image write + long lastSector; // Last sector number + var leadOutExtents = new ExtentsULong(); // Lead-out extents + Dictionary leadOutStarts = new(); // Lead-out starts + double maxSpeed = double.MinValue; // Maximum speed + MhddLog mhddLog; // MHDD log + double minSpeed = double.MaxValue; // Minimum speed + bool newTrim; // Is trim a new one? + int offsetBytes = 0; // Read offset + bool read6 = false; // Device supports READ(6) + bool read10 = false; // Device supports READ(10) + bool read12 = false; // Device supports READ(12) + bool read16 = false; // Device supports READ(16) + bool readcd; // Device supports READ CD + bool ret; // Image writing return status + const uint sectorSize = 2352; // Full sector size + int sectorsForOffset = 0; // Sectors needed to fix offset + bool sense = true; // Sense indicator + int sessions; // Number of sessions in disc + DateTime start; // Start of operation + SubchannelLog subLog = null; // Subchannel log + uint subSize; // Subchannel size in bytes + TrackSubchannelType subType; // Track subchannel type + bool supportsLongSectors = true; // Supports reading EDC and ECC + bool supportsPqSubchannel; // Supports reading PQ subchannel + bool supportsRwSubchannel; // Supports reading RW subchannel + byte[] tmpBuf; // Temporary buffer + FullTOC.CDFullTOC? toc; // Full CD TOC + double totalDuration = 0; // Total commands duration + Dictionary trackFlags = new(); // Track flags + Track[] tracks; // Tracks in disc + int firstTrackLastSession; // Number of first track in last session + bool hiddenTrack; // Disc has a hidden track before track 1 + MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel + MmcSubchannel desiredSubchannel; // User requested subchannel + bool bcdSubchannel = false; // Subchannel positioning is in BCD + Dictionary isrcs = new(); + string mcn = null; + HashSet subchannelExtents = new(); + bool cdiReadyReadAsAudio = false; + uint firstLba; + var outputOptical = _outputPlugin as IWritableOpticalImage; + + Dictionary mediaTags = new(); // Media tags + Dictionary smallestPregapLbaPerTrack = new(); + + MediaType dskType = MediaType.CD; + + if(_dumpRaw) { - ExtentsULong audioExtents; // Extents with audio sectors - ulong blocks; // Total number of positive sectors - uint blockSize; // Size of the read sector in bytes - CdOffset cdOffset; // Read offset from database - byte[] cmdBuf; // Data buffer - DumpHardwareType currentTry = null; // Current dump hardware try - double currentSpeed = 0; // Current read speed - int? discOffset = null; // Disc write offset - DateTime dumpStart = DateTime.UtcNow; // Time of dump start - DateTime end; // Time of operation end - ExtentsULong extents = null; // Extents - bool hiddenData; // Hidden track is data - IbgLog ibgLog; // IMGBurn log - double imageWriteDuration = 0; // Duration of image write - long lastSector; // Last sector number - var leadOutExtents = new ExtentsULong(); // Lead-out extents - Dictionary leadOutStarts = new(); // Lead-out starts - double maxSpeed = double.MinValue; // Maximum speed - MhddLog mhddLog; // MHDD log - double minSpeed = double.MaxValue; // Minimum speed - bool newTrim; // Is trim a new one? - int offsetBytes = 0; // Read offset - bool read6 = false; // Device supports READ(6) - bool read10 = false; // Device supports READ(10) - bool read12 = false; // Device supports READ(12) - bool read16 = false; // Device supports READ(16) - bool readcd; // Device supports READ CD - bool ret; // Image writing return status - const uint sectorSize = 2352; // Full sector size - int sectorsForOffset = 0; // Sectors needed to fix offset - bool sense = true; // Sense indicator - int sessions; // Number of sessions in disc - DateTime start; // Start of operation - SubchannelLog subLog = null; // Subchannel log - uint subSize; // Subchannel size in bytes - TrackSubchannelType subType; // Track subchannel type - bool supportsLongSectors = true; // Supports reading EDC and ECC - bool supportsPqSubchannel; // Supports reading PQ subchannel - bool supportsRwSubchannel; // Supports reading RW subchannel - byte[] tmpBuf; // Temporary buffer - FullTOC.CDFullTOC? toc; // Full CD TOC - double totalDuration = 0; // Total commands duration - Dictionary trackFlags = new(); // Track flags - Track[] tracks; // Tracks in disc - int firstTrackLastSession; // Number of first track in last session - bool hiddenTrack; // Disc has a hidden track before track 1 - MmcSubchannel supportedSubchannel; // Drive's maximum supported subchannel - MmcSubchannel desiredSubchannel; // User requested subchannel - bool bcdSubchannel = false; // Subchannel positioning is in BCD - Dictionary isrcs = new(); - string mcn = null; - HashSet subchannelExtents = new(); - bool cdiReadyReadAsAudio = false; - uint firstLba; - var outputOptical = _outputPlugin as IWritableOpticalImage; + _dumpLog.WriteLine("Raw CD dumping not yet implemented"); + StoppingErrorMessage?.Invoke("Raw CD dumping not yet implemented"); - Dictionary mediaTags = new(); // Media tags - Dictionary smallestPregapLbaPerTrack = new(); + return; + } - MediaType dskType = MediaType.CD; + tracks = GetCdTracks(_dev, _dumpLog, _force, out lastSector, leadOutStarts, mediaTags, StoppingErrorMessage, + out toc, trackFlags, UpdateStatus); - if(_dumpRaw) - { - _dumpLog.WriteLine("Raw CD dumping not yet implemented"); - StoppingErrorMessage?.Invoke("Raw CD dumping not yet implemented"); + if(tracks is null) + { + _dumpLog.WriteLine("Could not get tracks!"); + StoppingErrorMessage?.Invoke("Could not get tracks!"); - return; - } + return; + } - tracks = GetCdTracks(_dev, _dumpLog, _force, out lastSector, leadOutStarts, mediaTags, StoppingErrorMessage, - out toc, trackFlags, UpdateStatus); + firstLba = (uint)tracks.Min(t => t.StartSector); - if(tracks is null) - { - _dumpLog.WriteLine("Could not get tracks!"); - StoppingErrorMessage?.Invoke("Could not get tracks!"); + // Check subchannels support + supportsPqSubchannel = SupportsPqSubchannel(_dev, _dumpLog, UpdateStatus, firstLba); + supportsRwSubchannel = SupportsRwSubchannel(_dev, _dumpLog, UpdateStatus, firstLba); - return; - } + if(supportsRwSubchannel) + supportedSubchannel = MmcSubchannel.Raw; + else if(supportsPqSubchannel) + supportedSubchannel = MmcSubchannel.Q16; + else + supportedSubchannel = MmcSubchannel.None; - firstLba = (uint)tracks.Min(t => t.StartSector); - - // Check subchannels support - supportsPqSubchannel = SupportsPqSubchannel(_dev, _dumpLog, UpdateStatus, firstLba); - supportsRwSubchannel = SupportsRwSubchannel(_dev, _dumpLog, UpdateStatus, firstLba); - - if(supportsRwSubchannel) - supportedSubchannel = MmcSubchannel.Raw; - else if(supportsPqSubchannel) - supportedSubchannel = MmcSubchannel.Q16; - else - supportedSubchannel = MmcSubchannel.None; - - switch(_subchannel) - { - case DumpSubchannel.Any: - if(supportsRwSubchannel) - desiredSubchannel = MmcSubchannel.Raw; - else if(supportsPqSubchannel) - desiredSubchannel = MmcSubchannel.Q16; - else - desiredSubchannel = MmcSubchannel.None; - - break; - case DumpSubchannel.Rw: - if(supportsRwSubchannel) - desiredSubchannel = MmcSubchannel.Raw; - else - { - _dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing..."); - - StoppingErrorMessage?. - Invoke("Drive does not support the requested subchannel format, not continuing..."); - - return; - } - - break; - case DumpSubchannel.RwOrPq: - if(supportsRwSubchannel) - desiredSubchannel = MmcSubchannel.Raw; - else if(supportsPqSubchannel) - desiredSubchannel = MmcSubchannel.Q16; - else - { - _dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing..."); - - StoppingErrorMessage?. - Invoke("Drive does not support the requested subchannel format, not continuing..."); - - return; - } - - break; - case DumpSubchannel.Pq: - if(supportsPqSubchannel) - desiredSubchannel = MmcSubchannel.Q16; - else - { - _dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing..."); - - StoppingErrorMessage?. - Invoke("Drive does not support the requested subchannel format, not continuing..."); - - return; - } - - break; - case DumpSubchannel.None: + switch(_subchannel) + { + case DumpSubchannel.Any: + if(supportsRwSubchannel) + desiredSubchannel = MmcSubchannel.Raw; + else if(supportsPqSubchannel) + desiredSubchannel = MmcSubchannel.Q16; + else desiredSubchannel = MmcSubchannel.None; - break; - default: throw new ArgumentOutOfRangeException(); - } - - if(desiredSubchannel == MmcSubchannel.Q16 && supportsPqSubchannel) - supportedSubchannel = MmcSubchannel.Q16; - - // Check if output format supports subchannels - if(!outputOptical.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) && - desiredSubchannel != MmcSubchannel.None) - { - if(_force || _subchannel == DumpSubchannel.None) - { - _dumpLog.WriteLine("Output format does not support subchannels, continuing..."); - UpdateStatus?.Invoke("Output format does not support subchannels, continuing..."); - } + break; + case DumpSubchannel.Rw: + if(supportsRwSubchannel) + desiredSubchannel = MmcSubchannel.Raw; else { - _dumpLog.WriteLine("Output format does not support subchannels, not continuing..."); - StoppingErrorMessage?.Invoke("Output format does not support subchannels, not continuing..."); + _dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing..."); + + StoppingErrorMessage?. + Invoke("Drive does not support the requested subchannel format, not continuing..."); return; } + break; + case DumpSubchannel.RwOrPq: + if(supportsRwSubchannel) + desiredSubchannel = MmcSubchannel.Raw; + else if(supportsPqSubchannel) + desiredSubchannel = MmcSubchannel.Q16; + else + { + _dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing..."); + + StoppingErrorMessage?. + Invoke("Drive does not support the requested subchannel format, not continuing..."); + + return; + } + + break; + case DumpSubchannel.Pq: + if(supportsPqSubchannel) + desiredSubchannel = MmcSubchannel.Q16; + else + { + _dumpLog.WriteLine("Drive does not support the requested subchannel format, not continuing..."); + + StoppingErrorMessage?. + Invoke("Drive does not support the requested subchannel format, not continuing..."); + + return; + } + + break; + case DumpSubchannel.None: desiredSubchannel = MmcSubchannel.None; - } - switch(supportedSubchannel) + break; + default: throw new ArgumentOutOfRangeException(); + } + + if(desiredSubchannel == MmcSubchannel.Q16 && supportsPqSubchannel) + supportedSubchannel = MmcSubchannel.Q16; + + // Check if output format supports subchannels + if(!outputOptical.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) && + desiredSubchannel != MmcSubchannel.None) + { + if(_force || _subchannel == DumpSubchannel.None) { - case MmcSubchannel.None: - _dumpLog.WriteLine("Checking if drive supports reading without subchannel..."); - UpdateStatus?.Invoke("Checking if drive supports reading without subchannel..."); - - readcd = !_dev.ReadCd(out cmdBuf, out _, firstLba, sectorSize, 1, MmcSectorTypes.AllTypes, false, - false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, _dev.Timeout, out _); - - if(!readcd) - { - _dumpLog.WriteLine("Drive does not support READ CD, trying SCSI READ commands..."); - ErrorMessage?.Invoke("Drive does not support READ CD, trying SCSI READ commands..."); - - _dumpLog.WriteLine("Checking if drive supports READ(6)..."); - UpdateStatus?.Invoke("Checking if drive supports READ(6)..."); - read6 = !_dev.Read6(out cmdBuf, out _, firstLba, 2048, 1, _dev.Timeout, out _); - _dumpLog.WriteLine("Checking if drive supports READ(10)..."); - UpdateStatus?.Invoke("Checking if drive supports READ(10)..."); - - read10 = !_dev.Read10(out cmdBuf, out _, 0, false, true, false, false, firstLba, 2048, 0, 1, - _dev.Timeout, out _); - - _dumpLog.WriteLine("Checking if drive supports READ(12)..."); - UpdateStatus?.Invoke("Checking if drive supports READ(12)..."); - - read12 = !_dev.Read12(out cmdBuf, out _, 0, false, true, false, false, firstLba, 2048, 0, 1, - false, _dev.Timeout, out _); - - _dumpLog.WriteLine("Checking if drive supports READ(16)..."); - UpdateStatus?.Invoke("Checking if drive supports READ(16)..."); - - read16 = !_dev.Read16(out cmdBuf, out _, 0, false, true, false, firstLba, 2048, 0, 1, false, - _dev.Timeout, out _); - - if(!read6 && - !read10 && - !read12 && - !read16) - { - _dumpLog.WriteLine("Cannot read from disc, not continuing..."); - StoppingErrorMessage?.Invoke("Cannot read from disc, not continuing..."); - - return; - } - - if(read6) - { - _dumpLog.WriteLine("Drive supports READ(6)..."); - UpdateStatus?.Invoke("Drive supports READ(6)..."); - } - - if(read10) - { - _dumpLog.WriteLine("Drive supports READ(10)..."); - UpdateStatus?.Invoke("Drive supports READ(10)..."); - } - - if(read12) - { - _dumpLog.WriteLine("Drive supports READ(12)..."); - UpdateStatus?.Invoke("Drive supports READ(12)..."); - } - - if(read16) - { - _dumpLog.WriteLine("Drive supports READ(16)..."); - UpdateStatus?.Invoke("Drive supports READ(16)..."); - } - } - - _dumpLog.WriteLine("Drive can read without subchannel..."); - UpdateStatus?.Invoke("Drive can read without subchannel..."); - - subSize = 0; - subType = TrackSubchannelType.None; - - break; - case MmcSubchannel.Raw: - _dumpLog.WriteLine("Full raw subchannel reading supported..."); - UpdateStatus?.Invoke("Full raw subchannel reading supported..."); - subType = TrackSubchannelType.Raw; - subSize = 96; - readcd = true; - - break; - case MmcSubchannel.Q16: - _dumpLog.WriteLine("PQ subchannel reading supported..."); - _dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); - UpdateStatus?.Invoke("PQ subchannel reading supported..."); - - UpdateStatus?. - Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); - - subType = TrackSubchannelType.Q16; - subSize = 16; - readcd = true; - - break; - default: - _dumpLog.WriteLine("Handling subchannel type {0} not supported, exiting...", supportedSubchannel); - - StoppingErrorMessage?. - Invoke($"Handling subchannel type {supportedSubchannel} not supported, exiting..."); - - return; + _dumpLog.WriteLine("Output format does not support subchannels, continuing..."); + UpdateStatus?.Invoke("Output format does not support subchannels, continuing..."); } - - switch(desiredSubchannel) + else { - case MmcSubchannel.None: - subType = TrackSubchannelType.None; - - break; - case MmcSubchannel.Raw: - case MmcSubchannel.Q16: - subType = TrackSubchannelType.Raw; - - break; - } - - blockSize = sectorSize + subSize; - - // Check if subchannel is BCD - if(supportedSubchannel != MmcSubchannel.None) - { - sense = _dev.ReadCd(out cmdBuf, out _, (((firstLba / 75) + 1) * 75) + 35, blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _); - - if(!sense) - { - tmpBuf = new byte[subSize]; - Array.Copy(cmdBuf, sectorSize, tmpBuf, 0, subSize); - - if(supportedSubchannel == MmcSubchannel.Q16) - tmpBuf = Subchannel.ConvertQToRaw(tmpBuf); - - tmpBuf = Subchannel.Deinterleave(tmpBuf); - - // 9th Q subchannel is always FRAME when in user data area - // LBA 35 => MSF 00:02:35 => FRAME 35 (in hexadecimal 0x23) - // Sometimes drive returns a pregap here but MSF 00:02:3x => FRAME 3x (hexadecimal 0x20 to 0x27) - bcdSubchannel = (tmpBuf[21] & 0x30) > 0; - - if(bcdSubchannel) - { - _dumpLog.WriteLine("Drive returns subchannel in BCD..."); - UpdateStatus?.Invoke("Drive returns subchannel in BCD..."); - } - else - { - _dumpLog.WriteLine("Drive does not returns subchannel in BCD..."); - UpdateStatus?.Invoke("Drive does not returns subchannel in BCD..."); - } - } - } - - foreach(Track trk in tracks) - trk.SubchannelType = subType; - - _dumpLog.WriteLine("Calculating pregaps, can take some time..."); - UpdateStatus?.Invoke("Calculating pregaps, can take some time..."); - - SolveTrackPregaps(_dev, _dumpLog, UpdateStatus, tracks, supportsPqSubchannel, supportsRwSubchannel, _dbDev, - out bool inexactPositioning, true); - - if(inexactPositioning) - { - _dumpLog.WriteLine("WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect."); - - UpdateStatus?. - Invoke("WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect."); - } - - if(!(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. - CanStoreRawData)) - { - if(!_force) - { - _dumpLog.WriteLine("Output format does not support storing raw data, this may end in a loss of data, not continuing..."); - - StoppingErrorMessage?. - Invoke("Output format does not support storing raw data, this may end in a loss of data, not continuing..."); - - return; - } - - _dumpLog.WriteLine("Output format does not support storing raw data, this may end in a loss of data, continuing..."); - - ErrorMessage?. - Invoke("Output format does not support storing raw data, this may end in a loss of data, continuing..."); - } - - if(!(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. - CanStoreAudioTracks) && - tracks.Any(track => track.Type == TrackType.Audio)) - { - _dumpLog.WriteLine("Output format does not support audio tracks, cannot continue..."); - - StoppingErrorMessage?.Invoke("Output format does not support audio tracks, cannot continue..."); + _dumpLog.WriteLine("Output format does not support subchannels, not continuing..."); + StoppingErrorMessage?.Invoke("Output format does not support subchannels, not continuing..."); return; } - if(!(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. - CanStorePregaps) && - tracks.Where(track => track.Sequence != tracks.First(t => t.Session == track.Session).Sequence). - Any(track => track.Pregap > 0)) - { - if(!_force) + desiredSubchannel = MmcSubchannel.None; + } + + switch(supportedSubchannel) + { + case MmcSubchannel.None: + _dumpLog.WriteLine("Checking if drive supports reading without subchannel..."); + UpdateStatus?.Invoke("Checking if drive supports reading without subchannel..."); + + readcd = !_dev.ReadCd(out cmdBuf, out _, firstLba, sectorSize, 1, MmcSectorTypes.AllTypes, false, + false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, _dev.Timeout, out _); + + if(!readcd) { - _dumpLog.WriteLine("Output format does not support pregaps, this may end in a loss of data, not continuing..."); + _dumpLog.WriteLine("Drive does not support READ CD, trying SCSI READ commands..."); + ErrorMessage?.Invoke("Drive does not support READ CD, trying SCSI READ commands..."); - StoppingErrorMessage?. - Invoke("Output format does not support pregaps, this may end in a loss of data, not continuing..."); + _dumpLog.WriteLine("Checking if drive supports READ(6)..."); + UpdateStatus?.Invoke("Checking if drive supports READ(6)..."); + read6 = !_dev.Read6(out cmdBuf, out _, firstLba, 2048, 1, _dev.Timeout, out _); + _dumpLog.WriteLine("Checking if drive supports READ(10)..."); + UpdateStatus?.Invoke("Checking if drive supports READ(10)..."); - return; - } + read10 = !_dev.Read10(out cmdBuf, out _, 0, false, true, false, false, firstLba, 2048, 0, 1, + _dev.Timeout, out _); - _dumpLog.WriteLine("Output format does not support pregaps, this may end in a loss of data, continuing..."); + _dumpLog.WriteLine("Checking if drive supports READ(12)..."); + UpdateStatus?.Invoke("Checking if drive supports READ(12)..."); - ErrorMessage?. - Invoke("Output format does not support pregaps, this may end in a loss of data, continuing..."); - } + read12 = !_dev.Read12(out cmdBuf, out _, 0, false, true, false, false, firstLba, 2048, 0, 1, + false, _dev.Timeout, out _); - for(int t = 1; t < tracks.Length; t++) - tracks[t - 1].EndSector = tracks[t].StartSector - 1; + _dumpLog.WriteLine("Checking if drive supports READ(16)..."); + UpdateStatus?.Invoke("Checking if drive supports READ(16)..."); - tracks[^1].EndSector = (ulong)lastSector; - blocks = (ulong)(lastSector + 1); + read16 = !_dev.Read16(out cmdBuf, out _, 0, false, true, false, firstLba, 2048, 0, 1, false, + _dev.Timeout, out _); - if(blocks == 0) - { - StoppingErrorMessage?.Invoke("Cannot dump blank media."); - - return; - } - - ResumeSupport.Process(true, true, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, - ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private, _force); - - if(currentTry == null || - extents == null) - { - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); - - return; - } - - // Read media tags - ReadCdTags(ref dskType, mediaTags, out sessions, out firstTrackLastSession); - - if(!(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. - CanStoreSessions) && - sessions > 1) - { - // TODO: Disabled until 6.0 - /*if(!_force) - {*/ - _dumpLog.WriteLine("Output format does not support sessions, this will end in a loss of data, not continuing..."); - - StoppingErrorMessage?. - Invoke("Output format does not support sessions, this will end in a loss of data, not continuing..."); - - return; - /*} - - _dumpLog.WriteLine("Output format does not support sessions, this will end in a loss of data, continuing..."); - - ErrorMessage?. - Invoke("Output format does not support sessions, this will end in a loss of data, continuing...");*/ - } - - // Check if output format supports all disc tags we have retrieved so far - foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputOptical.SupportedMediaTags.Contains(tag))) - { - if(_force) - { - _dumpLog.WriteLine("Output format does not support {0}, continuing...", tag); - ErrorMessage?.Invoke($"Output format does not support {tag}, continuing..."); - } - else - { - _dumpLog.WriteLine("Output format does not support {0}, not continuing...", tag); - StoppingErrorMessage?.Invoke($"Output format does not support {tag}, not continuing..."); - - return; - } - } - - if(leadOutStarts.Any()) - { - UpdateStatus?.Invoke("Solving lead-outs..."); - - foreach(KeyValuePair leadOuts in leadOutStarts) - foreach(Track trk in tracks.Where(trk => trk.Session == leadOuts.Key). - Where(trk => trk.EndSector >= (ulong)leadOuts.Value)) - trk.EndSector = (ulong)leadOuts.Value - 1; - - var dataExtents = new ExtentsULong(); - - foreach(Track trk in tracks) - dataExtents.Add(trk.StartSector, trk.EndSector); - - Tuple[] dataExtentsArray = dataExtents.ToArray(); - - for(int i = 0; i < dataExtentsArray.Length - 1; i++) - leadOutExtents.Add(dataExtentsArray[i].Item2 + 1, dataExtentsArray[i + 1].Item1 - 1); - } - - _dumpLog.WriteLine("Detecting disc type..."); - UpdateStatus?.Invoke("Detecting disc type..."); - - MMC.DetectDiscType(ref dskType, sessions, toc, _dev, out hiddenTrack, out hiddenData, firstTrackLastSession, - blocks); - - if(hiddenTrack || firstLba > 0) - { - _dumpLog.WriteLine("Disc contains a hidden track..."); - UpdateStatus?.Invoke("Disc contains a hidden track..."); - - List trkList = new() - { - new Track + if(!read6 && + !read10 && + !read12 && + !read16) { - Sequence = 0, - Session = 1, - Type = hiddenData ? TrackType.Data : TrackType.Audio, - StartSector = 0, - BytesPerSector = (int)sectorSize, - RawBytesPerSector = (int)sectorSize, - SubchannelType = subType, - EndSector = tracks.First(t => t.Sequence == 1).StartSector - 1 + _dumpLog.WriteLine("Cannot read from disc, not continuing..."); + StoppingErrorMessage?.Invoke("Cannot read from disc, not continuing..."); + + return; } - }; - trkList.AddRange(tracks); - tracks = trkList.ToArray(); - } + if(read6) + { + _dumpLog.WriteLine("Drive supports READ(6)..."); + UpdateStatus?.Invoke("Drive supports READ(6)..."); + } - if(tracks.Any(t => t.Type == TrackType.Audio) && - desiredSubchannel != MmcSubchannel.Raw) - { + if(read10) + { + _dumpLog.WriteLine("Drive supports READ(10)..."); + UpdateStatus?.Invoke("Drive supports READ(10)..."); + } + + if(read12) + { + _dumpLog.WriteLine("Drive supports READ(12)..."); + UpdateStatus?.Invoke("Drive supports READ(12)..."); + } + + if(read16) + { + _dumpLog.WriteLine("Drive supports READ(16)..."); + UpdateStatus?.Invoke("Drive supports READ(16)..."); + } + } + + _dumpLog.WriteLine("Drive can read without subchannel..."); + UpdateStatus?.Invoke("Drive can read without subchannel..."); + + subSize = 0; + subType = TrackSubchannelType.None; + + break; + case MmcSubchannel.Raw: + _dumpLog.WriteLine("Full raw subchannel reading supported..."); + UpdateStatus?.Invoke("Full raw subchannel reading supported..."); + subType = TrackSubchannelType.Raw; + subSize = 96; + readcd = true; + + break; + case MmcSubchannel.Q16: + _dumpLog.WriteLine("PQ subchannel reading supported..."); _dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + UpdateStatus?.Invoke("PQ subchannel reading supported..."); UpdateStatus?. Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); - } - // Check mode for tracks - foreach(Track trk in tracks.Where(t => t.Type != TrackType.Audio)) + subType = TrackSubchannelType.Q16; + subSize = 16; + readcd = true; + + break; + default: + _dumpLog.WriteLine("Handling subchannel type {0} not supported, exiting...", supportedSubchannel); + + StoppingErrorMessage?. + Invoke($"Handling subchannel type {supportedSubchannel} not supported, exiting..."); + + return; + } + + switch(desiredSubchannel) + { + case MmcSubchannel.None: + subType = TrackSubchannelType.None; + + break; + case MmcSubchannel.Raw: + case MmcSubchannel.Q16: + subType = TrackSubchannelType.Raw; + + break; + } + + blockSize = sectorSize + subSize; + + // Check if subchannel is BCD + if(supportedSubchannel != MmcSubchannel.None) + { + sense = _dev.ReadCd(out cmdBuf, out _, (((firstLba / 75) + 1) * 75) + 35, blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _); + + if(!sense) { - if(!readcd) - { - trk.Type = TrackType.CdMode1; + tmpBuf = new byte[subSize]; + Array.Copy(cmdBuf, sectorSize, tmpBuf, 0, subSize); - continue; + if(supportedSubchannel == MmcSubchannel.Q16) + tmpBuf = Subchannel.ConvertQToRaw(tmpBuf); + + tmpBuf = Subchannel.Deinterleave(tmpBuf); + + // 9th Q subchannel is always FRAME when in user data area + // LBA 35 => MSF 00:02:35 => FRAME 35 (in hexadecimal 0x23) + // Sometimes drive returns a pregap here but MSF 00:02:3x => FRAME 3x (hexadecimal 0x20 to 0x27) + bcdSubchannel = (tmpBuf[21] & 0x30) > 0; + + if(bcdSubchannel) + { + _dumpLog.WriteLine("Drive returns subchannel in BCD..."); + UpdateStatus?.Invoke("Drive returns subchannel in BCD..."); } - - _dumpLog.WriteLine("Checking mode for track {0}...", trk.Sequence); - UpdateStatus?.Invoke($"Checking mode for track {trk.Sequence}..."); - - sense = _dev.ReadCd(out cmdBuf, out _, (uint)(trk.StartSector + trk.Pregap), blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _); - - if(sense) + else { - _dumpLog.WriteLine("Unable to guess mode for track {0}, continuing...", trk.Sequence); - - UpdateStatus?.Invoke($"Unable to guess mode for track {trk.Sequence}, continuing..."); - - continue; - } - - int bufOffset = 0; - - while(cmdBuf[0 + bufOffset] != 0x00 || - cmdBuf[1 + bufOffset] != 0xFF || - cmdBuf[2 + bufOffset] != 0xFF || - cmdBuf[3 + bufOffset] != 0xFF || - cmdBuf[4 + bufOffset] != 0xFF || - cmdBuf[5 + bufOffset] != 0xFF || - cmdBuf[6 + bufOffset] != 0xFF || - cmdBuf[7 + bufOffset] != 0xFF || - cmdBuf[8 + bufOffset] != 0xFF || - cmdBuf[9 + bufOffset] != 0xFF || - cmdBuf[10 + bufOffset] != 0xFF || - cmdBuf[11 + bufOffset] != 0x00) - { - if(bufOffset + 12 >= cmdBuf.Length) - break; - - bufOffset++; - } - - switch(cmdBuf[15 + bufOffset]) - { - case 1: - case 0x61: // Scrambled - UpdateStatus?.Invoke($"Track {trk.Sequence} is MODE1"); - _dumpLog.WriteLine("Track {0} is MODE1", trk.Sequence); - trk.Type = TrackType.CdMode1; - - break; - case 2: - case 0x62: // Scrambled - if(dskType == MediaType.CDI || - dskType == MediaType.CDIREADY) - { - UpdateStatus?.Invoke($"Track {trk.Sequence} is MODE2"); - _dumpLog.WriteLine("Track {0} is MODE2", trk.Sequence); - trk.Type = TrackType.CdMode2Formless; - - break; - } - - if((cmdBuf[0x012] & 0x20) == 0x20) // mode 2 form 2 - { - UpdateStatus?.Invoke($"Track {trk.Sequence} is MODE2 FORM 2"); - _dumpLog.WriteLine("Track {0} is MODE2 FORM 2", trk.Sequence); - trk.Type = TrackType.CdMode2Form2; - - break; - } - - UpdateStatus?.Invoke($"Track {trk.Sequence} is MODE2 FORM 1"); - _dumpLog.WriteLine("Track {0} is MODE2 FORM 1", trk.Sequence); - trk.Type = TrackType.CdMode2Form1; - - // These media type specifications do not legally allow mode 2 tracks to be present - if(dskType == MediaType.CDROM || - dskType == MediaType.CDPLUS || - dskType == MediaType.CDV) - dskType = MediaType.CD; - - break; - default: - UpdateStatus?.Invoke($"Track {trk.Sequence} is unknown mode {cmdBuf[15]}"); - _dumpLog.WriteLine("Track {0} is unknown mode {1}", trk.Sequence, cmdBuf[15]); - - break; + _dumpLog.WriteLine("Drive does not returns subchannel in BCD..."); + UpdateStatus?.Invoke("Drive does not returns subchannel in BCD..."); } } + } - if(outputOptical.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) + foreach(Track trk in tracks) + trk.SubchannelType = subType; + + _dumpLog.WriteLine("Calculating pregaps, can take some time..."); + UpdateStatus?.Invoke("Calculating pregaps, can take some time..."); + + SolveTrackPregaps(_dev, _dumpLog, UpdateStatus, tracks, supportsPqSubchannel, supportsRwSubchannel, _dbDev, + out bool inexactPositioning, true); + + if(inexactPositioning) + { + _dumpLog.WriteLine("WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect."); + + UpdateStatus?. + Invoke("WARNING: The drive has returned incorrect Q positioning when calculating pregaps. A best effort has been tried but they may be incorrect."); + } + + if(!(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. + CanStoreRawData)) + { + if(!_force) { - if(tracks.Length > 1) - { - StoppingErrorMessage?.Invoke("Output format does not support more than 1 track, not continuing..."); - _dumpLog.WriteLine("Output format does not support more than 1 track, not continuing..."); + _dumpLog.WriteLine("Output format does not support storing raw data, this may end in a loss of data, not continuing..."); - return; - } - - if(tracks.Any(t => t.Type == TrackType.Audio)) - { - StoppingErrorMessage?.Invoke("Output format does not support audio tracks, not continuing..."); - _dumpLog.WriteLine("Output format does not support audio tracks, not continuing..."); - - return; - } - - if(tracks.Any(t => t.Type != TrackType.CdMode1)) - { - StoppingErrorMessage?.Invoke("Output format only supports MODE 1 tracks, not continuing..."); - _dumpLog.WriteLine("Output format only supports MODE 1 tracks, not continuing..."); - - return; - } - - supportsLongSectors = false; - } - - // Check if something prevents from dumping the first track pregap - if(_dumpFirstTrackPregap && readcd) - { - if(!outputOptical.SupportedMediaTags.Contains(MediaTagType.CD_FirstTrackPregap)) - { - if(_force) - { - _dumpLog.WriteLine("Output format does not support CD first track pregap, continuing..."); - ErrorMessage?.Invoke("Output format does not support CD first track pregap, continuing..."); - } - else - { - _dumpLog.WriteLine("Output format does not support CD first track pregap, not continuing..."); - - StoppingErrorMessage?. - Invoke("Output format does not support CD first track pregap, not continuing..."); - - return; - } - - _dumpFirstTrackPregap = false; - } - } - - // Try how many blocks are readable at once - while(true) - { - if(readcd) - { - sense = _dev.ReadCd(out cmdBuf, out _, firstLba, blockSize, _maximumReadable, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _); - - if(_dev.Error || sense) - _maximumReadable /= 2; - } - else if(read16) - { - sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, firstLba, blockSize, 0, - _maximumReadable, false, _dev.Timeout, out _); - - if(_dev.Error || sense) - _maximumReadable /= 2; - } - else if(read12) - { - sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, firstLba, blockSize, 0, - _maximumReadable, false, _dev.Timeout, out _); - - if(_dev.Error || sense) - _maximumReadable /= 2; - } - else if(read10) - { - sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, firstLba, blockSize, 0, - (ushort)_maximumReadable, _dev.Timeout, out _); - - if(_dev.Error || sense) - _maximumReadable /= 2; - } - else if(read6) - { - sense = _dev.Read6(out cmdBuf, out _, firstLba, blockSize, (byte)_maximumReadable, _dev.Timeout, - out _); - - if(_dev.Error || sense) - _maximumReadable /= 2; - } - - if(!_dev.Error || - _maximumReadable == 1) - break; - } - - if(_dev.Error || sense) - { - _dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", _dev.LastError); - StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); - } - - // Try to read the first track pregap - if(_dumpFirstTrackPregap && readcd) - ReadCdFirstTrackPregap(blockSize, ref currentSpeed, mediaTags, supportedSubchannel, ref totalDuration); - - _dumpLog.WriteLine("Reading {0} sectors at a time.", _maximumReadable); - _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); - _dumpLog.WriteLine("Device can read {0} blocks at a time.", _maximumReadable); - _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); - _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); - _dumpLog.WriteLine("Media identified as {0}.", dskType); - - UpdateStatus?.Invoke($"Reading {_maximumReadable} sectors at a time."); - UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); - UpdateStatus?.Invoke($"Device can read {_maximumReadable} blocks at a time."); - UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); - UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); - UpdateStatus?.Invoke($"Media identified as {dskType}."); - - ret = outputOptical.Create(_outputPath, dskType, _formatOptions, blocks, - supportsLongSectors ? blockSize : 2048); - - // Cannot create image - if(!ret) - { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputOptical.ErrorMessage); - - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + - outputOptical.ErrorMessage); + StoppingErrorMessage?. + Invoke("Output format does not support storing raw data, this may end in a loss of data, not continuing..."); return; } - ErrorNumber errno = outputOptical.ReadMediaTag(MediaTagType.CD_MCN, out byte[] mcnBytes); + _dumpLog.WriteLine("Output format does not support storing raw data, this may end in a loss of data, continuing..."); - if(errno == ErrorNumber.NoError) - mcn = Encoding.ASCII.GetString(mcnBytes); + ErrorMessage?. + Invoke("Output format does not support storing raw data, this may end in a loss of data, continuing..."); + } - if((outputOptical as IWritableOpticalImage).Tracks != null) - foreach(Track imgTrack in (outputOptical as IWritableOpticalImage).Tracks) - { - errno = (outputOptical as IWritableOpticalImage).ReadSectorTag(imgTrack.Sequence, - SectorTagType.CdTrackIsrc, out byte[] isrcBytes); + if(!(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. + CanStoreAudioTracks) && + tracks.Any(track => track.Type == TrackType.Audio)) + { + _dumpLog.WriteLine("Output format does not support audio tracks, cannot continue..."); - if(errno == ErrorNumber.NoError) - isrcs[(byte)imgTrack.Sequence] = Encoding.ASCII.GetString(isrcBytes); + StoppingErrorMessage?.Invoke("Output format does not support audio tracks, cannot continue..."); - Track trk = tracks.FirstOrDefault(t => t.Sequence == imgTrack.Sequence); + return; + } - if(trk == null) - continue; - - trk.Pregap = imgTrack.Pregap; - trk.StartSector = imgTrack.StartSector; - trk.EndSector = imgTrack.EndSector; - - foreach(KeyValuePair imgIdx in imgTrack.Indexes) - trk.Indexes[imgIdx.Key] = imgIdx.Value; - } - - // Send track list to output plugin. This may fail if subchannel is set but unsupported. - ret = (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - - if(!ret && - desiredSubchannel == MmcSubchannel.None) + if(!(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. + CanStorePregaps) && + tracks.Where(track => track.Sequence != tracks.First(t => t.Session == track.Session).Sequence). + Any(track => track.Pregap > 0)) + { + if(!_force) { - _dumpLog.WriteLine("Error sending tracks to output image, not continuing."); - _dumpLog.WriteLine(outputOptical.ErrorMessage); + _dumpLog.WriteLine("Output format does not support pregaps, this may end in a loss of data, not continuing..."); - StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing." + - Environment.NewLine + outputOptical.ErrorMessage); + StoppingErrorMessage?. + Invoke("Output format does not support pregaps, this may end in a loss of data, not continuing..."); return; } - // If a subchannel is supported, check if output plugin allows us to write it. - if(desiredSubchannel != MmcSubchannel.None && - !(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. - CanStoreSubchannelRw)) + _dumpLog.WriteLine("Output format does not support pregaps, this may end in a loss of data, continuing..."); + + ErrorMessage?. + Invoke("Output format does not support pregaps, this may end in a loss of data, continuing..."); + } + + for(int t = 1; t < tracks.Length; t++) + tracks[t - 1].EndSector = tracks[t].StartSector - 1; + + tracks[^1].EndSector = (ulong)lastSector; + blocks = (ulong)(lastSector + 1); + + if(blocks == 0) + { + StoppingErrorMessage?.Invoke("Cannot dump blank media."); + + return; + } + + ResumeSupport.Process(true, true, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, + ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private, _force); + + if(currentTry == null || + extents == null) + { + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + + return; + } + + // Read media tags + ReadCdTags(ref dskType, mediaTags, out sessions, out firstTrackLastSession); + + if(!(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. + CanStoreSessions) && + sessions > 1) + { + // TODO: Disabled until 6.0 + /*if(!_force) + {*/ + _dumpLog.WriteLine("Output format does not support sessions, this will end in a loss of data, not continuing..."); + + StoppingErrorMessage?. + Invoke("Output format does not support sessions, this will end in a loss of data, not continuing..."); + + return; + /*} + + _dumpLog.WriteLine("Output format does not support sessions, this will end in a loss of data, continuing..."); + + ErrorMessage?. + Invoke("Output format does not support sessions, this will end in a loss of data, continuing...");*/ + } + + // Check if output format supports all disc tags we have retrieved so far + foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputOptical.SupportedMediaTags.Contains(tag))) + { + if(_force) { - _dumpLog.WriteLine("Output image does not support subchannels, {0}continuing...", _force ? "" : "not "); - - if(_force) - ErrorMessage?.Invoke("Output image does not support subchannels, continuing..."); - else - { - StoppingErrorMessage?.Invoke("Output image does not support subchannels, not continuing..."); - - return; - } - } - - if(supportedSubchannel != MmcSubchannel.None) - { - _dumpLog.WriteLine($"Creating subchannel log in {_outputPrefix + ".sub.log"}"); - subLog = new SubchannelLog(_outputPrefix + ".sub.log", bcdSubchannel); - } - - // Set track flags - foreach(KeyValuePair kvp in trackFlags) - { - Track track = tracks.FirstOrDefault(t => t.Sequence == kvp.Key); - - if(track is null) - continue; - - _dumpLog.WriteLine("Setting flags for track {0}...", track.Sequence); - UpdateStatus?.Invoke($"Setting flags for track {track.Sequence}..."); - - outputOptical.WriteSectorTag(new[] - { - kvp.Value - }, kvp.Key, SectorTagType.CdTrackFlags); - } - - // Set MCN - if(supportedSubchannel == MmcSubchannel.None) - { - sense = _dev.ReadMcn(out mcn, out _, out _, _dev.Timeout, out _); - - if(!sense && - mcn != null && - mcn != "0000000000000") - { - UpdateStatus?.Invoke($"Found Media Catalogue Number: {mcn}"); - _dumpLog.WriteLine("Found Media Catalogue Number: {0}", mcn); - } - else - mcn = null; - } - - // Set ISRCs - if(supportedSubchannel == MmcSubchannel.None) - foreach(Track trk in tracks) - { - sense = _dev.ReadIsrc((byte)trk.Sequence, out string isrc, out _, out _, _dev.Timeout, out _); - - if(sense || - isrc == null || - isrc == "000000000000") - continue; - - isrcs[(byte)trk.Sequence] = isrc; - - UpdateStatus?.Invoke($"Found ISRC for track {trk.Sequence}: {isrc}"); - _dumpLog.WriteLine($"Found ISRC for track {trk.Sequence}: {isrc}"); - } - - if(supportedSubchannel != MmcSubchannel.None && - desiredSubchannel != MmcSubchannel.None) - { - subchannelExtents = new HashSet(); - - _resume.BadSubchannels ??= new List(); - - foreach(int sub in _resume.BadSubchannels) - subchannelExtents.Add(sub); - - if(_resume.NextBlock < blocks) - for(ulong i = _resume.NextBlock; i < blocks; i++) - subchannelExtents.Add((int)i); - } - - if(_resume.NextBlock > 0) - { - UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); - _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); - } - - if(_skip < _maximumReadable) - _skip = _maximumReadable; - - #if DEBUG - foreach(Track trk in tracks) - UpdateStatus?. - Invoke($"Track {trk.Sequence} starts at LBA {trk.StartSector} and ends at LBA {trk.EndSector}"); - #endif - - // Check offset - if(_fixOffset) - { - if(tracks.All(t => t.Type != TrackType.Audio)) - { - // No audio tracks so no need to fix offset - _dumpLog.WriteLine("No audio tracks, disabling offset fix."); - UpdateStatus.Invoke("No audio tracks, disabling offset fix."); - - _fixOffset = false; - } - - if(!readcd) - { - _dumpLog.WriteLine("READ CD command is not supported, disabling offset fix. Dump may not be correct."); - - UpdateStatus?. - Invoke("READ CD command is not supported, disabling offset fix. Dump may not be correct."); - - _fixOffset = false; - } - } - else if(tracks.Any(t => t.Type == TrackType.Audio)) - { - _dumpLog.WriteLine("There are audio tracks and offset fixing is disabled, dump may not be correct."); - UpdateStatus?.Invoke("There are audio tracks and offset fixing is disabled, dump may not be correct."); - } - - // Search for read offset in main database - cdOffset = - _ctx.CdOffsets.FirstOrDefault(d => (d.Manufacturer == _dev.Manufacturer || - d.Manufacturer == _dev.Manufacturer.Replace('/', '-')) && - (d.Model == _dev.Model || d.Model == _dev.Model.Replace('/', '-'))); - - Media.Info.CompactDisc.GetOffset(cdOffset, _dbDev, _debug, _dev, dskType, _dumpLog, tracks, UpdateStatus, - out int? driveOffset, out int? combinedOffset, out _supportsPlextorD8); - - if(combinedOffset is null) - { - if(driveOffset is null) - { - _dumpLog.WriteLine("Drive reading offset not found in database."); - UpdateStatus?.Invoke("Drive reading offset not found in database."); - _dumpLog.WriteLine("Disc offset cannot be calculated."); - UpdateStatus?.Invoke("Disc offset cannot be calculated."); - - if(tracks.Any(t => t.Type == TrackType.Audio)) - { - _dumpLog.WriteLine("Dump may not be correct."); - - UpdateStatus?.Invoke("Dump may not be correct."); - } - - if(_fixOffset) - _fixOffset = false; - } - else - { - _dumpLog.WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); - UpdateStatus?.Invoke($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); - - _dumpLog.WriteLine("Disc write offset is unknown, dump may not be correct."); - UpdateStatus?.Invoke("Disc write offset is unknown, dump may not be correct."); - - offsetBytes = driveOffset.Value; - - sectorsForOffset = offsetBytes / (int)sectorSize; - - if(sectorsForOffset < 0) - sectorsForOffset *= -1; - - if(offsetBytes % sectorSize != 0) - sectorsForOffset++; - } + _dumpLog.WriteLine("Output format does not support {0}, continuing...", tag); + ErrorMessage?.Invoke($"Output format does not support {tag}, continuing..."); } else { - offsetBytes = combinedOffset.Value; + _dumpLog.WriteLine("Output format does not support {0}, not continuing...", tag); + StoppingErrorMessage?.Invoke($"Output format does not support {tag}, not continuing..."); + + return; + } + } + + if(leadOutStarts.Any()) + { + UpdateStatus?.Invoke("Solving lead-outs..."); + + foreach(KeyValuePair leadOuts in leadOutStarts) + foreach(Track trk in tracks.Where(trk => trk.Session == leadOuts.Key). + Where(trk => trk.EndSector >= (ulong)leadOuts.Value)) + trk.EndSector = (ulong)leadOuts.Value - 1; + + var dataExtents = new ExtentsULong(); + + foreach(Track trk in tracks) + dataExtents.Add(trk.StartSector, trk.EndSector); + + Tuple[] dataExtentsArray = dataExtents.ToArray(); + + for(int i = 0; i < dataExtentsArray.Length - 1; i++) + leadOutExtents.Add(dataExtentsArray[i].Item2 + 1, dataExtentsArray[i + 1].Item1 - 1); + } + + _dumpLog.WriteLine("Detecting disc type..."); + UpdateStatus?.Invoke("Detecting disc type..."); + + MMC.DetectDiscType(ref dskType, sessions, toc, _dev, out hiddenTrack, out hiddenData, firstTrackLastSession, + blocks); + + if(hiddenTrack || firstLba > 0) + { + _dumpLog.WriteLine("Disc contains a hidden track..."); + UpdateStatus?.Invoke("Disc contains a hidden track..."); + + List trkList = new() + { + new Track + { + Sequence = 0, + Session = 1, + Type = hiddenData ? TrackType.Data : TrackType.Audio, + StartSector = 0, + BytesPerSector = (int)sectorSize, + RawBytesPerSector = (int)sectorSize, + SubchannelType = subType, + EndSector = tracks.First(t => t.Sequence == 1).StartSector - 1 + } + }; + + trkList.AddRange(tracks); + tracks = trkList.ToArray(); + } + + if(tracks.Any(t => t.Type == TrackType.Audio) && + desiredSubchannel != MmcSubchannel.Raw) + { + _dumpLog.WriteLine("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + + UpdateStatus?. + Invoke("WARNING: If disc says CD+G, CD+EG, CD-MIDI, CD Graphics or CD Enhanced Graphics, dump will be incorrect!"); + } + + // Check mode for tracks + foreach(Track trk in tracks.Where(t => t.Type != TrackType.Audio)) + { + if(!readcd) + { + trk.Type = TrackType.CdMode1; + + continue; + } + + _dumpLog.WriteLine("Checking mode for track {0}...", trk.Sequence); + UpdateStatus?.Invoke($"Checking mode for track {trk.Sequence}..."); + + sense = _dev.ReadCd(out cmdBuf, out _, (uint)(trk.StartSector + trk.Pregap), blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _); + + if(sense) + { + _dumpLog.WriteLine("Unable to guess mode for track {0}, continuing...", trk.Sequence); + + UpdateStatus?.Invoke($"Unable to guess mode for track {trk.Sequence}, continuing..."); + + continue; + } + + int bufOffset = 0; + + while(cmdBuf[0 + bufOffset] != 0x00 || + cmdBuf[1 + bufOffset] != 0xFF || + cmdBuf[2 + bufOffset] != 0xFF || + cmdBuf[3 + bufOffset] != 0xFF || + cmdBuf[4 + bufOffset] != 0xFF || + cmdBuf[5 + bufOffset] != 0xFF || + cmdBuf[6 + bufOffset] != 0xFF || + cmdBuf[7 + bufOffset] != 0xFF || + cmdBuf[8 + bufOffset] != 0xFF || + cmdBuf[9 + bufOffset] != 0xFF || + cmdBuf[10 + bufOffset] != 0xFF || + cmdBuf[11 + bufOffset] != 0x00) + { + if(bufOffset + 12 >= cmdBuf.Length) + break; + + bufOffset++; + } + + switch(cmdBuf[15 + bufOffset]) + { + case 1: + case 0x61: // Scrambled + UpdateStatus?.Invoke($"Track {trk.Sequence} is MODE1"); + _dumpLog.WriteLine("Track {0} is MODE1", trk.Sequence); + trk.Type = TrackType.CdMode1; + + break; + case 2: + case 0x62: // Scrambled + if(dskType == MediaType.CDI || + dskType == MediaType.CDIREADY) + { + UpdateStatus?.Invoke($"Track {trk.Sequence} is MODE2"); + _dumpLog.WriteLine("Track {0} is MODE2", trk.Sequence); + trk.Type = TrackType.CdMode2Formless; + + break; + } + + if((cmdBuf[0x012] & 0x20) == 0x20) // mode 2 form 2 + { + UpdateStatus?.Invoke($"Track {trk.Sequence} is MODE2 FORM 2"); + _dumpLog.WriteLine("Track {0} is MODE2 FORM 2", trk.Sequence); + trk.Type = TrackType.CdMode2Form2; + + break; + } + + UpdateStatus?.Invoke($"Track {trk.Sequence} is MODE2 FORM 1"); + _dumpLog.WriteLine("Track {0} is MODE2 FORM 1", trk.Sequence); + trk.Type = TrackType.CdMode2Form1; + + // These media type specifications do not legally allow mode 2 tracks to be present + if(dskType == MediaType.CDROM || + dskType == MediaType.CDPLUS || + dskType == MediaType.CDV) + dskType = MediaType.CD; + + break; + default: + UpdateStatus?.Invoke($"Track {trk.Sequence} is unknown mode {cmdBuf[15]}"); + _dumpLog.WriteLine("Track {0} is unknown mode {1}", trk.Sequence, cmdBuf[15]); + + break; + } + } + + if(outputOptical.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) + { + if(tracks.Length > 1) + { + StoppingErrorMessage?.Invoke("Output format does not support more than 1 track, not continuing..."); + _dumpLog.WriteLine("Output format does not support more than 1 track, not continuing..."); + + return; + } + + if(tracks.Any(t => t.Type == TrackType.Audio)) + { + StoppingErrorMessage?.Invoke("Output format does not support audio tracks, not continuing..."); + _dumpLog.WriteLine("Output format does not support audio tracks, not continuing..."); + + return; + } + + if(tracks.Any(t => t.Type != TrackType.CdMode1)) + { + StoppingErrorMessage?.Invoke("Output format only supports MODE 1 tracks, not continuing..."); + _dumpLog.WriteLine("Output format only supports MODE 1 tracks, not continuing..."); + + return; + } + + supportsLongSectors = false; + } + + // Check if something prevents from dumping the first track pregap + if(_dumpFirstTrackPregap && readcd) + { + if(!outputOptical.SupportedMediaTags.Contains(MediaTagType.CD_FirstTrackPregap)) + { + if(_force) + { + _dumpLog.WriteLine("Output format does not support CD first track pregap, continuing..."); + ErrorMessage?.Invoke("Output format does not support CD first track pregap, continuing..."); + } + else + { + _dumpLog.WriteLine("Output format does not support CD first track pregap, not continuing..."); + + StoppingErrorMessage?. + Invoke("Output format does not support CD first track pregap, not continuing..."); + + return; + } + + _dumpFirstTrackPregap = false; + } + } + + // Try how many blocks are readable at once + while(true) + { + if(readcd) + { + sense = _dev.ReadCd(out cmdBuf, out _, firstLba, blockSize, _maximumReadable, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out _); + + if(_dev.Error || sense) + _maximumReadable /= 2; + } + else if(read16) + { + sense = _dev.Read16(out cmdBuf, out _, 0, false, true, false, firstLba, blockSize, 0, + _maximumReadable, false, _dev.Timeout, out _); + + if(_dev.Error || sense) + _maximumReadable /= 2; + } + else if(read12) + { + sense = _dev.Read12(out cmdBuf, out _, 0, false, true, false, false, firstLba, blockSize, 0, + _maximumReadable, false, _dev.Timeout, out _); + + if(_dev.Error || sense) + _maximumReadable /= 2; + } + else if(read10) + { + sense = _dev.Read10(out cmdBuf, out _, 0, false, true, false, false, firstLba, blockSize, 0, + (ushort)_maximumReadable, _dev.Timeout, out _); + + if(_dev.Error || sense) + _maximumReadable /= 2; + } + else if(read6) + { + sense = _dev.Read6(out cmdBuf, out _, firstLba, blockSize, (byte)_maximumReadable, _dev.Timeout, + out _); + + if(_dev.Error || sense) + _maximumReadable /= 2; + } + + if(!_dev.Error || + _maximumReadable == 1) + break; + } + + if(_dev.Error || sense) + { + _dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", _dev.LastError); + StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + } + + // Try to read the first track pregap + if(_dumpFirstTrackPregap && readcd) + ReadCdFirstTrackPregap(blockSize, ref currentSpeed, mediaTags, supportedSubchannel, ref totalDuration); + + _dumpLog.WriteLine("Reading {0} sectors at a time.", _maximumReadable); + _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); + _dumpLog.WriteLine("Device can read {0} blocks at a time.", _maximumReadable); + _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); + _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); + _dumpLog.WriteLine("Media identified as {0}.", dskType); + + UpdateStatus?.Invoke($"Reading {_maximumReadable} sectors at a time."); + UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); + UpdateStatus?.Invoke($"Device can read {_maximumReadable} blocks at a time."); + UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); + UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); + UpdateStatus?.Invoke($"Media identified as {dskType}."); + + ret = outputOptical.Create(_outputPath, dskType, _formatOptions, blocks, + supportsLongSectors ? blockSize : 2048); + + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputOptical.ErrorMessage); + + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputOptical.ErrorMessage); + + return; + } + + ErrorNumber errno = outputOptical.ReadMediaTag(MediaTagType.CD_MCN, out byte[] mcnBytes); + + if(errno == ErrorNumber.NoError) + mcn = Encoding.ASCII.GetString(mcnBytes); + + if((outputOptical as IWritableOpticalImage).Tracks != null) + foreach(Track imgTrack in (outputOptical as IWritableOpticalImage).Tracks) + { + errno = (outputOptical as IWritableOpticalImage).ReadSectorTag(imgTrack.Sequence, + SectorTagType.CdTrackIsrc, out byte[] isrcBytes); + + if(errno == ErrorNumber.NoError) + isrcs[(byte)imgTrack.Sequence] = Encoding.ASCII.GetString(isrcBytes); + + Track trk = tracks.FirstOrDefault(t => t.Sequence == imgTrack.Sequence); + + if(trk == null) + continue; + + trk.Pregap = imgTrack.Pregap; + trk.StartSector = imgTrack.StartSector; + trk.EndSector = imgTrack.EndSector; + + foreach(KeyValuePair imgIdx in imgTrack.Indexes) + trk.Indexes[imgIdx.Key] = imgIdx.Value; + } + + // Send track list to output plugin. This may fail if subchannel is set but unsupported. + ret = (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + + if(!ret && + desiredSubchannel == MmcSubchannel.None) + { + _dumpLog.WriteLine("Error sending tracks to output image, not continuing."); + _dumpLog.WriteLine(outputOptical.ErrorMessage); + + StoppingErrorMessage?.Invoke("Error sending tracks to output image, not continuing." + + Environment.NewLine + outputOptical.ErrorMessage); + + return; + } + + // If a subchannel is supported, check if output plugin allows us to write it. + if(desiredSubchannel != MmcSubchannel.None && + !(outputOptical as IWritableOpticalImage).OpticalCapabilities.HasFlag(OpticalImageCapabilities. + CanStoreSubchannelRw)) + { + _dumpLog.WriteLine("Output image does not support subchannels, {0}continuing...", _force ? "" : "not "); + + if(_force) + ErrorMessage?.Invoke("Output image does not support subchannels, continuing..."); + else + { + StoppingErrorMessage?.Invoke("Output image does not support subchannels, not continuing..."); + + return; + } + } + + if(supportedSubchannel != MmcSubchannel.None) + { + _dumpLog.WriteLine($"Creating subchannel log in {_outputPrefix + ".sub.log"}"); + subLog = new SubchannelLog(_outputPrefix + ".sub.log", bcdSubchannel); + } + + // Set track flags + foreach(KeyValuePair kvp in trackFlags) + { + Track track = tracks.FirstOrDefault(t => t.Sequence == kvp.Key); + + if(track is null) + continue; + + _dumpLog.WriteLine("Setting flags for track {0}...", track.Sequence); + UpdateStatus?.Invoke($"Setting flags for track {track.Sequence}..."); + + outputOptical.WriteSectorTag(new[] + { + kvp.Value + }, kvp.Key, SectorTagType.CdTrackFlags); + } + + // Set MCN + if(supportedSubchannel == MmcSubchannel.None) + { + sense = _dev.ReadMcn(out mcn, out _, out _, _dev.Timeout, out _); + + if(!sense && + mcn != null && + mcn != "0000000000000") + { + UpdateStatus?.Invoke($"Found Media Catalogue Number: {mcn}"); + _dumpLog.WriteLine("Found Media Catalogue Number: {0}", mcn); + } + else + mcn = null; + } + + // Set ISRCs + if(supportedSubchannel == MmcSubchannel.None) + foreach(Track trk in tracks) + { + sense = _dev.ReadIsrc((byte)trk.Sequence, out string isrc, out _, out _, _dev.Timeout, out _); + + if(sense || + isrc == null || + isrc == "000000000000") + continue; + + isrcs[(byte)trk.Sequence] = isrc; + + UpdateStatus?.Invoke($"Found ISRC for track {trk.Sequence}: {isrc}"); + _dumpLog.WriteLine($"Found ISRC for track {trk.Sequence}: {isrc}"); + } + + if(supportedSubchannel != MmcSubchannel.None && + desiredSubchannel != MmcSubchannel.None) + { + subchannelExtents = new HashSet(); + + _resume.BadSubchannels ??= new List(); + + foreach(int sub in _resume.BadSubchannels) + subchannelExtents.Add(sub); + + if(_resume.NextBlock < blocks) + for(ulong i = _resume.NextBlock; i < blocks; i++) + subchannelExtents.Add((int)i); + } + + if(_resume.NextBlock > 0) + { + UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); + _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + } + + if(_skip < _maximumReadable) + _skip = _maximumReadable; + + #if DEBUG + foreach(Track trk in tracks) + UpdateStatus?. + Invoke($"Track {trk.Sequence} starts at LBA {trk.StartSector} and ends at LBA {trk.EndSector}"); + #endif + + // Check offset + if(_fixOffset) + { + if(tracks.All(t => t.Type != TrackType.Audio)) + { + // No audio tracks so no need to fix offset + _dumpLog.WriteLine("No audio tracks, disabling offset fix."); + UpdateStatus.Invoke("No audio tracks, disabling offset fix."); + + _fixOffset = false; + } + + if(!readcd) + { + _dumpLog.WriteLine("READ CD command is not supported, disabling offset fix. Dump may not be correct."); + + UpdateStatus?. + Invoke("READ CD command is not supported, disabling offset fix. Dump may not be correct."); + + _fixOffset = false; + } + } + else if(tracks.Any(t => t.Type == TrackType.Audio)) + { + _dumpLog.WriteLine("There are audio tracks and offset fixing is disabled, dump may not be correct."); + UpdateStatus?.Invoke("There are audio tracks and offset fixing is disabled, dump may not be correct."); + } + + // Search for read offset in main database + cdOffset = + _ctx.CdOffsets.FirstOrDefault(d => (d.Manufacturer == _dev.Manufacturer || + d.Manufacturer == _dev.Manufacturer.Replace('/', '-')) && + (d.Model == _dev.Model || d.Model == _dev.Model.Replace('/', '-'))); + + Media.Info.CompactDisc.GetOffset(cdOffset, _dbDev, _debug, _dev, dskType, _dumpLog, tracks, UpdateStatus, + out int? driveOffset, out int? combinedOffset, out _supportsPlextorD8); + + if(combinedOffset is null) + { + if(driveOffset is null) + { + _dumpLog.WriteLine("Drive reading offset not found in database."); + UpdateStatus?.Invoke("Drive reading offset not found in database."); + _dumpLog.WriteLine("Disc offset cannot be calculated."); + UpdateStatus?.Invoke("Disc offset cannot be calculated."); + + if(tracks.Any(t => t.Type == TrackType.Audio)) + { + _dumpLog.WriteLine("Dump may not be correct."); + + UpdateStatus?.Invoke("Dump may not be correct."); + } + + if(_fixOffset) + _fixOffset = false; + } + else + { + _dumpLog.WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); + UpdateStatus?.Invoke($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); + + _dumpLog.WriteLine("Disc write offset is unknown, dump may not be correct."); + UpdateStatus?.Invoke("Disc write offset is unknown, dump may not be correct."); + + offsetBytes = driveOffset.Value; + sectorsForOffset = offsetBytes / (int)sectorSize; if(sectorsForOffset < 0) @@ -1060,358 +1048,369 @@ namespace Aaru.Core.Devices.Dumping if(offsetBytes % sectorSize != 0) sectorsForOffset++; - - if(driveOffset is null) - { - _dumpLog.WriteLine("Drive reading offset not found in database."); - UpdateStatus?.Invoke("Drive reading offset not found in database."); - _dumpLog.WriteLine($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)."); - - UpdateStatus?. - Invoke($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)."); - } - else - { - _dumpLog.WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); - UpdateStatus?.Invoke($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); - - discOffset = offsetBytes - driveOffset; - - _dumpLog.WriteLine($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)"); - - UpdateStatus?.Invoke($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)"); - } } + } + else + { + offsetBytes = combinedOffset.Value; + sectorsForOffset = offsetBytes / (int)sectorSize; - if(!_fixOffset || - tracks.All(t => t.Type != TrackType.Audio)) + if(sectorsForOffset < 0) + sectorsForOffset *= -1; + + if(offsetBytes % sectorSize != 0) + sectorsForOffset++; + + if(driveOffset is null) { - offsetBytes = 0; - sectorsForOffset = 0; + _dumpLog.WriteLine("Drive reading offset not found in database."); + UpdateStatus?.Invoke("Drive reading offset not found in database."); + _dumpLog.WriteLine($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)."); + + UpdateStatus?. + Invoke($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)."); } - - mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, _maximumReadable, _private); - ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008); - - audioExtents = new ExtentsULong(); - - foreach(Track audioTrack in tracks.Where(t => t.Type == TrackType.Audio)) + else { - audioExtents.Add(audioTrack.StartSector, audioTrack.EndSector); + _dumpLog.WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); + UpdateStatus?.Invoke($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); + + discOffset = offsetBytes - driveOffset; + + _dumpLog.WriteLine($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)"); + + UpdateStatus?.Invoke($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)"); } + } - // Set speed - if(_speedMultiplier >= 0) + if(!_fixOffset || + tracks.All(t => t.Type != TrackType.Audio)) + { + offsetBytes = 0; + sectorsForOffset = 0; + } + + mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, _maximumReadable, _private); + ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008); + + audioExtents = new ExtentsULong(); + + foreach(Track audioTrack in tracks.Where(t => t.Type == TrackType.Audio)) + { + audioExtents.Add(audioTrack.StartSector, audioTrack.EndSector); + } + + // Set speed + if(_speedMultiplier >= 0) + { + _dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX for data reading" : $"{_speed}x")}."); + UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX for data reading" : $"{_speed}x")}."); + + _speed *= _speedMultiplier; + + if(_speed == 0 || + _speed > 0xFFFF) + _speed = 0xFFFF; + + _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _); + } + + // Start reading + start = DateTime.UtcNow; + + if(dskType == MediaType.CDIREADY) + { + Track track0 = tracks.FirstOrDefault(t => t.Sequence == 0); + + track0.Type = TrackType.CdMode2Formless; + + if(!supportsLongSectors) { - _dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX for data reading" : $"{_speed}x")}."); - UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX for data reading" : $"{_speed}x")}."); + _dumpLog.WriteLine("Dumping CD-i Ready requires the output image format to support long sectors."); - _speed *= _speedMultiplier; - - if(_speed == 0 || - _speed > 0xFFFF) - _speed = 0xFFFF; - - _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _); - } - - // Start reading - start = DateTime.UtcNow; - - if(dskType == MediaType.CDIREADY) - { - Track track0 = tracks.FirstOrDefault(t => t.Sequence == 0); - - track0.Type = TrackType.CdMode2Formless; - - if(!supportsLongSectors) - { - _dumpLog.WriteLine("Dumping CD-i Ready requires the output image format to support long sectors."); - - StoppingErrorMessage?. - Invoke("Dumping CD-i Ready requires the output image format to support long sectors."); - - return; - } - - if(!readcd) - { - _dumpLog.WriteLine("Dumping CD-i Ready requires the drive to support the READ CD command."); - - StoppingErrorMessage?. - Invoke("Dumping CD-i Ready requires the drive to support the READ CD command."); - - return; - } - - _dev.ReadCd(out cmdBuf, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, - out _); - - hiddenData = IsData(cmdBuf); - - if(!hiddenData) - { - cdiReadyReadAsAudio = IsScrambledData(cmdBuf, 0, out combinedOffset); - - if(cdiReadyReadAsAudio) - { - offsetBytes = combinedOffset.Value; - sectorsForOffset = offsetBytes / (int)sectorSize; - - if(sectorsForOffset < 0) - sectorsForOffset *= -1; - - if(offsetBytes % sectorSize != 0) - sectorsForOffset++; - - _dumpLog.WriteLine("Enabling skipping CD-i Ready hole because drive returns data as audio."); - - UpdateStatus?.Invoke("Enabling skipping CD-i Ready hole because drive returns data as audio."); - - _skipCdireadyHole = true; - - if(driveOffset is null) - { - _dumpLog.WriteLine("Drive reading offset not found in database."); - UpdateStatus?.Invoke("Drive reading offset not found in database."); - - _dumpLog. - WriteLine($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)."); - - UpdateStatus?. - Invoke($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)."); - } - else - { - _dumpLog. - WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); - - UpdateStatus?. - Invoke($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); - - discOffset = offsetBytes - driveOffset; - - _dumpLog.WriteLine($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)"); - - UpdateStatus?.Invoke($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)"); - } - } - } - - if(!_skipCdireadyHole) - { - _dumpLog.WriteLine("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them."); - - UpdateStatus?. - Invoke("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them."); - } - - if(_skipCdireadyHole) - ReadCdiReady(blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration, - leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, subSize, supportedSubchannel, - ref totalDuration, tracks, subLog, desiredSubchannel, isrcs, ref mcn, - subchannelExtents, blocks, cdiReadyReadAsAudio, offsetBytes, sectorsForOffset, - smallestPregapLbaPerTrack); - } - - ReadCdData(audioExtents, blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, - ref imageWriteDuration, lastSector, leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, - out newTrim, tracks[0].Type != TrackType.Audio, offsetBytes, read6, read10, read12, read16, - readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, ref totalDuration, - tracks, subLog, desiredSubchannel, isrcs, ref mcn, subchannelExtents, smallestPregapLbaPerTrack); - - // TODO: Enable when underlying images support lead-outs - /* - DumpCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration, - leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd, - supportedSubchannel, subSize, ref totalDuration, subLog, desiredSubchannel, isrcs, ref mcn, tracks, - smallestPregapLbaPerTrack); - */ - - end = DateTime.UtcNow; - mhddLog.Close(); - - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - TrimCdUserData(audioExtents, blockSize, currentTry, extents, newTrim, offsetBytes, read6, read10, read12, - read16, readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, - ref totalDuration, subLog, desiredSubchannel, tracks, isrcs, ref mcn, subchannelExtents, - smallestPregapLbaPerTrack); - - RetryCdUserData(audioExtents, blockSize, currentTry, extents, offsetBytes, readcd, sectorsForOffset, - subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel, tracks, isrcs, - ref mcn, subchannelExtents, smallestPregapLbaPerTrack, supportsLongSectors); - - foreach(Tuple leadoutExtent in leadOutExtents.ToArray()) - { - for(ulong e = leadoutExtent.Item1; e <= leadoutExtent.Item2; e++) - subchannelExtents.Remove((int)e); - } - - if(subchannelExtents.Count > 0 && - _retryPasses > 0 && - _retrySubchannel) - RetrySubchannel(readcd, subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel, - tracks, isrcs, ref mcn, subchannelExtents, smallestPregapLbaPerTrack); - - // Write media tags to image - if(!_aborted) - foreach(KeyValuePair tag in mediaTags) - { - if(tag.Value is null) - { - AaruConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key); - - continue; - } - - ret = outputOptical.WriteMediaTag(tag.Value, tag.Key); - - if(ret || _force) - continue; - - // Cannot write tag to image - _dumpLog.WriteLine($"Cannot write tag {tag.Key}."); - StoppingErrorMessage?.Invoke(outputOptical.ErrorMessage); - - return; - } - - _resume.BadBlocks.Sort(); - - foreach(ulong bad in _resume.BadBlocks) - _dumpLog.WriteLine("Sector {0} could not be read.", bad); - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - _resume.BadSubchannels = new List(); - _resume.BadSubchannels.AddRange(subchannelExtents); - _resume.BadSubchannels.Sort(); - - if(_generateSubchannels && - outputOptical.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) && - !_aborted) - Media.CompactDisc.GenerateSubchannels(subchannelExtents, tracks, trackFlags, blocks, subLog, _dumpLog, - InitProgress, UpdateProgress, EndProgress, outputOptical); - - // TODO: Disc ID - var metadata = new CommonTypes.Structs.ImageInfo - { - Application = "Aaru", - ApplicationVersion = Version.GetVersion() - }; - - if(!outputOptical.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputOptical.ErrorMessage); - - outputOptical.SetDumpHardware(_resume.Tries); - - if(_preSidecar != null) - outputOptical.SetCicmMetadata(_preSidecar); - - foreach(KeyValuePair isrc in isrcs) - { - // TODO: Track tags - if(!outputOptical.WriteSectorTag(Encoding.ASCII.GetBytes(isrc.Value), isrc.Key, - SectorTagType.CdTrackIsrc)) - continue; - - UpdateStatus?.Invoke($"Setting ISRC for track {isrc.Key} to {isrc.Value}"); - _dumpLog.WriteLine("Setting ISRC for track {0} to {1}", isrc.Key, isrc.Value); - } - - if(mcn != null && - outputOptical.WriteMediaTag(Encoding.ASCII.GetBytes(mcn), MediaTagType.CD_MCN)) - { - UpdateStatus?.Invoke($"Setting disc Media Catalogue Number to {mcn}"); - _dumpLog.WriteLine("Setting disc Media Catalogue Number to {0}", mcn); - } - - foreach(Track trk in tracks) - { - // Fix track starts in each session's first track - if(tracks.Where(t => t.Session == trk.Session).OrderBy(t => t.Sequence).FirstOrDefault().Sequence == - trk.Sequence) - { - if(trk.Sequence == 1) - continue; - - trk.StartSector -= trk.Pregap; - trk.Indexes[0] = (int)trk.StartSector; - - continue; - } - - if(trk.Indexes.TryGetValue(0, out int idx0) && - trk.Indexes.TryGetValue(1, out int idx1) && - idx0 == idx1) - trk.Indexes.Remove(0); - } - - (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputOptical.Close(); - DateTime closeEnd = DateTime.Now; - UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - - subLog?.Close(); - - if(_aborted) - { - _dumpLog.WriteLine("Aborted!"); + StoppingErrorMessage?. + Invoke("Dumping CD-i Ready requires the output image format to support long sectors."); return; } - double totalChkDuration = 0; + if(!readcd) + { + _dumpLog.WriteLine("Dumping CD-i Ready requires the drive to support the READ CD command."); - if(_metadata) - WriteOpticalSidecar(blockSize, blocks, dskType, null, mediaTags, sessions, out totalChkDuration, - discOffset); + StoppingErrorMessage?. + Invoke("Dumping CD-i Ready requires the drive to support the READ CD command."); - end = DateTime.UtcNow; - UpdateStatus?.Invoke(""); + return; + } - UpdateStatus?. - Invoke($"Took a total of {(end - dumpStart).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + _dev.ReadCd(out cmdBuf, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, + out _); - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + hiddenData = IsData(cmdBuf); - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + if(!hiddenData) + { + cdiReadyReadAsAudio = IsScrambledData(cmdBuf, 0, out combinedOffset); - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + if(cdiReadyReadAsAudio) + { + offsetBytes = combinedOffset.Value; + sectorsForOffset = offsetBytes / (int)sectorSize; - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - UpdateStatus?.Invoke($"{_resume.BadSubchannels.Count} subchannels could not be read."); - UpdateStatus?.Invoke(""); + if(sectorsForOffset < 0) + sectorsForOffset *= -1; - Statistics.AddMedia(dskType, true); + if(offsetBytes % sectorSize != 0) + sectorsForOffset++; + + _dumpLog.WriteLine("Enabling skipping CD-i Ready hole because drive returns data as audio."); + + UpdateStatus?.Invoke("Enabling skipping CD-i Ready hole because drive returns data as audio."); + + _skipCdireadyHole = true; + + if(driveOffset is null) + { + _dumpLog.WriteLine("Drive reading offset not found in database."); + UpdateStatus?.Invoke("Drive reading offset not found in database."); + + _dumpLog. + WriteLine($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)."); + + UpdateStatus?. + Invoke($"Combined disc and drive offsets are {offsetBytes} bytes ({offsetBytes / 4} samples)."); + } + else + { + _dumpLog. + WriteLine($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); + + UpdateStatus?. + Invoke($"Drive reading offset is {driveOffset} bytes ({driveOffset / 4} samples)."); + + discOffset = offsetBytes - driveOffset; + + _dumpLog.WriteLine($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)"); + + UpdateStatus?.Invoke($"Disc offsets is {discOffset} bytes ({discOffset / 4} samples)"); + } + } + } + + if(!_skipCdireadyHole) + { + _dumpLog.WriteLine("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them."); + + UpdateStatus?. + Invoke("There will be thousand of errors between track 0 and track 1, that is normal and you can ignore them."); + } + + if(_skipCdireadyHole) + ReadCdiReady(blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration, + leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, subSize, supportedSubchannel, + ref totalDuration, tracks, subLog, desiredSubchannel, isrcs, ref mcn, + subchannelExtents, blocks, cdiReadyReadAsAudio, offsetBytes, sectorsForOffset, + smallestPregapLbaPerTrack); } + + ReadCdData(audioExtents, blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, + ref imageWriteDuration, lastSector, leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, + out newTrim, tracks[0].Type != TrackType.Audio, offsetBytes, read6, read10, read12, read16, + readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, ref totalDuration, + tracks, subLog, desiredSubchannel, isrcs, ref mcn, subchannelExtents, smallestPregapLbaPerTrack); + + // TODO: Enable when underlying images support lead-outs + /* + DumpCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration, + leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd, + supportedSubchannel, subSize, ref totalDuration, subLog, desiredSubchannel, isrcs, ref mcn, tracks, + smallestPregapLbaPerTrack); + */ + + end = DateTime.UtcNow; + mhddLog.Close(); + + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + + TrimCdUserData(audioExtents, blockSize, currentTry, extents, newTrim, offsetBytes, read6, read10, read12, + read16, readcd, sectorsForOffset, subSize, supportedSubchannel, supportsLongSectors, + ref totalDuration, subLog, desiredSubchannel, tracks, isrcs, ref mcn, subchannelExtents, + smallestPregapLbaPerTrack); + + RetryCdUserData(audioExtents, blockSize, currentTry, extents, offsetBytes, readcd, sectorsForOffset, + subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel, tracks, isrcs, + ref mcn, subchannelExtents, smallestPregapLbaPerTrack, supportsLongSectors); + + foreach(Tuple leadoutExtent in leadOutExtents.ToArray()) + { + for(ulong e = leadoutExtent.Item1; e <= leadoutExtent.Item2; e++) + subchannelExtents.Remove((int)e); + } + + if(subchannelExtents.Count > 0 && + _retryPasses > 0 && + _retrySubchannel) + RetrySubchannel(readcd, subSize, supportedSubchannel, ref totalDuration, subLog, desiredSubchannel, + tracks, isrcs, ref mcn, subchannelExtents, smallestPregapLbaPerTrack); + + // Write media tags to image + if(!_aborted) + foreach(KeyValuePair tag in mediaTags) + { + if(tag.Value is null) + { + AaruConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key); + + continue; + } + + ret = outputOptical.WriteMediaTag(tag.Value, tag.Key); + + if(ret || _force) + continue; + + // Cannot write tag to image + _dumpLog.WriteLine($"Cannot write tag {tag.Key}."); + StoppingErrorMessage?.Invoke(outputOptical.ErrorMessage); + + return; + } + + _resume.BadBlocks.Sort(); + + foreach(ulong bad in _resume.BadBlocks) + _dumpLog.WriteLine("Sector {0} could not be read.", bad); + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + _resume.BadSubchannels = new List(); + _resume.BadSubchannels.AddRange(subchannelExtents); + _resume.BadSubchannels.Sort(); + + if(_generateSubchannels && + outputOptical.SupportedSectorTags.Contains(SectorTagType.CdSectorSubchannel) && + !_aborted) + Media.CompactDisc.GenerateSubchannels(subchannelExtents, tracks, trackFlags, blocks, subLog, _dumpLog, + InitProgress, UpdateProgress, EndProgress, outputOptical); + + // TODO: Disc ID + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion() + }; + + if(!outputOptical.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputOptical.ErrorMessage); + + outputOptical.SetDumpHardware(_resume.Tries); + + if(_preSidecar != null) + outputOptical.SetCicmMetadata(_preSidecar); + + foreach(KeyValuePair isrc in isrcs) + { + // TODO: Track tags + if(!outputOptical.WriteSectorTag(Encoding.ASCII.GetBytes(isrc.Value), isrc.Key, + SectorTagType.CdTrackIsrc)) + continue; + + UpdateStatus?.Invoke($"Setting ISRC for track {isrc.Key} to {isrc.Value}"); + _dumpLog.WriteLine("Setting ISRC for track {0} to {1}", isrc.Key, isrc.Value); + } + + if(mcn != null && + outputOptical.WriteMediaTag(Encoding.ASCII.GetBytes(mcn), MediaTagType.CD_MCN)) + { + UpdateStatus?.Invoke($"Setting disc Media Catalogue Number to {mcn}"); + _dumpLog.WriteLine("Setting disc Media Catalogue Number to {0}", mcn); + } + + foreach(Track trk in tracks) + { + // Fix track starts in each session's first track + if(tracks.Where(t => t.Session == trk.Session).OrderBy(t => t.Sequence).FirstOrDefault().Sequence == + trk.Sequence) + { + if(trk.Sequence == 1) + continue; + + trk.StartSector -= trk.Pregap; + trk.Indexes[0] = (int)trk.StartSector; + + continue; + } + + if(trk.Indexes.TryGetValue(0, out int idx0) && + trk.Indexes.TryGetValue(1, out int idx1) && + idx0 == idx1) + trk.Indexes.Remove(0); + } + + (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputOptical.Close(); + DateTime closeEnd = DateTime.Now; + UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); + + subLog?.Close(); + + if(_aborted) + { + _dumpLog.WriteLine("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + if(_metadata) + WriteOpticalSidecar(blockSize, blocks, dskType, null, mediaTags, sessions, out totalChkDuration, + discOffset); + + end = DateTime.UtcNow; + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - dumpStart).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + UpdateStatus?.Invoke($"{_resume.BadSubchannels.Count} subchannels could not be read."); + UpdateStatus?.Invoke(""); + + Statistics.AddMedia(dskType, true); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs index 9a2a5f56a..bf02420b2 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Error.cs @@ -49,641 +49,640 @@ using TrackType = Aaru.CommonTypes.Enums.TrackType; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Retried errored sectors in CompactDisc + /// Extents with audio sectors + /// Size of the read sector in bytes + /// Current dump hardware try + /// Extents + /// Read offset + /// Device supports READ CD + /// Sectors needed to fix offset + /// Subchannel size in bytes + /// Drive's maximum supported subchannel + /// Supports reading EDC and ECC + /// Total commands duration + /// Disc tracks + /// Subchannel log + /// Subchannel desired to save + /// List of disc ISRCs + /// Disc media catalogue number + /// List of subchannels not yet dumped correctly + /// List of smallest pregap relative address per track + void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, + ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize, + MmcSubchannel supportedSubchannel, ref double totalDuration, SubchannelLog subLog, + MmcSubchannel desiredSubchannel, Track[] tracks, Dictionary isrcs, + ref string mcn, HashSet subchannelExtents, + Dictionary smallestPregapLbaPerTrack, bool supportsLongSectors) { - /// Retried errored sectors in CompactDisc - /// Extents with audio sectors - /// Size of the read sector in bytes - /// Current dump hardware try - /// Extents - /// Read offset - /// Device supports READ CD - /// Sectors needed to fix offset - /// Subchannel size in bytes - /// Drive's maximum supported subchannel - /// Supports reading EDC and ECC - /// Total commands duration - /// Disc tracks - /// Subchannel log - /// Subchannel desired to save - /// List of disc ISRCs - /// Disc media catalogue number - /// List of subchannels not yet dumped correctly - /// List of smallest pregap relative address per track - void RetryCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, - ExtentsULong extents, int offsetBytes, bool readcd, int sectorsForOffset, uint subSize, - MmcSubchannel supportedSubchannel, ref double totalDuration, SubchannelLog subLog, - MmcSubchannel desiredSubchannel, Track[] tracks, Dictionary isrcs, - ref string mcn, HashSet subchannelExtents, - Dictionary smallestPregapLbaPerTrack, bool supportsLongSectors) + bool sense = true; // Sense indicator + byte[] cmdBuf = null; // Data buffer + double cmdDuration; // Command execution time + const uint sectorSize = 2352; // Full sector size + byte[] senseBuf = null; // Sense buffer + PlextorSubchannel supportedPlextorSubchannel; + var outputOptical = _outputPlugin as IWritableOpticalImage; + + switch(supportedSubchannel) { - bool sense = true; // Sense indicator - byte[] cmdBuf = null; // Data buffer - double cmdDuration; // Command execution time - const uint sectorSize = 2352; // Full sector size - byte[] senseBuf = null; // Sense buffer - PlextorSubchannel supportedPlextorSubchannel; - var outputOptical = _outputPlugin as IWritableOpticalImage; + case MmcSubchannel.None: + supportedPlextorSubchannel = PlextorSubchannel.None; - switch(supportedSubchannel) + break; + case MmcSubchannel.Raw: + supportedPlextorSubchannel = PlextorSubchannel.Pack; + + break; + case MmcSubchannel.Q16: + supportedPlextorSubchannel = PlextorSubchannel.Q16; + + break; + default: + supportedPlextorSubchannel = PlextorSubchannel.None; + + break; + } + + if(_resume.BadBlocks.Count <= 0 || + _aborted || + _retryPasses <= 0) + return; + + int pass = 1; + bool forward = true; + bool runningPersistent = false; + + Modes.ModePage? currentModePage = null; + byte[] md6; + byte[] md10; + + if(_persistent) + { + Modes.ModePage_01_MMC pgMmc; + + sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, + out _); + + if(sense) { - case MmcSubchannel.None: - supportedPlextorSubchannel = PlextorSubchannel.None; + sense = _dev.ModeSense10(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, + _dev.Timeout, out _); - break; - case MmcSubchannel.Raw: - supportedPlextorSubchannel = PlextorSubchannel.Pack; - - break; - case MmcSubchannel.Q16: - supportedPlextorSubchannel = PlextorSubchannel.Q16; - - break; - default: - supportedPlextorSubchannel = PlextorSubchannel.None; - - break; - } - - if(_resume.BadBlocks.Count <= 0 || - _aborted || - _retryPasses <= 0) - return; - - int pass = 1; - bool forward = true; - bool runningPersistent = false; - - Modes.ModePage? currentModePage = null; - byte[] md6; - byte[] md10; - - if(_persistent) - { - Modes.ModePage_01_MMC pgMmc; - - sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, - out _); - - if(sense) + if(!sense) { - sense = _dev.ModeSense10(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x01, - _dev.Timeout, out _); + Modes.DecodedMode? dcMode10 = + Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); - if(!sense) - { - Modes.DecodedMode? dcMode10 = - Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); - - if(dcMode10?.Pages != null) - foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) - currentModePage = modePage; - } - } - else - { - Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); - - if(dcMode6?.Pages != null) - foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) + if(dcMode10?.Pages != null) + foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) currentModePage = modePage; } + } + else + { + Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); - if(currentModePage == null) + if(dcMode6?.Pages != null) + foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) + currentModePage = modePage; + } + + if(currentModePage == null) + { + pgMmc = new Modes.ModePage_01_MMC { - pgMmc = new Modes.ModePage_01_MMC - { - PS = false, - ReadRetryCount = 32, - Parameter = 0x00 - }; + PS = false, + ReadRetryCount = 32, + Parameter = 0x00 + }; - currentModePage = new Modes.ModePage + currentModePage = new Modes.ModePage + { + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + }; + } + + pgMmc = new Modes.ModePage_01_MMC + { + PS = false, + ReadRetryCount = 255, + Parameter = 0x20 + }; + + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) - }; + } + } + }; + + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + md10 = Modes.EncodeMode10(md, _dev.ScsiType); + + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); + sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) + sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) + { + UpdateStatus?. + Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + + AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); + + _dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + } + else + { + runningPersistent = true; + } + } + + InitProgress?.Invoke(); + cdRepeatRetry: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); + List sectorsNotEvenPartial = new List(); + + for(int i = 0; i < tmpArray.Length; i++) + { + ulong badSector = tmpArray[i]; + + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); + + Track track = tracks.OrderBy(t => t.StartSector). + LastOrDefault(t => badSector >= t.StartSector); + + byte sectorsToReRead = 1; + uint badSectorToReRead = (uint)badSector; + + if(_fixOffset && + audioExtents.Contains(badSector) && + offsetBytes != 0) + { + if(offsetBytes > 0) + { + badSectorToReRead -= (uint)sectorsForOffset; } - pgMmc = new Modes.ModePage_01_MMC - { - PS = false, - ReadRetryCount = 255, - Parameter = 0x20 - }; + sectorsToReRead = (byte)(sectorsForOffset + 1); + } - var md = new Modes.DecodedMode + if(_supportsPlextorD8 && audioExtents.Contains(badSector)) + { + sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, + sectorsToReRead, supportedPlextorSubchannel, out cmdDuration); + + totalDuration += cmdDuration; + } + else if(readcd) + { + if(audioExtents.Contains(badSector)) { - Header = new Modes.ModeHeader(), - Pages = new[] + sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead, + MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); + + if(sense) { - new Modes.ModePage + DecodedSense? decSense = Sense.Decode(senseBuf); + + // Try to workaround firmware + if((decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05) || + decSense?.ASC == 0x64) { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + sense = _dev.ReadCd(out cmdBuf, out _, badSectorToReRead, blockSize, sectorsToReRead, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, _dev.Timeout, out double cmdDuration2); + + cmdDuration += cmdDuration2; } } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - md10 = Modes.EncodeMode10(md, _dev.ScsiType); - - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); - sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - { - UpdateStatus?. - Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - - AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - - _dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } else { - runningPersistent = true; - } - } + sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, + true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, + out cmdDuration); - InitProgress?.Invoke(); - cdRepeatRetry: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - List sectorsNotEvenPartial = new List(); - - for(int i = 0; i < tmpArray.Length; i++) - { - ulong badSector = tmpArray[i]; - - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, - forward ? "forward" : "reverse", - runningPersistent ? "recovering partial data, " : "")); - - Track track = tracks.OrderBy(t => t.StartSector). - LastOrDefault(t => badSector >= t.StartSector); - - byte sectorsToReRead = 1; - uint badSectorToReRead = (uint)badSector; - - if(_fixOffset && - audioExtents.Contains(badSector) && - offsetBytes != 0) - { - if(offsetBytes > 0) + if(sense) { - badSectorToReRead -= (uint)sectorsForOffset; - } + DecodedSense? decSense = Sense.Decode(senseBuf); - sectorsToReRead = (byte)(sectorsForOffset + 1); - } - - if(_supportsPlextorD8 && audioExtents.Contains(badSector)) - { - sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, - sectorsToReRead, supportedPlextorSubchannel, out cmdDuration); - - totalDuration += cmdDuration; - } - else if(readcd) - { - if(audioExtents.Contains(badSector)) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead, - MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - - if(sense) + // Try to workaround firmware + if((decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05) || + decSense?.ASC == 0x64) { - DecodedSense? decSense = Sense.Decode(senseBuf); + sense = _dev.ReadCd(out cmdBuf, out _, badSectorToReRead, blockSize, sectorsToReRead, + MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, supportedSubchannel, _dev.Timeout, + out double cmdDuration2); - // Try to workaround firmware - if((decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05) || - decSense?.ASC == 0x64) - { - sense = _dev.ReadCd(out cmdBuf, out _, badSectorToReRead, blockSize, sectorsToReRead, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, _dev.Timeout, out double cmdDuration2); - - cmdDuration += cmdDuration2; - } + cmdDuration += cmdDuration2; } } - else + } + + totalDuration += cmdDuration; + } + + if(sense || _dev.Error) + { + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); + + if(!runningPersistent) + continue; + + DecodedSense? decSense = Sense.Decode(senseBuf); + + // MEDIUM ERROR, retry with ignore error below + if(decSense is { ASC: 0x11 }) + if(!sectorsNotEvenPartial.Contains(badSector)) + sectorsNotEvenPartial.Add(badSector); + } + + // Because one block has been partially used to fix the offset + if(_fixOffset && + audioExtents.Contains(badSector) && + offsetBytes != 0) + { + uint blocksToRead = sectorsToReRead; + + FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, + subSize, ref cmdBuf, blockSize, false); + } + + if(!sense && + !_dev.Error) + { + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + UpdateStatus?.Invoke($"Correctly retried sector {badSector} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass); + sectorsNotEvenPartial.Remove(badSector); + } + else + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); + + if(supportedSubchannel != MmcSubchannel.None) + { + byte[] data = new byte[sectorSize]; + byte[] sub = new byte[subSize]; + Array.Copy(cmdBuf, 0, data, 0, sectorSize); + Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); + + if(supportsLongSectors) + outputOptical.WriteSectorLong(data, badSector); + else + outputOptical.WriteSector(Sector.GetUserData(data), badSector); + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.Sequence, ref mcn, + tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); + + // Set tracks and go back + if(!indexesChanged) + continue; + + (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + i--; + } + else + { + if(supportsLongSectors) + outputOptical.WriteSectorLong(cmdBuf, badSector); + else + outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector); + } + } + + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) + { + pass++; + forward = !forward; + _resume.BadBlocks.Sort(); + + if(!forward) + _resume.BadBlocks.Reverse(); + + goto cdRepeatRetry; + } + + EndProgress?.Invoke(); + + // TODO: Enable when underlying images support lead-outs + /* + RetryCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration, + leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd, + supportedSubchannel, subSize, ref totalDuration); + */ + + // Try to ignore read errors, on some drives this allows to recover partial even if damaged data + if(_persistent && sectorsNotEvenPartial.Count > 0) + { + var pgMmc = new Modes.ModePage_01_MMC + { + PS = false, + ReadRetryCount = 255, + Parameter = 0x01 + }; + + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + new Modes.ModePage { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToReRead, blockSize, sectorsToReRead, + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + } + } + }; + + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + md10 = Modes.EncodeMode10(md, _dev.ScsiType); + + _dumpLog.WriteLine("Sending MODE SELECT to drive (ignore error correction)."); + sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) + sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); + + if(!sense) + { + runningPersistent = true; + + InitProgress?.Invoke(); + + for(int i = 0; i < sectorsNotEvenPartial.Count; i++) + { + ulong badSector = sectorsNotEvenPartial[i]; + + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}"); + + Track track = tracks.OrderBy(t => t.StartSector). + LastOrDefault(t => badSector >= t.StartSector); + + if(readcd) + { + sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - if(sense) - { - DecodedSense? decSense = Sense.Decode(senseBuf); - - // Try to workaround firmware - if((decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05) || - decSense?.ASC == 0x64) - { - sense = _dev.ReadCd(out cmdBuf, out _, badSectorToReRead, blockSize, sectorsToReRead, - MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, supportedSubchannel, _dev.Timeout, - out double cmdDuration2); - - cmdDuration += cmdDuration2; - } - } + totalDuration += cmdDuration; } - totalDuration += cmdDuration; - } - - if(sense || _dev.Error) - { - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - if(!runningPersistent) - continue; - - DecodedSense? decSense = Sense.Decode(senseBuf); - - // MEDIUM ERROR, retry with ignore error below - if(decSense is { ASC: 0x11 }) - if(!sectorsNotEvenPartial.Contains(badSector)) - sectorsNotEvenPartial.Add(badSector); - } - - // Because one block has been partially used to fix the offset - if(_fixOffset && - audioExtents.Contains(badSector) && - offsetBytes != 0) - { - uint blocksToRead = sectorsToReRead; - - FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, - subSize, ref cmdBuf, blockSize, false); - } - - if(!sense && - !_dev.Error) - { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - UpdateStatus?.Invoke($"Correctly retried sector {badSector} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried sector {0} in pass {1}.", badSector, pass); - sectorsNotEvenPartial.Remove(badSector); - } - else - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - if(supportedSubchannel != MmcSubchannel.None) - { - byte[] data = new byte[sectorSize]; - byte[] sub = new byte[subSize]; - Array.Copy(cmdBuf, 0, data, 0, sectorSize); - Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); - - if(supportsLongSectors) - outputOptical.WriteSectorLong(data, badSector); - else - outputOptical.WriteSector(Sector.GetUserData(data), badSector); - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.Sequence, ref mcn, - tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, - _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); - - // Set tracks and go back - if(!indexesChanged) - continue; - - (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - i--; - } - else - { - if(supportsLongSectors) - outputOptical.WriteSectorLong(cmdBuf, badSector); - else - outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector); - } - } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto cdRepeatRetry; - } - - EndProgress?.Invoke(); - - // TODO: Enable when underlying images support lead-outs - /* - RetryCdLeadOuts(blocks, blockSize, ref currentSpeed, currentTry, extents, ibgLog, ref imageWriteDuration, - leadOutExtents, ref maxSpeed, mhddLog, ref minSpeed, read6, read10, read12, read16, readcd, - supportedSubchannel, subSize, ref totalDuration); - */ - - // Try to ignore read errors, on some drives this allows to recover partial even if damaged data - if(_persistent && sectorsNotEvenPartial.Count > 0) - { - var pgMmc = new Modes.ModePage_01_MMC - { - PS = false, - ReadRetryCount = 255, - Parameter = 0x01 - }; - - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] + if(sense || _dev.Error) { - new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) - } + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); + + continue; } - }; - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - md10 = Modes.EncodeMode10(md, _dev.ScsiType); + _dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass); - _dumpLog.WriteLine("Sending MODE SELECT to drive (ignore error correction)."); - sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); - - if(!sense) - { - runningPersistent = true; - - InitProgress?.Invoke(); - - for(int i = 0; i < sectorsNotEvenPartial.Count; i++) + if(supportedSubchannel != MmcSubchannel.None) { - ulong badSector = sectorsNotEvenPartial[i]; + byte[] data = new byte[sectorSize]; + byte[] sub = new byte[subSize]; + Array.Copy(cmdBuf, 0, data, 0, sectorSize); + Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke($"Trying to get partial data for sector {badSector}"); - - Track track = tracks.OrderBy(t => t.StartSector). - LastOrDefault(t => badSector >= t.StartSector); - - if(readcd) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)badSector, blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, - true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, - out cmdDuration); - - totalDuration += cmdDuration; - } - - if(sense || _dev.Error) - { - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - continue; - } - - _dumpLog.WriteLine("Got partial data for sector {0} in pass {1}.", badSector, pass); - - if(supportedSubchannel != MmcSubchannel.None) - { - byte[] data = new byte[sectorSize]; - byte[] sub = new byte[subSize]; - Array.Copy(cmdBuf, 0, data, 0, sectorSize); - Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); - - if(supportsLongSectors) - outputOptical.WriteSectorLong(data, badSector); - else - outputOptical.WriteSector(Sector.GetUserData(data), badSector); - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.Sequence, - ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, - _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, - smallestPregapLbaPerTrack, true); - - // Set tracks and go back - if(indexesChanged) - { - (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - i--; - } - } + if(supportsLongSectors) + outputOptical.WriteSectorLong(data, badSector); else + outputOptical.WriteSector(Sector.GetUserData(data), badSector); + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.Sequence, + ref mcn, tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, + _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, + smallestPregapLbaPerTrack, true); + + // Set tracks and go back + if(indexesChanged) { - if(supportsLongSectors) - outputOptical.WriteSectorLong(cmdBuf, badSector); - else - outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector); + (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + i--; } } - - EndProgress?.Invoke(); - } - } - - if(runningPersistent && currentModePage.HasValue) - { - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] + else { - currentModePage.Value + if(supportsLongSectors) + outputOptical.WriteSectorLong(cmdBuf, badSector); + else + outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector); } - }; + } - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - md10 = Modes.EncodeMode10(md, _dev.ScsiType); - - _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); - sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); + EndProgress?.Invoke(); } - - EndProgress?.Invoke(); } - /// Retried errored subchannels in CompactDisc - /// Device supports READ CD - /// Subchannel size in bytes - /// Drive's maximum supported subchannel - /// Total commands duration - /// Disc tracks - /// Subchannel log - /// Subchannel desired to save - /// List of disc ISRCs - /// Disc media catalogue number - /// List of subchannels not yet dumped correctly - /// List of smallest pregap relative address per track - void RetrySubchannel(bool readcd, uint subSize, MmcSubchannel supportedSubchannel, ref double totalDuration, - SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks, - Dictionary isrcs, ref string mcn, HashSet subchannelExtents, - Dictionary smallestPregapLbaPerTrack) + if(runningPersistent && currentModePage.HasValue) { - bool sense = true; // Sense indicator - byte[] cmdBuf = null; // Data buffer - double cmdDuration; // Command execution time - byte[] senseBuf = null; // Sense buffer - PlextorSubchannel supportedPlextorSubchannel; - var outputOptical = _outputPlugin as IWritableOpticalImage; - - if(supportedSubchannel == MmcSubchannel.None || - desiredSubchannel == MmcSubchannel.None) - return; - - switch(supportedSubchannel) + var md = new Modes.DecodedMode { - case MmcSubchannel.None: - supportedPlextorSubchannel = PlextorSubchannel.None; + Header = new Modes.ModeHeader(), + Pages = new[] + { + currentModePage.Value + } + }; - break; - case MmcSubchannel.Raw: - supportedPlextorSubchannel = PlextorSubchannel.All; + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + md10 = Modes.EncodeMode10(md, _dev.ScsiType); - break; - case MmcSubchannel.Q16: - supportedPlextorSubchannel = PlextorSubchannel.Q16; + _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); + sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); - break; - case MmcSubchannel.Rw: - supportedPlextorSubchannel = PlextorSubchannel.Pack; + if(sense) + _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); + } - break; - default: - supportedPlextorSubchannel = PlextorSubchannel.None; + EndProgress?.Invoke(); + } - break; - } + /// Retried errored subchannels in CompactDisc + /// Device supports READ CD + /// Subchannel size in bytes + /// Drive's maximum supported subchannel + /// Total commands duration + /// Disc tracks + /// Subchannel log + /// Subchannel desired to save + /// List of disc ISRCs + /// Disc media catalogue number + /// List of subchannels not yet dumped correctly + /// List of smallest pregap relative address per track + void RetrySubchannel(bool readcd, uint subSize, MmcSubchannel supportedSubchannel, ref double totalDuration, + SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks, + Dictionary isrcs, ref string mcn, HashSet subchannelExtents, + Dictionary smallestPregapLbaPerTrack) + { + bool sense = true; // Sense indicator + byte[] cmdBuf = null; // Data buffer + double cmdDuration; // Command execution time + byte[] senseBuf = null; // Sense buffer + PlextorSubchannel supportedPlextorSubchannel; + var outputOptical = _outputPlugin as IWritableOpticalImage; + + if(supportedSubchannel == MmcSubchannel.None || + desiredSubchannel == MmcSubchannel.None) + return; + + switch(supportedSubchannel) + { + case MmcSubchannel.None: + supportedPlextorSubchannel = PlextorSubchannel.None; + + break; + case MmcSubchannel.Raw: + supportedPlextorSubchannel = PlextorSubchannel.All; + + break; + case MmcSubchannel.Q16: + supportedPlextorSubchannel = PlextorSubchannel.Q16; + + break; + case MmcSubchannel.Rw: + supportedPlextorSubchannel = PlextorSubchannel.Pack; + + break; + default: + supportedPlextorSubchannel = PlextorSubchannel.None; + + break; + } + + if(_aborted) + return; + + int pass = 1; + bool forward = true; + + InitProgress?.Invoke(); + + cdRepeatRetry: + + _resume.BadSubchannels = new List(); + _resume.BadSubchannels.AddRange(subchannelExtents); + _resume.BadSubchannels.Sort(); + + if(!forward) + _resume.BadSubchannels.Reverse(); + + int[] tmpArray = _resume.BadSubchannels.ToArray(); + + foreach(int bs in tmpArray) + { + uint badSector = (uint)bs; + + Track track = tracks.OrderBy(t => t.StartSector). + LastOrDefault(t => badSector >= t.StartSector); if(_aborted) - return; - - int pass = 1; - bool forward = true; - - InitProgress?.Invoke(); - - cdRepeatRetry: - - _resume.BadSubchannels = new List(); - _resume.BadSubchannels.AddRange(subchannelExtents); - _resume.BadSubchannels.Sort(); - - if(!forward) - _resume.BadSubchannels.Reverse(); - - int[] tmpArray = _resume.BadSubchannels.ToArray(); - - foreach(int bs in tmpArray) { - uint badSector = (uint)bs; + _dumpLog.WriteLine("Aborted!"); - Track track = tracks.OrderBy(t => t.StartSector). - LastOrDefault(t => badSector >= t.StartSector); - - if(_aborted) - { - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?. - Invoke($"Retrying sector {badSector} subchannel, pass {pass}, {(forward ? "forward" : "reverse")}"); - - uint startSector = badSector - 2; - - if(_supportsPlextorD8) - { - sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, startSector, subSize, 5, - supportedPlextorSubchannel, 0, out cmdDuration); - - totalDuration += cmdDuration; - } - else if(readcd) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, startSector, subSize, 5, - track.Type == TrackType.Audio ? MmcSectorTypes.Cdda - : MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, - false, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - - totalDuration += cmdDuration; - } - - if(sense || _dev.Error) - { - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - continue; - } - - Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, cmdBuf, badSector, 5, - subLog, isrcs, (byte)track.Sequence, ref mcn, tracks, - subchannelExtents, _fixSubchannelPosition, outputOptical, - _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, - smallestPregapLbaPerTrack, true); - - if(subchannelExtents.Contains(bs)) - continue; - - UpdateStatus?.Invoke($"Correctly retried sector {badSector} subchannel in pass {pass}."); - _dumpLog.WriteLine("Correctly retried sector {0} subchannel in pass {1}.", badSector, pass); + break; } - if(pass < _retryPasses && - !_aborted && - subchannelExtents.Count > 0) - { - pass++; - forward = !forward; + PulseProgress?. + Invoke($"Retrying sector {badSector} subchannel, pass {pass}, {(forward ? "forward" : "reverse")}"); - goto cdRepeatRetry; + uint startSector = badSector - 2; + + if(_supportsPlextorD8) + { + sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, startSector, subSize, 5, + supportedPlextorSubchannel, 0, out cmdDuration); + + totalDuration += cmdDuration; + } + else if(readcd) + { + sense = _dev.ReadCd(out cmdBuf, out senseBuf, startSector, subSize, 5, + track.Type == TrackType.Audio ? MmcSectorTypes.Cdda + : MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.None, false, + false, MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); + + totalDuration += cmdDuration; } - EndProgress?.Invoke(); + if(sense || _dev.Error) + { + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); + + continue; + } + + Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, desiredSubchannel, cmdBuf, badSector, 5, + subLog, isrcs, (byte)track.Sequence, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, outputOptical, + _fixSubchannel, _fixSubchannelCrc, _dumpLog, UpdateStatus, + smallestPregapLbaPerTrack, true); + + if(subchannelExtents.Contains(bs)) + continue; + + UpdateStatus?.Invoke($"Correctly retried sector {badSector} subchannel in pass {pass}."); + _dumpLog.WriteLine("Correctly retried sector {0} subchannel in pass {1}.", badSector, pass); } + + if(pass < _retryPasses && + !_aborted && + subchannelExtents.Count > 0) + { + pass++; + forward = !forward; + + goto cdRepeatRetry; + } + + EndProgress?.Invoke(); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs b/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs index ad0a5a2aa..15372aa79 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/LeadOuts.cs @@ -45,346 +45,345 @@ using Schemas; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Dumps inter-session lead-outs + /// Size of the read sector in bytes + /// Current read speed + /// Current dump hardware try + /// Extents + /// IMGBurn log + /// Duration of image write + /// Lead-out extents + /// Maximum speed + /// MHDD log + /// Minimum speed + /// Device supports READ(6) + /// Device supports READ(10) + /// Device supports READ(12) + /// Device supports READ(16) + /// Device supports READ CD + /// Drive's maximum supported subchannel + /// Subchannel size in bytes + /// Total commands duration + /// Disc tracks + /// Subchannel log + /// Subchannel desired to save + /// List of disc ISRCs + /// Disc media catalogue number + /// List of subchannels not yet dumped correctly + /// List of smallest pregap relative address per track + void DumpCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents, + IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, + ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, bool read6, bool read10, + bool read12, bool read16, bool readcd, MmcSubchannel supportedSubchannel, uint subSize, + ref double totalDuration, SubchannelLog subLog, MmcSubchannel desiredSubchannel, + Dictionary isrcs, ref string mcn, Track[] tracks, + HashSet subchannelExtents, Dictionary smallestPregapLbaPerTrack) { - /// Dumps inter-session lead-outs - /// Size of the read sector in bytes - /// Current read speed - /// Current dump hardware try - /// Extents - /// IMGBurn log - /// Duration of image write - /// Lead-out extents - /// Maximum speed - /// MHDD log - /// Minimum speed - /// Device supports READ(6) - /// Device supports READ(10) - /// Device supports READ(12) - /// Device supports READ(16) - /// Device supports READ CD - /// Drive's maximum supported subchannel - /// Subchannel size in bytes - /// Total commands duration - /// Disc tracks - /// Subchannel log - /// Subchannel desired to save - /// List of disc ISRCs - /// Disc media catalogue number - /// List of subchannels not yet dumped correctly - /// List of smallest pregap relative address per track - void DumpCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents, - IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, - ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, bool read6, bool read10, - bool read12, bool read16, bool readcd, MmcSubchannel supportedSubchannel, uint subSize, - ref double totalDuration, SubchannelLog subLog, MmcSubchannel desiredSubchannel, - Dictionary isrcs, ref string mcn, Track[] tracks, - HashSet subchannelExtents, Dictionary smallestPregapLbaPerTrack) - { - byte[] cmdBuf = null; // Data buffer - const uint sectorSize = 2352; // Full sector size - bool sense = true; // Sense indicator - byte[] senseBuf = null; - var outputOptical = _outputPlugin as IWritableOpticalImage; + byte[] cmdBuf = null; // Data buffer + const uint sectorSize = 2352; // Full sector size + bool sense = true; // Sense indicator + byte[] senseBuf = null; + var outputOptical = _outputPlugin as IWritableOpticalImage; - UpdateStatus?.Invoke("Reading lead-outs"); - _dumpLog.WriteLine("Reading lead-outs"); + UpdateStatus?.Invoke("Reading lead-outs"); + _dumpLog.WriteLine("Reading lead-outs"); - InitProgress?.Invoke(); + InitProgress?.Invoke(); - foreach((ulong item1, ulong item2) in leadOutExtents.ToArray()) - for(ulong i = item1; i <= item2; i++) + foreach((ulong item1, ulong item2) in leadOutExtents.ToArray()) + for(ulong i = item1; i <= item2; i++) + { + if(_aborted) { - if(_aborted) + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + double cmdDuration = 0; + + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)"); + + if(readcd) + { + sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, + false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); + + totalDuration += cmdDuration; + } + else if(read16) + sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0, 1, false, + _dev.Timeout, out cmdDuration); + else if(read12) + sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, + 0, 1, false, _dev.Timeout, out cmdDuration); + else if(read10) + sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, + 0, 1, _dev.Timeout, out cmdDuration); + else if(read6) + sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout, + out cmdDuration); + + if(!sense && + !_dev.Error) + { + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + extents.Add(i, _maximumReadable, true); + leadOutExtents.Remove(i); + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - _dumpLog.WriteLine("Aborted!"); + byte[] data = new byte[sectorSize * _maximumReadable]; + byte[] sub = new byte[subSize * _maximumReadable]; - break; - } - - double cmdDuration = 0; - - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; - - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)"); - - if(readcd) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, - false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - - totalDuration += cmdDuration; - } - else if(read16) - sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0, 1, false, - _dev.Timeout, out cmdDuration); - else if(read12) - sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, - 0, 1, false, _dev.Timeout, out cmdDuration); - else if(read10) - sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, - 0, 1, _dev.Timeout, out cmdDuration); - else if(read6) - sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout, - out cmdDuration); - - if(!sense && - !_dev.Error) - { - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - extents.Add(i, _maximumReadable, true); - leadOutExtents.Remove(i); - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) + for(int b = 0; b < _maximumReadable; b++) { - byte[] data = new byte[sectorSize * _maximumReadable]; - byte[] sub = new byte[subSize * _maximumReadable]; + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); - for(int b = 0; b < _maximumReadable; b++) - { - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); - - Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); - } - - outputOptical.WriteSectorsLong(data, i, _maximumReadable); - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks, - subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, - _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); - - // Set tracks and go back - if(indexesChanged) - { - (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - i--; - - continue; - } + Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); } - else - outputOptical.WriteSectors(cmdBuf, i, _maximumReadable); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + outputOptical.WriteSectorsLong(data, i, _maximumReadable); + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); + + // Set tracks and go back + if(indexesChanged) + { + (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + i--; + + continue; + } } else + outputOptical.WriteSectors(cmdBuf, i, _maximumReadable); + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + } + else + { + _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); + + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + // Write empty data + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) { - _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); + outputOptical.WriteSectorsLong(new byte[sectorSize * _skip], i, 1); - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly + outputOptical.WriteSectorsTag(new byte[subSize * _skip], i, 1, + SectorTagType.CdSectorSubchannel); + } + else + outputOptical.WriteSectors(new byte[blockSize * _skip], i, 1); - // Write empty data - DateTime writeStart = DateTime.Now; + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - if(supportedSubchannel != MmcSubchannel.None) + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + } + + double newSpeed = (double)blockSize * _maximumReadable / 1048576 / (cmdDuration / 1000); + + if(!double.IsInfinity(newSpeed)) + currentSpeed = newSpeed; + + _resume.NextBlock = i + 1; + } + + EndProgress?.Invoke(); + } + + /// Retries inter-session lead-outs + /// Size of the read sector in bytes + /// Current read speed + /// Current dump hardware try + /// Extents + /// IMGBurn log + /// Duration of image write + /// Lead-out extents + /// Maximum speed + /// MHDD log + /// Minimum speed + /// Device supports READ(6) + /// Device supports READ(10) + /// Device supports READ(12) + /// Device supports READ(16) + /// Device supports READ CD + /// Drive's maximum supported subchannel + /// Subchannel size in bytes + /// Total commands duration + /// Disc tracks + /// Subchannel log + /// Subchannel desired to save + /// List of disc ISRCs + /// Disc media catalogue number + /// List of subchannels not yet dumped correctly + /// List of smallest pregap relative address per track + void RetryCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents, + IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, + ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, bool read6, bool read10, + bool read12, bool read16, bool readcd, MmcSubchannel supportedSubchannel, uint subSize, + ref double totalDuration, SubchannelLog subLog, MmcSubchannel desiredSubchannel, + Dictionary isrcs, ref string mcn, Track[] tracks, + HashSet subchannelExtents, Dictionary smallestPregapLbaPerTrack) + { + byte[] cmdBuf = null; // Data buffer + const uint sectorSize = 2352; // Full sector size + bool sense = true; // Sense indicator + byte[] senseBuf = null; + var outputOptical = _outputPlugin as IWritableOpticalImage; + + _dumpLog.WriteLine("Retrying lead-outs"); + + InitProgress?.Invoke(); + + foreach((ulong item1, ulong item2) in leadOutExtents.ToArray()) + for(ulong i = item1; i <= item2; i++) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + double cmdDuration = 0; + + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)"); + + if(readcd) + { + sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, + false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); + + totalDuration += cmdDuration; + } + else if(read16) + sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0, 1, false, + _dev.Timeout, out cmdDuration); + else if(read12) + sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, + 0, 1, false, _dev.Timeout, out cmdDuration); + else if(read10) + sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, + 0, 1, _dev.Timeout, out cmdDuration); + else if(read6) + sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout, + out cmdDuration); + + if(!sense && + !_dev.Error) + { + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + extents.Add(i, _maximumReadable, true); + leadOutExtents.Remove(i); + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) + { + byte[] data = new byte[sectorSize * _maximumReadable]; + byte[] sub = new byte[subSize * _maximumReadable]; + + for(int b = 0; b < _maximumReadable; b++) { - outputOptical.WriteSectorsLong(new byte[sectorSize * _skip], i, 1); + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); + Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); + } + + outputOptical.WriteSectorsLong(data, i, _maximumReadable); + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks, + subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); + + // Set tracks and go back + if(indexesChanged) + { + (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + i--; + + continue; + } + } + else + outputOptical.WriteSectors(cmdBuf, i, _maximumReadable); + + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + } + else + { + _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); + + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + // Write empty data + DateTime writeStart = DateTime.Now; + + if(supportedSubchannel != MmcSubchannel.None) + { + outputOptical.WriteSectorsLong(new byte[sectorSize * _skip], i, 1); + + if(desiredSubchannel != MmcSubchannel.None) outputOptical.WriteSectorsTag(new byte[subSize * _skip], i, 1, SectorTagType.CdSectorSubchannel); - } - else - outputOptical.WriteSectors(new byte[blockSize * _skip], i, 1); - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - } - - double newSpeed = (double)blockSize * _maximumReadable / 1048576 / (cmdDuration / 1000); - - if(!double.IsInfinity(newSpeed)) - currentSpeed = newSpeed; - - _resume.NextBlock = i + 1; - } - - EndProgress?.Invoke(); - } - - /// Retries inter-session lead-outs - /// Size of the read sector in bytes - /// Current read speed - /// Current dump hardware try - /// Extents - /// IMGBurn log - /// Duration of image write - /// Lead-out extents - /// Maximum speed - /// MHDD log - /// Minimum speed - /// Device supports READ(6) - /// Device supports READ(10) - /// Device supports READ(12) - /// Device supports READ(16) - /// Device supports READ CD - /// Drive's maximum supported subchannel - /// Subchannel size in bytes - /// Total commands duration - /// Disc tracks - /// Subchannel log - /// Subchannel desired to save - /// List of disc ISRCs - /// Disc media catalogue number - /// List of subchannels not yet dumped correctly - /// List of smallest pregap relative address per track - void RetryCdLeadOuts(uint blockSize, ref double currentSpeed, DumpHardwareType currentTry, ExtentsULong extents, - IbgLog ibgLog, ref double imageWriteDuration, ExtentsULong leadOutExtents, - ref double maxSpeed, MhddLog mhddLog, ref double minSpeed, bool read6, bool read10, - bool read12, bool read16, bool readcd, MmcSubchannel supportedSubchannel, uint subSize, - ref double totalDuration, SubchannelLog subLog, MmcSubchannel desiredSubchannel, - Dictionary isrcs, ref string mcn, Track[] tracks, - HashSet subchannelExtents, Dictionary smallestPregapLbaPerTrack) - { - byte[] cmdBuf = null; // Data buffer - const uint sectorSize = 2352; // Full sector size - bool sense = true; // Sense indicator - byte[] senseBuf = null; - var outputOptical = _outputPlugin as IWritableOpticalImage; - - _dumpLog.WriteLine("Retrying lead-outs"); - - InitProgress?.Invoke(); - - foreach((ulong item1, ulong item2) in leadOutExtents.ToArray()) - for(ulong i = item1; i <= item2; i++) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - double cmdDuration = 0; - - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; - - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - PulseProgress?.Invoke($"Reading sector {i} at lead-out ({currentSpeed:F3} MiB/sec.)"); - - if(readcd) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, MmcSectorTypes.AllTypes, - false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - - totalDuration += cmdDuration; - } - else if(read16) - sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, i, blockSize, 0, 1, false, - _dev.Timeout, out cmdDuration); - else if(read12) - sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, - 0, 1, false, _dev.Timeout, out cmdDuration); - else if(read10) - sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, - 0, 1, _dev.Timeout, out cmdDuration); - else if(read6) - sense = _dev.Read6(out cmdBuf, out senseBuf, (uint)i, blockSize, 1, _dev.Timeout, - out cmdDuration); - - if(!sense && - !_dev.Error) - { - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - extents.Add(i, _maximumReadable, true); - leadOutExtents.Remove(i); - DateTime writeStart = DateTime.Now; - - if(supportedSubchannel != MmcSubchannel.None) - { - byte[] data = new byte[sectorSize * _maximumReadable]; - byte[] sub = new byte[subSize * _maximumReadable]; - - for(int b = 0; b < _maximumReadable; b++) - { - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); - - Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); - } - - outputOptical.WriteSectorsLong(data, i, _maximumReadable); - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, i, _maximumReadable, subLog, isrcs, 0xAA, ref mcn, tracks, - subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, - _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); - - // Set tracks and go back - if(indexesChanged) - { - (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - i--; - - continue; - } - } - else - outputOptical.WriteSectors(cmdBuf, i, _maximumReadable); - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; } else - { - _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); + outputOptical.WriteSectors(new byte[blockSize * _skip], i, 1); - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - // Write empty data - DateTime writeStart = DateTime.Now; + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - if(supportedSubchannel != MmcSubchannel.None) - { - outputOptical.WriteSectorsLong(new byte[sectorSize * _skip], i, 1); - - if(desiredSubchannel != MmcSubchannel.None) - outputOptical.WriteSectorsTag(new byte[subSize * _skip], i, 1, - SectorTagType.CdSectorSubchannel); - } - else - outputOptical.WriteSectors(new byte[blockSize * _skip], i, 1); - - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - } - - double newSpeed = (double)blockSize * _maximumReadable / 1048576 / (cmdDuration / 1000); - - if(!double.IsInfinity(newSpeed)) - currentSpeed = newSpeed; + ibgLog.Write(i, 0); } - EndProgress?.Invoke(); - } + double newSpeed = (double)blockSize * _maximumReadable / 1048576 / (cmdDuration / 1000); + + if(!double.IsInfinity(newSpeed)) + currentSpeed = newSpeed; + } + + EndProgress?.Invoke(); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Offset.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Offset.cs index 8229842cc..e80f61e76 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Offset.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Offset.cs @@ -37,86 +37,85 @@ using Aaru.Devices; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Fix offset in audio/scrambled sectors + /// Offset in bytes + /// Sector size in bytes + /// How many extra sectors we got for offset + /// Subchannel type + /// How many sectors did we got + /// Subchannel size in bytes + /// Data buffer + /// Block size in bytes + /// Set if we failed to cross into the Lead-Out + static void FixOffsetData(int offsetBytes, uint sectorSize, int sectorsForOffset, + MmcSubchannel supportedSubchannel, ref uint blocksToRead, uint subSize, + ref byte[] cmdBuf, uint blockSize, bool failedCrossingLeadOut) { - /// Fix offset in audio/scrambled sectors - /// Offset in bytes - /// Sector size in bytes - /// How many extra sectors we got for offset - /// Subchannel type - /// How many sectors did we got - /// Subchannel size in bytes - /// Data buffer - /// Block size in bytes - /// Set if we failed to cross into the Lead-Out - static void FixOffsetData(int offsetBytes, uint sectorSize, int sectorsForOffset, - MmcSubchannel supportedSubchannel, ref uint blocksToRead, uint subSize, - ref byte[] cmdBuf, uint blockSize, bool failedCrossingLeadOut) + if(cmdBuf.Length == 0) + return; + + int offsetFix = offsetBytes < 0 ? (int)((sectorSize * sectorsForOffset) + offsetBytes) : offsetBytes; + + byte[] tmpBuf; + + if(supportedSubchannel != MmcSubchannel.None) { - if(cmdBuf.Length == 0) - return; + // De-interleave subchannel + byte[] data = new byte[sectorSize * blocksToRead]; + byte[] sub = new byte[subSize * blocksToRead]; - int offsetFix = offsetBytes < 0 ? (int)((sectorSize * sectorsForOffset) + offsetBytes) : offsetBytes; - - byte[] tmpBuf; - - if(supportedSubchannel != MmcSubchannel.None) + for(int b = 0; b < blocksToRead; b++) { - // De-interleave subchannel - byte[] data = new byte[sectorSize * blocksToRead]; - byte[] sub = new byte[subSize * blocksToRead]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); - Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); - } - - if(failedCrossingLeadOut) - { - blocksToRead += (uint)sectorsForOffset; - - tmpBuf = new byte[sectorSize * blocksToRead]; - Array.Copy(data, 0, tmpBuf, 0, data.Length); - data = tmpBuf; - tmpBuf = new byte[subSize * blocksToRead]; - Array.Copy(sub, 0, tmpBuf, 0, sub.Length); - sub = tmpBuf; - } - - tmpBuf = new byte[sectorSize * (blocksToRead - sectorsForOffset)]; - Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length); - data = tmpBuf; - - blocksToRead -= (uint)sectorsForOffset; - - // Re-interleave subchannel - cmdBuf = new byte[blockSize * blocksToRead]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize); - Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize); - } + Array.Copy(cmdBuf, (int)(0 + (b * blockSize)), data, sectorSize * b, sectorSize); + Array.Copy(cmdBuf, (int)(sectorSize + (b * blockSize)), sub, subSize * b, subSize); } - else + + if(failedCrossingLeadOut) { - if(failedCrossingLeadOut) - { - blocksToRead += (uint)sectorsForOffset; + blocksToRead += (uint)sectorsForOffset; - tmpBuf = new byte[blockSize * blocksToRead]; - Array.Copy(cmdBuf, 0, tmpBuf, 0, cmdBuf.Length); - cmdBuf = tmpBuf; - } - - tmpBuf = new byte[blockSize * (blocksToRead - sectorsForOffset)]; - Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length); - cmdBuf = tmpBuf; - blocksToRead -= (uint)sectorsForOffset; + tmpBuf = new byte[sectorSize * blocksToRead]; + Array.Copy(data, 0, tmpBuf, 0, data.Length); + data = tmpBuf; + tmpBuf = new byte[subSize * blocksToRead]; + Array.Copy(sub, 0, tmpBuf, 0, sub.Length); + sub = tmpBuf; } + + tmpBuf = new byte[sectorSize * (blocksToRead - sectorsForOffset)]; + Array.Copy(data, offsetFix, tmpBuf, 0, tmpBuf.Length); + data = tmpBuf; + + blocksToRead -= (uint)sectorsForOffset; + + // Re-interleave subchannel + cmdBuf = new byte[blockSize * blocksToRead]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(data, sectorSize * b, cmdBuf, (int)(0 + (b * blockSize)), sectorSize); + Array.Copy(sub, subSize * b, cmdBuf, (int)(sectorSize + (b * blockSize)), subSize); + } + } + else + { + if(failedCrossingLeadOut) + { + blocksToRead += (uint)sectorsForOffset; + + tmpBuf = new byte[blockSize * blocksToRead]; + Array.Copy(cmdBuf, 0, tmpBuf, 0, cmdBuf.Length); + cmdBuf = tmpBuf; + } + + tmpBuf = new byte[blockSize * (blocksToRead - sectorsForOffset)]; + Array.Copy(cmdBuf, offsetFix, tmpBuf, 0, tmpBuf.Length); + cmdBuf = tmpBuf; + blocksToRead -= (uint)sectorsForOffset; } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Plextor.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Plextor.cs index 44a8c1725..5b433deab 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Plextor.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Plextor.cs @@ -33,84 +33,83 @@ using System; using Aaru.Devices; -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Reads a sector using Plextor's D8h READ CDDA command with subchannel + /// Data buffer + /// Sense buffer + /// Fix sector to read + /// Sector size in bytes + /// How many sectors to read + /// Supported subchannel type + /// Time spent sending commands to the drive + /// true if an error occured, false otherwise + bool ReadPlextorWithSubchannel(out byte[] cmdBuf, out byte[] senseBuf, uint firstSectorToRead, uint blockSize, + uint blocksToRead, PlextorSubchannel supportedPlextorSubchannel, + out double cmdDuration) { - /// Reads a sector using Plextor's D8h READ CDDA command with subchannel - /// Data buffer - /// Sense buffer - /// Fix sector to read - /// Sector size in bytes - /// How many sectors to read - /// Supported subchannel type - /// Time spent sending commands to the drive - /// true if an error occured, false otherwise - bool ReadPlextorWithSubchannel(out byte[] cmdBuf, out byte[] senseBuf, uint firstSectorToRead, uint blockSize, - uint blocksToRead, PlextorSubchannel supportedPlextorSubchannel, - out double cmdDuration) + bool sense; + cmdBuf = null; + + if(supportedPlextorSubchannel == PlextorSubchannel.None) { - bool sense; - cmdBuf = null; + sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, + supportedPlextorSubchannel, 0, out cmdDuration); - if(supportedPlextorSubchannel == PlextorSubchannel.None) - { - sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, - supportedPlextorSubchannel, 0, out cmdDuration); - - if(!sense) - return false; - - // As a workaround for some firmware bugs, seek far away. - _dev.PlextorReadCdDa(out _, out senseBuf, firstSectorToRead - 32, blockSize, blocksToRead, - supportedPlextorSubchannel, 0, out _); - - sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, - supportedPlextorSubchannel, _dev.Timeout, out cmdDuration); - - return sense; - } - - byte[] subBuf; - - uint subSize = supportedPlextorSubchannel == PlextorSubchannel.Q16 ? 16u : 96u; - - if(supportedPlextorSubchannel == PlextorSubchannel.Q16 || - supportedPlextorSubchannel == PlextorSubchannel.Pack) - { - sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, 2352 + subSize, blocksToRead, - supportedPlextorSubchannel, _dev.Timeout, out cmdDuration); - - if(!sense) - return false; - } + if(!sense) + return false; // As a workaround for some firmware bugs, seek far away. _dev.PlextorReadCdDa(out _, out senseBuf, firstSectorToRead - 32, blockSize, blocksToRead, supportedPlextorSubchannel, 0, out _); - sense = _dev.PlextorReadCdDa(out byte[] dataBuf, out senseBuf, firstSectorToRead, 2352, blocksToRead, - PlextorSubchannel.None, 0, out cmdDuration); + sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, blockSize, blocksToRead, + supportedPlextorSubchannel, _dev.Timeout, out cmdDuration); - if(sense) - return true; - - sense = _dev.PlextorReadCdDa(out subBuf, out senseBuf, firstSectorToRead, subSize, blocksToRead, - supportedPlextorSubchannel == PlextorSubchannel.Pack ? PlextorSubchannel.All - : supportedPlextorSubchannel, 0, out cmdDuration); - - if(sense) - return true; - - cmdBuf = new byte[(2352 * blocksToRead) + (subSize * blocksToRead)]; - - for(int b = 0; b < blocksToRead; b++) - { - Array.Copy(dataBuf, 2352 * b, cmdBuf, (2352 + subSize) * b, 2352); - Array.Copy(subBuf, subSize * b, cmdBuf, ((2352 + subSize) * b) + 2352, subSize); - } - - return false; + return sense; } + + byte[] subBuf; + + uint subSize = supportedPlextorSubchannel == PlextorSubchannel.Q16 ? 16u : 96u; + + if(supportedPlextorSubchannel == PlextorSubchannel.Q16 || + supportedPlextorSubchannel == PlextorSubchannel.Pack) + { + sense = _dev.PlextorReadCdDa(out cmdBuf, out senseBuf, firstSectorToRead, 2352 + subSize, blocksToRead, + supportedPlextorSubchannel, _dev.Timeout, out cmdDuration); + + if(!sense) + return false; + } + + // As a workaround for some firmware bugs, seek far away. + _dev.PlextorReadCdDa(out _, out senseBuf, firstSectorToRead - 32, blockSize, blocksToRead, + supportedPlextorSubchannel, 0, out _); + + sense = _dev.PlextorReadCdDa(out byte[] dataBuf, out senseBuf, firstSectorToRead, 2352, blocksToRead, + PlextorSubchannel.None, 0, out cmdDuration); + + if(sense) + return true; + + sense = _dev.PlextorReadCdDa(out subBuf, out senseBuf, firstSectorToRead, subSize, blocksToRead, + supportedPlextorSubchannel == PlextorSubchannel.Pack ? PlextorSubchannel.All + : supportedPlextorSubchannel, 0, out cmdDuration); + + if(sense) + return true; + + cmdBuf = new byte[(2352 * blocksToRead) + (subSize * blocksToRead)]; + + for(int b = 0; b < blocksToRead; b++) + { + Array.Copy(dataBuf, 2352 * b, cmdBuf, (2352 + subSize) * b, 2352); + Array.Copy(subBuf, subSize * b, cmdBuf, ((2352 + subSize) * b) + 2352, subSize); + } + + return false; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Pregap.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Pregap.cs index 3d9b59f3e..19f279853 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Pregap.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Pregap.cs @@ -46,214 +46,316 @@ using Aaru.Devices; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + // TODO: Fix offset + /// Reads the first track pregap from a CompactDisc + /// Block size in bytes + /// Current speed + /// List of media tags + /// Subchannel the drive can read + /// Total time spent sending commands to a drive + void ReadCdFirstTrackPregap(uint blockSize, ref double currentSpeed, Dictionary mediaTags, + MmcSubchannel supportedSubchannel, ref double totalDuration) { - // TODO: Fix offset - /// Reads the first track pregap from a CompactDisc - /// Block size in bytes - /// Current speed - /// List of media tags - /// Subchannel the drive can read - /// Total time spent sending commands to a drive - void ReadCdFirstTrackPregap(uint blockSize, ref double currentSpeed, Dictionary mediaTags, - MmcSubchannel supportedSubchannel, ref double totalDuration) + bool sense; // Sense indicator + byte[] cmdBuf; // Data buffer + double cmdDuration; // Command execution time + DateTime timeSpeedStart; // Time of start for speed calculation + ulong sectorSpeedStart = 0; // Used to calculate correct speed + bool gotFirstTrackPregap = false; + int firstTrackPregapSectorsGood = 0; + var firstTrackPregapMs = new MemoryStream(); + + _dumpLog.WriteLine("Reading first track pregap"); + UpdateStatus?.Invoke("Reading first track pregap"); + InitProgress?.Invoke(); + timeSpeedStart = DateTime.UtcNow; + + for(int firstTrackPregapBlock = -150; firstTrackPregapBlock < 0 && _resume.NextBlock == 0; + firstTrackPregapBlock++) { - bool sense; // Sense indicator - byte[] cmdBuf; // Data buffer - double cmdDuration; // Command execution time - DateTime timeSpeedStart; // Time of start for speed calculation - ulong sectorSpeedStart = 0; // Used to calculate correct speed - bool gotFirstTrackPregap = false; - int firstTrackPregapSectorsGood = 0; - var firstTrackPregapMs = new MemoryStream(); - - _dumpLog.WriteLine("Reading first track pregap"); - UpdateStatus?.Invoke("Reading first track pregap"); - InitProgress?.Invoke(); - timeSpeedStart = DateTime.UtcNow; - - for(int firstTrackPregapBlock = -150; firstTrackPregapBlock < 0 && _resume.NextBlock == 0; - firstTrackPregapBlock++) + if(_aborted) { - if(_aborted) - { - _dumpLog.WriteLine("Aborted!"); - UpdateStatus?.Invoke("Aborted!"); - - break; - } - - PulseProgress?. - Invoke($"Trying to read first track pregap sector {firstTrackPregapBlock} ({currentSpeed:F3} MiB/sec.)"); - - // ReSharper disable IntVariableOverflowInUncheckedContext - sense = _dev.ReadCd(out cmdBuf, out _, (uint)firstTrackPregapBlock, blockSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - - // ReSharper restore IntVariableOverflowInUncheckedContext - - if(!sense && - !_dev.Error) - { - firstTrackPregapMs.Write(cmdBuf, 0, (int)blockSize); - gotFirstTrackPregap = true; - firstTrackPregapSectorsGood++; - totalDuration += cmdDuration; - } - else - { - // Write empty data - if(gotFirstTrackPregap) - firstTrackPregapMs.Write(new byte[blockSize], 0, (int)blockSize); - } - - sectorSpeedStart++; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - if(firstTrackPregapSectorsGood > 0) - mediaTags.Add(MediaTagType.CD_FirstTrackPregap, firstTrackPregapMs.ToArray()); - - EndProgress?.Invoke(); - UpdateStatus?.Invoke($"Got {firstTrackPregapSectorsGood} first track pregap sectors."); - _dumpLog.WriteLine("Got {0} first track pregap sectors.", firstTrackPregapSectorsGood); - - firstTrackPregapMs.Close(); - } - - /// Calculate track pregaps - /// Device - /// Dumping log - /// Progress update callback - /// List of tracks - /// Set if drive supports reading PQ subchannel - /// Set if drive supports reading RW subchannel - /// Database entry for device - /// Set if we found the drive does not return the exact subchannel we requested - /// Set if dumping, otherwise media info - public static void SolveTrackPregaps(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, - Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel, - Database.Models.Device dbDev, out bool inexactPositioning, bool dumping) - { - bool sense = true; // Sense indicator - byte[] subBuf = null; - int posQ; - uint retries; - bool? bcd = null; - byte[] crc; - Dictionary pregaps = new Dictionary(); - inexactPositioning = false; - - if(!supportsPqSubchannel && - !supportsRwSubchannel) - return; - - // Check if subchannel is BCD - for(retries = 0; retries < 10; retries++) - { - sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf, false) - : GetSectorForPregapQ16(dev, 11, out subBuf, false); - - if(sense) - continue; - - bcd = (subBuf[9] & 0x10) > 0; + _dumpLog.WriteLine("Aborted!"); + UpdateStatus?.Invoke("Aborted!"); break; } - AaruConsole.DebugWriteLine("Pregap calculator", bcd == true - ? "Subchannel is BCD" - : bcd == false - ? "Subchannel is not BCD" - : "Could not detect drive subchannel BCD"); + PulseProgress?. + Invoke($"Trying to read first track pregap sector {firstTrackPregapBlock} ({currentSpeed:F3} MiB/sec.)"); - if(bcd is null) + // ReSharper disable IntVariableOverflowInUncheckedContext + sense = _dev.ReadCd(out cmdBuf, out _, (uint)firstTrackPregapBlock, blockSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); + + // ReSharper restore IntVariableOverflowInUncheckedContext + + if(!sense && + !_dev.Error) { - dumpLog?.WriteLine("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); - - updateStatus?. - Invoke("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); - - return; + firstTrackPregapMs.Write(cmdBuf, 0, (int)blockSize); + gotFirstTrackPregap = true; + firstTrackPregapSectorsGood++; + totalDuration += cmdDuration; + } + else + { + // Write empty data + if(gotFirstTrackPregap) + firstTrackPregapMs.Write(new byte[blockSize], 0, (int)blockSize); } - // Initialize the dictionary - foreach(Track t in tracks) - pregaps[t.Sequence] = 0; + sectorSpeedStart++; - for(int t = 0; t < tracks.Length; t++) + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + + if(firstTrackPregapSectorsGood > 0) + mediaTags.Add(MediaTagType.CD_FirstTrackPregap, firstTrackPregapMs.ToArray()); + + EndProgress?.Invoke(); + UpdateStatus?.Invoke($"Got {firstTrackPregapSectorsGood} first track pregap sectors."); + _dumpLog.WriteLine("Got {0} first track pregap sectors.", firstTrackPregapSectorsGood); + + firstTrackPregapMs.Close(); + } + + /// Calculate track pregaps + /// Device + /// Dumping log + /// Progress update callback + /// List of tracks + /// Set if drive supports reading PQ subchannel + /// Set if drive supports reading RW subchannel + /// Database entry for device + /// Set if we found the drive does not return the exact subchannel we requested + /// Set if dumping, otherwise media info + public static void SolveTrackPregaps(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, + Track[] tracks, bool supportsPqSubchannel, bool supportsRwSubchannel, + Database.Models.Device dbDev, out bool inexactPositioning, bool dumping) + { + bool sense = true; // Sense indicator + byte[] subBuf = null; + int posQ; + uint retries; + bool? bcd = null; + byte[] crc; + Dictionary pregaps = new Dictionary(); + inexactPositioning = false; + + if(!supportsPqSubchannel && + !supportsRwSubchannel) + return; + + // Check if subchannel is BCD + for(retries = 0; retries < 10; retries++) + { + sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, 11, dbDev, out subBuf, false) + : GetSectorForPregapQ16(dev, 11, out subBuf, false); + + if(sense) + continue; + + bcd = (subBuf[9] & 0x10) > 0; + + break; + } + + AaruConsole.DebugWriteLine("Pregap calculator", bcd == true + ? "Subchannel is BCD" + : bcd == false + ? "Subchannel is not BCD" + : "Could not detect drive subchannel BCD"); + + if(bcd is null) + { + dumpLog?.WriteLine("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); + + updateStatus?. + Invoke("Could not detect if drive subchannel is BCD or not, pregaps could not be calculated, dump may be incorrect..."); + + return; + } + + // Initialize the dictionary + foreach(Track t in tracks) + pregaps[t.Sequence] = 0; + + for(int t = 0; t < tracks.Length; t++) + { + Track track = tracks[t]; + int trackRetries = 0; + + // First track of each session has at least 150 sectors of pregap and is not always readable + if(tracks.Where(trk => trk.Session == track.Session).OrderBy(trk => trk.Sequence). + FirstOrDefault().Sequence == track.Sequence) { - Track track = tracks[t]; - int trackRetries = 0; + AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.Sequence); - // First track of each session has at least 150 sectors of pregap and is not always readable - if(tracks.Where(trk => trk.Session == track.Session).OrderBy(trk => trk.Sequence). - FirstOrDefault().Sequence == track.Sequence) + if(track.Sequence > 1) + pregaps[track.Sequence] = 150; + + continue; + } + + if(t > 0 && + tracks[t - 1].Type == tracks[t].Type && + dumping) + { + AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.Sequence); + + continue; + } + + if(dumping && dev.Manufacturer.ToLowerInvariant().StartsWith("plextor", StringComparison.Ordinal)) + { + AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0} due to Plextor firmware bug", + track.Sequence); + + continue; + } + + AaruConsole.DebugWriteLine("Pregap calculator", "Track {0}", track.Sequence); + + int lba = (int)track.StartSector - 1; + bool pregapFound = false; + Track previousTrack = tracks.FirstOrDefault(trk => trk.Sequence == track.Sequence - 1); + + bool goneBack = false; + bool goFront = false; + bool forward = false; + bool crcOk = false; + bool previousPregapIsPreviousTrack = false; + + // Check if pregap is 0 + for(retries = 0; retries < 10 && !pregapFound; retries++) + { + sense = supportsRwSubchannel + ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf, + track.Type == TrackType.Audio) + : GetSectorForPregapQ16(dev, (uint)lba, out subBuf, track.Type == TrackType.Audio); + + if(sense) { - AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.Sequence); - - if(track.Sequence > 1) - pregaps[track.Sequence] = 150; + AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}", lba, + retries + 1, sense); continue; } - if(t > 0 && - tracks[t - 1].Type == tracks[t].Type && - dumping) - { - AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0}", track.Sequence); + if(bcd == false) + BinaryToBcdQ(subBuf); - continue; + CRC16CCITTContext.Data(subBuf, 10, out crc); + + AaruConsole.DebugWriteLine("Pregap calculator", + "LBA: {0}, Try {1}, Sense {2}, Q: {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", + lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], + subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], + subBuf[10], subBuf[11], crc[0], crc[1]); + + crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; + + // Try to do a simple correction + if(!crcOk) + { + // Data track cannot have 11xxb in CONTROL + if((subBuf[0] & 0x40) > 0) + subBuf[0] &= 0x7F; + + // ADR only uses two bits + subBuf[0] &= 0xF3; + + // Don't care about other Q modes + if((subBuf[0] & 0xF) == 1) + { + // ZERO only used in DDCD + subBuf[6] = 0; + + // Fix BCD numbering + for(int i = 1; i < 10; i++) + { + if((subBuf[i] & 0xF0) > 0xA0) + subBuf[i] &= 0x7F; + + if((subBuf[i] & 0x0F) > 0x0A) + subBuf[i] &= 0xF7; + } + } + + CRC16CCITTContext.Data(subBuf, 10, out crc); + + crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; + + if(crcOk) + { + AaruConsole.DebugWriteLine("Pregap calculator", + "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", + lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], + subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], + subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); + } + else + continue; } - if(dumping && dev.Manufacturer.ToLowerInvariant().StartsWith("plextor", StringComparison.Ordinal)) - { - AaruConsole.DebugWriteLine("Pregap calculator", "Skipping track {0} due to Plextor firmware bug", - track.Sequence); + BcdToBinaryQ(subBuf); + // Q position + if((subBuf[0] & 0xF) != 1) continue; - } - AaruConsole.DebugWriteLine("Pregap calculator", "Track {0}", track.Sequence); + posQ = (subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9] - 150; - int lba = (int)track.StartSector - 1; - bool pregapFound = false; - Track previousTrack = tracks.FirstOrDefault(trk => trk.Sequence == track.Sequence - 1); + if(subBuf[1] != track.Sequence - 1 || + subBuf[2] == 0 || + posQ != lba) + break; - bool goneBack = false; - bool goFront = false; - bool forward = false; - bool crcOk = false; - bool previousPregapIsPreviousTrack = false; + pregaps[track.Sequence] = 0; - // Check if pregap is 0 - for(retries = 0; retries < 10 && !pregapFound; retries++) + pregapFound = true; + } + + if(pregapFound) + continue; + + // Calculate pregap + lba = (int)track.StartSector - 150; + + while(lba > (int)previousTrack.StartSector && + lba <= (int)track.StartSector) + { + // Some drives crash if you try to read just before the previous read, so seek away first + if(!forward) + sense = supportsRwSubchannel + ? GetSectorForPregapRaw(dev, (uint)lba - 10, dbDev, out subBuf, + track.Type == TrackType.Audio) + : GetSectorForPregapQ16(dev, (uint)lba - 10, out subBuf, + track.Type == TrackType.Audio); + + for(retries = 0; retries < 10; retries++) { sense = supportsRwSubchannel ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf, track.Type == TrackType.Audio) - : GetSectorForPregapQ16(dev, (uint)lba, out subBuf, track.Type == TrackType.Audio); + : GetSectorForPregapQ16(dev, (uint)lba, out subBuf, + track.Type == TrackType.Audio); if(sense) - { - AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}", lba, - retries + 1, sense); - continue; - } if(bcd == false) BinaryToBcdQ(subBuf); @@ -304,510 +406,407 @@ namespace Aaru.Core.Devices.Dumping AaruConsole.DebugWriteLine("Pregap calculator", "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], - subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], - subBuf[9], subBuf[10], subBuf[11], crc[0], crc[1]); + subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], + subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], + crc[1]); + + break; } - else - continue; } - BcdToBinaryQ(subBuf); - - // Q position - if((subBuf[0] & 0xF) != 1) - continue; - - posQ = (subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9] - 150; - - if(subBuf[1] != track.Sequence - 1 || - subBuf[2] == 0 || - posQ != lba) + if(crcOk) break; - - pregaps[track.Sequence] = 0; - - pregapFound = true; } - if(pregapFound) - continue; - - // Calculate pregap - lba = (int)track.StartSector - 150; - - while(lba > (int)previousTrack.StartSector && - lba <= (int)track.StartSector) + if(retries == 10) { - // Some drives crash if you try to read just before the previous read, so seek away first - if(!forward) - sense = supportsRwSubchannel - ? GetSectorForPregapRaw(dev, (uint)lba - 10, dbDev, out subBuf, - track.Type == TrackType.Audio) - : GetSectorForPregapQ16(dev, (uint)lba - 10, out subBuf, - track.Type == TrackType.Audio); - - for(retries = 0; retries < 10; retries++) + if(sense) { - sense = supportsRwSubchannel - ? GetSectorForPregapRaw(dev, (uint)lba, dbDev, out subBuf, - track.Type == TrackType.Audio) - : GetSectorForPregapQ16(dev, (uint)lba, out subBuf, - track.Type == TrackType.Audio); + trackRetries++; - if(sense) - continue; - - if(bcd == false) - BinaryToBcdQ(subBuf); - - CRC16CCITTContext.Data(subBuf, 10, out crc); - - AaruConsole.DebugWriteLine("Pregap calculator", - "LBA: {0}, Try {1}, Sense {2}, Q: {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", - lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], subBuf[3], - subBuf[4], subBuf[5], subBuf[6], subBuf[7], subBuf[8], subBuf[9], - subBuf[10], subBuf[11], crc[0], crc[1]); - - crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; - - // Try to do a simple correction - if(!crcOk) + if(trackRetries >= 10) { - // Data track cannot have 11xxb in CONTROL - if((subBuf[0] & 0x40) > 0) - subBuf[0] &= 0x7F; - - // ADR only uses two bits - subBuf[0] &= 0xF3; - - // Don't care about other Q modes - if((subBuf[0] & 0xF) == 1) + if(pregaps[track.Sequence] == 0) { - // ZERO only used in DDCD - subBuf[6] = 0; - - // Fix BCD numbering - for(int i = 1; i < 10; i++) + if((previousTrack.Type == TrackType.Audio && + track.Type != TrackType.Audio) || + (previousTrack.Type != TrackType.Audio && + track.Type == TrackType.Audio)) { - if((subBuf[i] & 0xF0) > 0xA0) - subBuf[i] &= 0x7F; + dumpLog?. + WriteLine("Could not read subchannel for this track, supposing 150 sectors."); - if((subBuf[i] & 0x0F) > 0x0A) - subBuf[i] &= 0xF7; - } - } - - CRC16CCITTContext.Data(subBuf, 10, out crc); - - crcOk = crc[0] == subBuf[10] && crc[1] == subBuf[11]; - - if(crcOk) - { - AaruConsole.DebugWriteLine("Pregap calculator", - "LBA: {0}, Try {1}, Sense {2}, Q (FIXED): {3:X2} {4:X2} {5:X2} {6:X2} {7:X2} {8:X2} {9:X2} {10:X2} {11:X2} {12:X2} CRC 0x{13:X2}{14:X2}, Calculated CRC: 0x{15:X2}{16:X2}", - lba, retries + 1, sense, subBuf[0], subBuf[1], subBuf[2], - subBuf[3], subBuf[4], subBuf[5], subBuf[6], subBuf[7], - subBuf[8], subBuf[9], subBuf[10], subBuf[11], crc[0], - crc[1]); - - break; - } - } - - if(crcOk) - break; - } - - if(retries == 10) - { - if(sense) - { - trackRetries++; - - if(trackRetries >= 10) - { - if(pregaps[track.Sequence] == 0) - { - if((previousTrack.Type == TrackType.Audio && - track.Type != TrackType.Audio) || - (previousTrack.Type != TrackType.Audio && - track.Type == TrackType.Audio)) - { - dumpLog?. - WriteLine("Could not read subchannel for this track, supposing 150 sectors."); - - updateStatus?. - Invoke("Could not read subchannel for this track, supposing 150 sectors."); - } - else - { - dumpLog?. - WriteLine("Could not read subchannel for this track, supposing 0 sectors."); - - updateStatus?. - Invoke("Could not read subchannel for this track, supposing 0 sectors."); - } + updateStatus?. + Invoke("Could not read subchannel for this track, supposing 150 sectors."); } else { dumpLog?. - WriteLine($"Could not read subchannel for this track, supposing {pregaps[track.Sequence]} sectors."); + WriteLine("Could not read subchannel for this track, supposing 0 sectors."); updateStatus?. - Invoke($"Could not read subchannel for this track, supposing {pregaps[track.Sequence]} sectors."); + Invoke("Could not read subchannel for this track, supposing 0 sectors."); } + } + else + { + dumpLog?. + WriteLine($"Could not read subchannel for this track, supposing {pregaps[track.Sequence]} sectors."); - break; + updateStatus?. + Invoke($"Could not read subchannel for this track, supposing {pregaps[track.Sequence]} sectors."); } - dumpLog?.WriteLine($"Could not read subchannel for sector {lba}"); - updateStatus?.Invoke($"Could not read subchannel for sector {lba}"); - - lba++; - forward = true; - - continue; + break; } - dumpLog?.WriteLine($"Could not get correct subchannel for sector {lba}"); - updateStatus?.Invoke($"Could not get correct subchannel for sector {lba}"); - } + dumpLog?.WriteLine($"Could not read subchannel for sector {lba}"); + updateStatus?.Invoke($"Could not read subchannel for sector {lba}"); - if(subBuf.All(b => b == 0)) - { - inexactPositioning = true; - - AaruConsole.DebugWriteLine("Pregap calculator", "All Q empty for LBA {0}", lba); - - break; - } - - BcdToBinaryQ(subBuf); - - // If it's not Q position - if((subBuf[0] & 0xF) != 1) - { - // This means we already searched back, so search forward - if(goFront) - { - lba++; - forward = true; - - if(lba == (int)previousTrack.StartSector) - break; - - continue; - } - - // Search back - goneBack = true; - lba--; - forward = false; + lba++; + forward = true; continue; } - // Previous track - if(subBuf[1] < track.Sequence) + dumpLog?.WriteLine($"Could not get correct subchannel for sector {lba}"); + updateStatus?.Invoke($"Could not get correct subchannel for sector {lba}"); + } + + if(subBuf.All(b => b == 0)) + { + inexactPositioning = true; + + AaruConsole.DebugWriteLine("Pregap calculator", "All Q empty for LBA {0}", lba); + + break; + } + + BcdToBinaryQ(subBuf); + + // If it's not Q position + if((subBuf[0] & 0xF) != 1) + { + // This means we already searched back, so search forward + if(goFront) { lba++; - forward = true; - previousPregapIsPreviousTrack = true; + forward = true; - // Already gone back, so go forward - if(goneBack) - goFront = true; - - continue; - } - - // Same track, but not pregap - if(subBuf[1] == track.Sequence && - subBuf[2] > 0) - { - lba--; - forward = false; - - if(previousPregapIsPreviousTrack) + if(lba == (int)previousTrack.StartSector) break; continue; } - previousPregapIsPreviousTrack = false; + // Search back + goneBack = true; + lba--; + forward = false; - // Pregap according to Q position - posQ = (subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9] - 150; - int diff = posQ - lba; - int pregapQ = (int)track.StartSector - lba; + continue; + } - if(diff != 0) - { - AaruConsole.DebugWriteLine("Pregap calculator", "Invalid Q position for LBA {0}, got {1}", lba, - posQ); + // Previous track + if(subBuf[1] < track.Sequence) + { + lba++; + forward = true; + previousPregapIsPreviousTrack = true; - inexactPositioning = true; - } + // Already gone back, so go forward + if(goneBack) + goFront = true; - // Received a Q post the LBA we wanted, just go back. If we are already going forward, break - if(posQ > lba) - { - if(forward) - break; + continue; + } - lba--; + // Same track, but not pregap + if(subBuf[1] == track.Sequence && + subBuf[2] > 0) + { + lba--; + forward = false; - continue; - } + if(previousPregapIsPreviousTrack) + break; - // Bigger than known change, otherwise we found it - if(pregapQ > pregaps[track.Sequence]) - { - // If CRC is not OK, only accept pregaps less than 10 sectors longer than previously now - if(crcOk || pregapQ - pregaps[track.Sequence] < 10) - { - AaruConsole.DebugWriteLine("Pregap calculator", "Pregap for track {0}: {1}", - track.Sequence, pregapQ); + continue; + } - pregaps[track.Sequence] = pregapQ; - } + previousPregapIsPreviousTrack = false; - // We are going forward, so we have already been in the previous track, so add 1 to pregap and get out of here - else if(forward) - { - pregaps[track.Sequence]++; + // Pregap according to Q position + posQ = (subBuf[7] * 60 * 75) + (subBuf[8] * 75) + subBuf[9] - 150; + int diff = posQ - lba; + int pregapQ = (int)track.StartSector - lba; - break; - } - } - else if(pregapQ == pregaps[track.Sequence]) + if(diff != 0) + { + AaruConsole.DebugWriteLine("Pregap calculator", "Invalid Q position for LBA {0}, got {1}", lba, + posQ); + + inexactPositioning = true; + } + + // Received a Q post the LBA we wanted, just go back. If we are already going forward, break + if(posQ > lba) + { + if(forward) break; lba--; - forward = false; - } - } - foreach(Track trk in tracks) - { - trk.Pregap = (ulong)pregaps[trk.Sequence]; - - // Do not reduce pregap, or starting position of session's first track - if(tracks.Where(t => t.Session == trk.Session).OrderBy(t => t.Sequence).FirstOrDefault(). - Sequence == trk.Sequence) continue; + } - if(dumping) + // Bigger than known change, otherwise we found it + if(pregapQ > pregaps[track.Sequence]) { - // Minus five, to ensure dumping will fix if there is a pregap LBA 0 - int red = 5; - - while(trk.Pregap > 0 && - red > 0) + // If CRC is not OK, only accept pregaps less than 10 sectors longer than previously now + if(crcOk || pregapQ - pregaps[track.Sequence] < 10) { - trk.Pregap--; - red--; + AaruConsole.DebugWriteLine("Pregap calculator", "Pregap for track {0}: {1}", + track.Sequence, pregapQ); + + pregaps[track.Sequence] = pregapQ; + } + + // We are going forward, so we have already been in the previous track, so add 1 to pregap and get out of here + else if(forward) + { + pregaps[track.Sequence]++; + + break; } } + else if(pregapQ == pregaps[track.Sequence]) + break; - trk.StartSector -= trk.Pregap; - - #if DEBUG - dumpLog?.WriteLine($"Track {trk.Sequence} pregap is {trk.Pregap} sectors"); - updateStatus?.Invoke($"Track {trk.Sequence} pregap is {trk.Pregap} sectors"); - #endif + lba--; + forward = false; } } - /// Reads a RAW subchannel sector for pregap calculation - /// Device - /// LBA - /// Database entry for device - /// Read subchannel - /// Set if it is an audio track - /// true if read correctly, false otherwise - static bool GetSectorForPregapRaw(Device dev, uint lba, Database.Models.Device dbDev, out byte[] subBuf, - bool audioTrack) + foreach(Track trk in tracks) { - byte[] cmdBuf; - bool sense; - subBuf = null; + trk.Pregap = (ulong)pregaps[trk.Sequence]; - if(audioTrack) + // Do not reduce pregap, or starting position of session's first track + if(tracks.Where(t => t.Session == trk.Session).OrderBy(t => t.Sequence).FirstOrDefault(). + Sequence == trk.Sequence) + continue; + + if(dumping) { - sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, - out _); + // Minus five, to ensure dumping will fix if there is a pregap LBA 0 + int red = 5; - if(sense) - sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw, - dev.Timeout, out _); + while(trk.Pregap > 0 && + red > 0) + { + trk.Pregap--; + red--; + } } - else - { + + trk.StartSector -= trk.Pregap; + + #if DEBUG + dumpLog?.WriteLine($"Track {trk.Sequence} pregap is {trk.Pregap} sectors"); + updateStatus?.Invoke($"Track {trk.Sequence} pregap is {trk.Pregap} sectors"); + #endif + } + } + + /// Reads a RAW subchannel sector for pregap calculation + /// Device + /// LBA + /// Database entry for device + /// Read subchannel + /// Set if it is an audio track + /// true if read correctly, false otherwise + static bool GetSectorForPregapRaw(Device dev, uint lba, Database.Models.Device dbDev, out byte[] subBuf, + bool audioTrack) + { + byte[] cmdBuf; + bool sense; + subBuf = null; + + if(audioTrack) + { + sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, + out _); + + if(sense) sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, out _); + } + else + { + sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw, + dev.Timeout, out _); - if(sense) - sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Raw, - dev.Timeout, out _); - } + if(sense) + sense = dev.ReadCd(out cmdBuf, out _, lba, 2448, 1, MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Raw, + dev.Timeout, out _); + } - if(!sense) - { - byte[] tmpBuf = new byte[96]; - Array.Copy(cmdBuf, 2352, tmpBuf, 0, 96); - subBuf = DeinterleaveQ(tmpBuf); - } - else - { - sense = dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.AllTypes, false, false, false, + if(!sense) + { + byte[] tmpBuf = new byte[96]; + Array.Copy(cmdBuf, 2352, tmpBuf, 0, 96); + subBuf = DeinterleaveQ(tmpBuf); + } + else + { + sense = dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Raw, + dev.Timeout, out _); + + if(sense) + sense = dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Raw, dev.Timeout, out _); - if(sense) - sense = dev.ReadCd(out cmdBuf, out _, lba, 96, 1, MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Raw, - dev.Timeout, out _); - - if(!sense) - { - subBuf = DeinterleaveQ(cmdBuf); - } - else if(dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || - dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || - dev.Manufacturer.ToLowerInvariant() == "plextor") - sense = dev.PlextorReadCdDa(out cmdBuf, out _, lba, 96, 1, PlextorSubchannel.All, dev.Timeout, - out _); - - { - if(!sense) - subBuf = DeinterleaveQ(cmdBuf); - } + if(!sense) + { + subBuf = DeinterleaveQ(cmdBuf); } + else if(dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || + dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || + dev.Manufacturer.ToLowerInvariant() == "plextor") + sense = dev.PlextorReadCdDa(out cmdBuf, out _, lba, 96, 1, PlextorSubchannel.All, dev.Timeout, + out _); - return sense; + { + if(!sense) + subBuf = DeinterleaveQ(cmdBuf); + } } - /// Reads a Q16 subchannel sector for pregap calculation - /// Device - /// LBA - /// Read subchannel - /// Set if it is an audio track - /// true if read correctly, false otherwise - static bool GetSectorForPregapQ16(Device dev, uint lba, out byte[] subBuf, bool audioTrack) + return sense; + } + + /// Reads a Q16 subchannel sector for pregap calculation + /// Device + /// LBA + /// Read subchannel + /// Set if it is an audio track + /// true if read correctly, false otherwise + static bool GetSectorForPregapQ16(Device dev, uint lba, out byte[] subBuf, bool audioTrack) + { + byte[] cmdBuf; + bool sense; + subBuf = null; + + if(audioTrack) { - byte[] cmdBuf; - bool sense; - subBuf = null; + sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, + out _); - if(audioTrack) - { - sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, - out _); - - if(sense) - sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, - dev.Timeout, out _); - } - else - { + if(sense) sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); + } + else + { + sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, + dev.Timeout, out _); - if(sense) - sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Q16, - dev.Timeout, out _); - } + if(sense) + sense = dev.ReadCd(out cmdBuf, out _, lba, 2368, 1, MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.Q16, + dev.Timeout, out _); + } - if(!sense) - { - subBuf = new byte[16]; - Array.Copy(cmdBuf, 2352, subBuf, 0, 16); - } - else - { - sense = dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.AllTypes, false, false, false, + if(!sense) + { + subBuf = new byte[16]; + Array.Copy(cmdBuf, 2352, subBuf, 0, 16); + } + else + { + sense = dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16, + dev.Timeout, out _); + + if(sense) + sense = dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16, dev.Timeout, out _); - if(sense) - sense = dev.ReadCd(out cmdBuf, out _, lba, 16, 1, MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, false, false, MmcErrorField.None, MmcSubchannel.Q16, - dev.Timeout, out _); - - if(!sense) - subBuf = cmdBuf; - } - - return sense; + if(!sense) + subBuf = cmdBuf; } - /// De-interleaves Q subchannel - /// Interleaved subchannel - /// De-interleaved Q subchannel - static byte[] DeinterleaveQ(byte[] subchannel) + return sense; + } + + /// De-interleaves Q subchannel + /// Interleaved subchannel + /// De-interleaved Q subchannel + static byte[] DeinterleaveQ(byte[] subchannel) + { + int[] q = new int[subchannel.Length / 8]; + + // De-interlace Q subchannel + for(int iq = 0; iq < subchannel.Length; iq += 8) { - int[] q = new int[subchannel.Length / 8]; - - // De-interlace Q subchannel - for(int iq = 0; iq < subchannel.Length; iq += 8) - { - q[iq / 8] = (subchannel[iq] & 0x40) << 1; - q[iq / 8] += subchannel[iq + 1] & 0x40; - q[iq / 8] += (subchannel[iq + 2] & 0x40) >> 1; - q[iq / 8] += (subchannel[iq + 3] & 0x40) >> 2; - q[iq / 8] += (subchannel[iq + 4] & 0x40) >> 3; - q[iq / 8] += (subchannel[iq + 5] & 0x40) >> 4; - q[iq / 8] += (subchannel[iq + 6] & 0x40) >> 5; - q[iq / 8] += (subchannel[iq + 7] & 0x40) >> 6; - } - - byte[] deQ = new byte[q.Length]; - - for(int iq = 0; iq < q.Length; iq++) - { - deQ[iq] = (byte)q[iq]; - } - - return deQ; + q[iq / 8] = (subchannel[iq] & 0x40) << 1; + q[iq / 8] += subchannel[iq + 1] & 0x40; + q[iq / 8] += (subchannel[iq + 2] & 0x40) >> 1; + q[iq / 8] += (subchannel[iq + 3] & 0x40) >> 2; + q[iq / 8] += (subchannel[iq + 4] & 0x40) >> 3; + q[iq / 8] += (subchannel[iq + 5] & 0x40) >> 4; + q[iq / 8] += (subchannel[iq + 6] & 0x40) >> 5; + q[iq / 8] += (subchannel[iq + 7] & 0x40) >> 6; } - /// In place converts Q subchannel from binary to BCD numbering - /// Q subchannel - static void BinaryToBcdQ(byte[] q) + byte[] deQ = new byte[q.Length]; + + for(int iq = 0; iq < q.Length; iq++) { - q[1] = (byte)(((q[1] / 10) << 4) + (q[1] % 10)); - q[2] = (byte)(((q[2] / 10) << 4) + (q[2] % 10)); - q[3] = (byte)(((q[3] / 10) << 4) + (q[3] % 10)); - q[4] = (byte)(((q[4] / 10) << 4) + (q[4] % 10)); - q[5] = (byte)(((q[5] / 10) << 4) + (q[5] % 10)); - q[6] = (byte)(((q[6] / 10) << 4) + (q[6] % 10)); - q[7] = (byte)(((q[7] / 10) << 4) + (q[7] % 10)); - q[8] = (byte)(((q[8] / 10) << 4) + (q[8] % 10)); - q[9] = (byte)(((q[9] / 10) << 4) + (q[9] % 10)); + deQ[iq] = (byte)q[iq]; } - /// In place converts Q subchannel from BCD to binary numbering - /// Q subchannel - static void BcdToBinaryQ(byte[] q) - { - q[1] = (byte)((q[1] / 16 * 10) + (q[1] & 0x0F)); - q[2] = (byte)((q[2] / 16 * 10) + (q[2] & 0x0F)); - q[3] = (byte)((q[3] / 16 * 10) + (q[3] & 0x0F)); - q[4] = (byte)((q[4] / 16 * 10) + (q[4] & 0x0F)); - q[5] = (byte)((q[5] / 16 * 10) + (q[5] & 0x0F)); - q[6] = (byte)((q[6] / 16 * 10) + (q[6] & 0x0F)); - q[7] = (byte)((q[7] / 16 * 10) + (q[7] & 0x0F)); - q[8] = (byte)((q[8] / 16 * 10) + (q[8] & 0x0F)); - q[9] = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); - } + return deQ; + } + + /// In place converts Q subchannel from binary to BCD numbering + /// Q subchannel + static void BinaryToBcdQ(byte[] q) + { + q[1] = (byte)(((q[1] / 10) << 4) + (q[1] % 10)); + q[2] = (byte)(((q[2] / 10) << 4) + (q[2] % 10)); + q[3] = (byte)(((q[3] / 10) << 4) + (q[3] % 10)); + q[4] = (byte)(((q[4] / 10) << 4) + (q[4] % 10)); + q[5] = (byte)(((q[5] / 10) << 4) + (q[5] % 10)); + q[6] = (byte)(((q[6] / 10) << 4) + (q[6] % 10)); + q[7] = (byte)(((q[7] / 10) << 4) + (q[7] % 10)); + q[8] = (byte)(((q[8] / 10) << 4) + (q[8] % 10)); + q[9] = (byte)(((q[9] / 10) << 4) + (q[9] % 10)); + } + + /// In place converts Q subchannel from BCD to binary numbering + /// Q subchannel + static void BcdToBinaryQ(byte[] q) + { + q[1] = (byte)((q[1] / 16 * 10) + (q[1] & 0x0F)); + q[2] = (byte)((q[2] / 16 * 10) + (q[2] & 0x0F)); + q[3] = (byte)((q[3] / 16 * 10) + (q[3] & 0x0F)); + q[4] = (byte)((q[4] / 16 * 10) + (q[4] & 0x0F)); + q[5] = (byte)((q[5] / 16 * 10) + (q[5] & 0x0F)); + q[6] = (byte)((q[6] / 16 * 10) + (q[6] & 0x0F)); + q[7] = (byte)((q[7] / 16 * 10) + (q[7] & 0x0F)); + q[8] = (byte)((q[8] / 16 * 10) + (q[8] & 0x0F)); + q[9] = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs index df0194427..f15313215 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Subchannel.cs @@ -38,40 +38,39 @@ using Aaru.Devices; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Check if the drive can read RW raw subchannel + /// Device + /// Dumping log + /// Progress update callback + /// LBA to try + /// true if read correctly, false otherwise + public static bool SupportsRwSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, uint lba) { - /// Check if the drive can read RW raw subchannel - /// Device - /// Dumping log - /// Progress update callback - /// LBA to try - /// true if read correctly, false otherwise - public static bool SupportsRwSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, uint lba) - { - dumpLog?.WriteLine("Checking if drive supports full raw subchannel reading..."); - updateStatus?.Invoke("Checking if drive supports full raw subchannel reading..."); + dumpLog?.WriteLine("Checking if drive supports full raw subchannel reading..."); + updateStatus?.Invoke("Checking if drive supports full raw subchannel reading..."); - return !dev.ReadCd(out _, out _, lba, 2352 + 96, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw, - dev.Timeout, out _); - } + return !dev.ReadCd(out _, out _, lba, 2352 + 96, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Raw, + dev.Timeout, out _); + } - /// Check if the drive can read RW raw subchannel - /// Device - /// Dumping log - /// Progress update callback - /// LBA to try - /// true if read correctly, false otherwise - public static bool SupportsPqSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, uint lba) - { - dumpLog?.WriteLine("Checking if drive supports PQ subchannel reading..."); - updateStatus?.Invoke("Checking if drive supports PQ subchannel reading..."); + /// Check if the drive can read RW raw subchannel + /// Device + /// Dumping log + /// Progress update callback + /// LBA to try + /// true if read correctly, false otherwise + public static bool SupportsPqSubchannel(Device dev, DumpLog dumpLog, UpdateStatusHandler updateStatus, uint lba) + { + dumpLog?.WriteLine("Checking if drive supports PQ subchannel reading..."); + updateStatus?.Invoke("Checking if drive supports PQ subchannel reading..."); - return !dev.ReadCd(out _, out _, lba, 2352 + 16, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, - dev.Timeout, out _); - } + return !dev.ReadCd(out _, out _, lba, 2352 + 16, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.Q16, + dev.Timeout, out _); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Tags.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Tags.cs index 271958df6..2545878f9 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Tags.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Tags.cs @@ -42,106 +42,105 @@ using Aaru.Devices; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Reads media tags from Compact Disc media + /// Media type + /// Media tags dictionary + /// Sessions + /// First track in last session + void ReadCdTags(ref MediaType mediaType, Dictionary mediaTags, out int sessions, + out int firstTrackLastSession) { - /// Reads media tags from Compact Disc media - /// Media type - /// Media tags dictionary - /// Sessions - /// First track in last session - void ReadCdTags(ref MediaType mediaType, Dictionary mediaTags, out int sessions, - out int firstTrackLastSession) + byte[] cmdBuf; // Data buffer + bool sense; // Sense indicator + byte[] tmpBuf; // Temporary buffer + sessions = 1; + firstTrackLastSession = 1; + + // ATIP exists on blank CDs + _dumpLog.WriteLine("Reading ATIP"); + UpdateStatus?.Invoke("Reading ATIP"); + sense = _dev.ReadAtip(out cmdBuf, out _, _dev.Timeout, out _); + + if(!sense) { - byte[] cmdBuf; // Data buffer - bool sense; // Sense indicator - byte[] tmpBuf; // Temporary buffer - sessions = 1; - firstTrackLastSession = 1; + ATIP.CDATIP atip = ATIP.Decode(cmdBuf); - // ATIP exists on blank CDs - _dumpLog.WriteLine("Reading ATIP"); - UpdateStatus?.Invoke("Reading ATIP"); - sense = _dev.ReadAtip(out cmdBuf, out _, _dev.Timeout, out _); - - if(!sense) + if(atip != null) { - ATIP.CDATIP atip = ATIP.Decode(cmdBuf); + // Only CD-R and CD-RW have ATIP + mediaType = atip.DiscType ? MediaType.CDRW : MediaType.CDR; - if(atip != null) - { - // Only CD-R and CD-RW have ATIP - mediaType = atip.DiscType ? MediaType.CDRW : MediaType.CDR; - - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf); - } - } - - _dumpLog.WriteLine("Reading Disc Information"); - UpdateStatus?.Invoke("Reading Disc Information"); - - sense = _dev.ReadDiscInformation(out cmdBuf, out _, MmcDiscInformationDataTypes.DiscInformation, - _dev.Timeout, out _); - - if(!sense) - { - DiscInformation.StandardDiscInformation? discInfo = DiscInformation.Decode000b(cmdBuf); - - if(discInfo.HasValue && - mediaType == MediaType.CD) - switch(discInfo.Value.DiscType) - { - case 0x10: - mediaType = MediaType.CDI; - - break; - case 0x20: - mediaType = MediaType.CDROMXA; - - break; - } - } - - _dumpLog.WriteLine("Reading PMA"); - UpdateStatus?.Invoke("Reading PMA"); - sense = _dev.ReadPma(out cmdBuf, out _, _dev.Timeout, out _); - - if(!sense && - PMA.Decode(cmdBuf).HasValue) - { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.CD_PMA, tmpBuf); + mediaTags.Add(MediaTagType.CD_ATIP, tmpBuf); } + } - _dumpLog.WriteLine("Reading Session Information"); - UpdateStatus?.Invoke("Reading Session Information"); - sense = _dev.ReadSessionInfo(out cmdBuf, out _, _dev.Timeout, out _); + _dumpLog.WriteLine("Reading Disc Information"); + UpdateStatus?.Invoke("Reading Disc Information"); - if(!sense) - { - Session.CDSessionInfo? session = Session.Decode(cmdBuf); + sense = _dev.ReadDiscInformation(out cmdBuf, out _, MmcDiscInformationDataTypes.DiscInformation, + _dev.Timeout, out _); - if(session.HasValue) + if(!sense) + { + DiscInformation.StandardDiscInformation? discInfo = DiscInformation.Decode000b(cmdBuf); + + if(discInfo.HasValue && + mediaType == MediaType.CD) + switch(discInfo.Value.DiscType) { - sessions = session.Value.LastCompleteSession; - firstTrackLastSession = session.Value.TrackDescriptors[0].TrackNumber; + case 0x10: + mediaType = MediaType.CDI; + + break; + case 0x20: + mediaType = MediaType.CDROMXA; + + break; } - } + } - _dumpLog.WriteLine("Reading CD-Text from Lead-In"); - UpdateStatus?.Invoke("Reading CD-Text from Lead-In"); - sense = _dev.ReadCdText(out cmdBuf, out _, _dev.Timeout, out _); - - if(sense || !CDTextOnLeadIn.Decode(cmdBuf).HasValue) - return; + _dumpLog.WriteLine("Reading PMA"); + UpdateStatus?.Invoke("Reading PMA"); + sense = _dev.ReadPma(out cmdBuf, out _, _dev.Timeout, out _); + if(!sense && + PMA.Decode(cmdBuf).HasValue) + { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.CD_TEXT, tmpBuf); + mediaTags.Add(MediaTagType.CD_PMA, tmpBuf); } + + _dumpLog.WriteLine("Reading Session Information"); + UpdateStatus?.Invoke("Reading Session Information"); + sense = _dev.ReadSessionInfo(out cmdBuf, out _, _dev.Timeout, out _); + + if(!sense) + { + Session.CDSessionInfo? session = Session.Decode(cmdBuf); + + if(session.HasValue) + { + sessions = session.Value.LastCompleteSession; + firstTrackLastSession = session.Value.TrackDescriptors[0].TrackNumber; + } + } + + _dumpLog.WriteLine("Reading CD-Text from Lead-In"); + UpdateStatus?.Invoke("Reading CD-Text from Lead-In"); + sense = _dev.ReadCdText(out cmdBuf, out _, _dev.Timeout, out _); + + if(sense || !CDTextOnLeadIn.Decode(cmdBuf).HasValue) + return; + + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.CD_TEXT, tmpBuf); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Tracks.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Tracks.cs index 186fdb48a..0392d520d 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Tracks.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Tracks.cs @@ -44,243 +44,242 @@ using Aaru.Devices; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Reads the TOC, processes it, returns the track list and last sector + /// Device + /// Dump log + /// Force dump enabled + /// Last sector number + /// Lead-out starts + /// Media tags + /// Stopping error message handler + /// Full CD TOC + /// Track flags + /// Update status handler + /// List of tracks + public static Track[] GetCdTracks(Device dev, DumpLog dumpLog, bool force, out long lastSector, + Dictionary leadOutStarts, + Dictionary mediaTags, + ErrorMessageHandler stoppingErrorMessage, out FullTOC.CDFullTOC? toc, + Dictionary trackFlags, UpdateStatusHandler updateStatus) { - /// Reads the TOC, processes it, returns the track list and last sector - /// Device - /// Dump log - /// Force dump enabled - /// Last sector number - /// Lead-out starts - /// Media tags - /// Stopping error message handler - /// Full CD TOC - /// Track flags - /// Update status handler - /// List of tracks - public static Track[] GetCdTracks(Device dev, DumpLog dumpLog, bool force, out long lastSector, - Dictionary leadOutStarts, - Dictionary mediaTags, - ErrorMessageHandler stoppingErrorMessage, out FullTOC.CDFullTOC? toc, - Dictionary trackFlags, UpdateStatusHandler updateStatus) + byte[] cmdBuf; // Data buffer + const uint sectorSize = 2352; // Full sector size + bool sense; // Sense indicator + List trackList = new List(); // Tracks in disc + byte[] tmpBuf; // Temporary buffer + toc = null; + lastSector = 0; + TrackType leadoutTrackType = TrackType.Audio; + + // We discarded all discs that falsify a TOC before requesting a real TOC + // No TOC, no CD (or an empty one) + dumpLog?.WriteLine("Reading full TOC"); + updateStatus?.Invoke("Reading full TOC"); + sense = dev.ReadRawToc(out cmdBuf, out _, 0, dev.Timeout, out _); + + if(!sense) { - byte[] cmdBuf; // Data buffer - const uint sectorSize = 2352; // Full sector size - bool sense; // Sense indicator - List trackList = new List(); // Tracks in disc - byte[] tmpBuf; // Temporary buffer - toc = null; - lastSector = 0; - TrackType leadoutTrackType = TrackType.Audio; - - // We discarded all discs that falsify a TOC before requesting a real TOC - // No TOC, no CD (or an empty one) - dumpLog?.WriteLine("Reading full TOC"); - updateStatus?.Invoke("Reading full TOC"); - sense = dev.ReadRawToc(out cmdBuf, out _, 0, dev.Timeout, out _); - - if(!sense) - { - toc = FullTOC.Decode(cmdBuf); - - if(toc.HasValue) - { - tmpBuf = new byte[cmdBuf.Length - 2]; - Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2); - mediaTags?.Add(MediaTagType.CD_FullTOC, tmpBuf); - } - } - - updateStatus?.Invoke("Building track map..."); - dumpLog?.WriteLine("Building track map..."); + toc = FullTOC.Decode(cmdBuf); if(toc.HasValue) { - FullTOC.TrackDataDescriptor[] sortedTracks = - toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray(); + tmpBuf = new byte[cmdBuf.Length - 2]; + Array.Copy(cmdBuf, 2, tmpBuf, 0, cmdBuf.Length - 2); + mediaTags?.Add(MediaTagType.CD_FullTOC, tmpBuf); + } + } - foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4)) - if(trk.POINT >= 0x01 && - trk.POINT <= 0x63) + updateStatus?.Invoke("Building track map..."); + dumpLog?.WriteLine("Building track map..."); + + if(toc.HasValue) + { + FullTOC.TrackDataDescriptor[] sortedTracks = + toc.Value.TrackDescriptors.OrderBy(track => track.POINT).ToArray(); + + foreach(FullTOC.TrackDataDescriptor trk in sortedTracks.Where(trk => trk.ADR == 1 || trk.ADR == 4)) + if(trk.POINT >= 0x01 && + trk.POINT <= 0x63) + { + trackList.Add(new Track { - trackList.Add(new Track - { - Sequence = trk.POINT, - Session = trk.SessionNumber, - Type = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental - ? TrackType.Data : TrackType.Audio, - StartSector = - (ulong)((trk.PHOUR * 3600 * 75) + (trk.PMIN * 60 * 75) + (trk.PSEC * 75) + trk.PFRAME - - 150), - BytesPerSector = (int)sectorSize, - RawBytesPerSector = (int)sectorSize - }); + Sequence = trk.POINT, + Session = trk.SessionNumber, + Type = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental + ? TrackType.Data : TrackType.Audio, + StartSector = + (ulong)((trk.PHOUR * 3600 * 75) + (trk.PMIN * 60 * 75) + (trk.PSEC * 75) + trk.PFRAME - + 150), + BytesPerSector = (int)sectorSize, + RawBytesPerSector = (int)sectorSize + }); - trackFlags?.Add(trk.POINT, trk.CONTROL); - } - else if(trk.POINT == 0xA2) + trackFlags?.Add(trk.POINT, trk.CONTROL); + } + else if(trk.POINT == 0xA2) + { + int phour, pmin, psec, pframe; + + if(trk.PFRAME == 0) { - int phour, pmin, psec, pframe; + pframe = 74; - if(trk.PFRAME == 0) + if(trk.PSEC == 0) { - pframe = 74; + psec = 59; - if(trk.PSEC == 0) + if(trk.PMIN == 0) { - psec = 59; - - if(trk.PMIN == 0) - { - pmin = 59; - phour = trk.PHOUR - 1; - } - else - { - pmin = trk.PMIN - 1; - phour = trk.PHOUR; - } + pmin = 59; + phour = trk.PHOUR - 1; } else { - psec = trk.PSEC - 1; - pmin = trk.PMIN; + pmin = trk.PMIN - 1; phour = trk.PHOUR; } } else { - pframe = trk.PFRAME - 1; - psec = trk.PSEC; - pmin = trk.PMIN; - phour = trk.PHOUR; + psec = trk.PSEC - 1; + pmin = trk.PMIN; + phour = trk.PHOUR; } - - lastSector = (phour * 3600 * 75) + (pmin * 60 * 75) + (psec * 75) + pframe - 150; - leadOutStarts?.Add(trk.SessionNumber, lastSector + 1); } - else if(trk.POINT == 0xA0 && - trk.ADR == 1) + else + { + pframe = trk.PFRAME - 1; + psec = trk.PSEC; + pmin = trk.PMIN; + phour = trk.PHOUR; + } + + lastSector = (phour * 3600 * 75) + (pmin * 60 * 75) + (psec * 75) + pframe - 150; + leadOutStarts?.Add(trk.SessionNumber, lastSector + 1); + } + else if(trk.POINT == 0xA0 && + trk.ADR == 1) + { + leadoutTrackType = + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data + : TrackType.Audio; + } + } + else + { + updateStatus?.Invoke("Cannot read RAW TOC, requesting processed one..."); + dumpLog?.WriteLine("Cannot read RAW TOC, requesting processed one..."); + sense = dev.ReadToc(out cmdBuf, out _, false, 0, dev.Timeout, out _); + + TOC.CDTOC? oldToc = TOC.Decode(cmdBuf); + + if((sense || !oldToc.HasValue) && + !force) + { + dumpLog?.WriteLine("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000..."); + + stoppingErrorMessage?. + Invoke("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000..."); + + return null; + } + + if(oldToc.HasValue) + foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc.Value.TrackDescriptors. + OrderBy(t => t.TrackNumber). + Where(trk => trk.ADR == 1 || trk.ADR == 4)) + if(trk.TrackNumber >= 0x01 && + trk.TrackNumber <= 0x63) + { + trackList.Add(new Track + { + Sequence = trk.TrackNumber, + Session = 1, + Type = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental + ? TrackType.Data : TrackType.Audio, + StartSector = trk.TrackStartAddress, + BytesPerSector = (int)sectorSize, + RawBytesPerSector = (int)sectorSize + }); + + trackFlags?.Add(trk.TrackNumber, trk.CONTROL); + } + else if(trk.TrackNumber == 0xAA) { leadoutTrackType = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data : TrackType.Audio; + + lastSector = trk.TrackStartAddress - 1; } - } - else + } + + if(trackList.Count == 0) + { + updateStatus?.Invoke("No tracks found, adding a single track from 0 to Lead-Out"); + dumpLog?.WriteLine("No tracks found, adding a single track from 0 to Lead-Out"); + + trackList.Add(new Track { - updateStatus?.Invoke("Cannot read RAW TOC, requesting processed one..."); - dumpLog?.WriteLine("Cannot read RAW TOC, requesting processed one..."); - sense = dev.ReadToc(out cmdBuf, out _, false, 0, dev.Timeout, out _); + Sequence = 1, + Session = 1, + Type = leadoutTrackType, + StartSector = 0, + BytesPerSector = (int)sectorSize, + RawBytesPerSector = (int)sectorSize + }); - TOC.CDTOC? oldToc = TOC.Decode(cmdBuf); + trackFlags?.Add(1, (byte)(leadoutTrackType == TrackType.Audio ? 0 : 4)); + } - if((sense || !oldToc.HasValue) && - !force) - { - dumpLog?.WriteLine("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000..."); + if(lastSector != 0) + return trackList.ToArray(); - stoppingErrorMessage?. - Invoke("Could not read TOC, if you want to continue, use force, and will try from LBA 0 to 360000..."); + sense = dev.ReadCapacity16(out cmdBuf, out _, dev.Timeout, out _); - return null; - } + if(!sense) + { + byte[] temp = new byte[8]; - if(oldToc.HasValue) - foreach(TOC.CDTOCTrackDataDescriptor trk in oldToc.Value.TrackDescriptors. - OrderBy(t => t.TrackNumber). - Where(trk => trk.ADR == 1 || trk.ADR == 4)) - if(trk.TrackNumber >= 0x01 && - trk.TrackNumber <= 0x63) - { - trackList.Add(new Track - { - Sequence = trk.TrackNumber, - Session = 1, - Type = (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental - ? TrackType.Data : TrackType.Audio, - StartSector = trk.TrackStartAddress, - BytesPerSector = (int)sectorSize, - RawBytesPerSector = (int)sectorSize - }); - - trackFlags?.Add(trk.TrackNumber, trk.CONTROL); - } - else if(trk.TrackNumber == 0xAA) - { - leadoutTrackType = - (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl)(trk.CONTROL & 0x0D) == TocControl.DataTrackIncremental ? TrackType.Data - : TrackType.Audio; - - lastSector = trk.TrackStartAddress - 1; - } - } - - if(trackList.Count == 0) - { - updateStatus?.Invoke("No tracks found, adding a single track from 0 to Lead-Out"); - dumpLog?.WriteLine("No tracks found, adding a single track from 0 to Lead-Out"); - - trackList.Add(new Track - { - Sequence = 1, - Session = 1, - Type = leadoutTrackType, - StartSector = 0, - BytesPerSector = (int)sectorSize, - RawBytesPerSector = (int)sectorSize - }); - - trackFlags?.Add(1, (byte)(leadoutTrackType == TrackType.Audio ? 0 : 4)); - } - - if(lastSector != 0) - return trackList.ToArray(); - - sense = dev.ReadCapacity16(out cmdBuf, out _, dev.Timeout, out _); + Array.Copy(cmdBuf, 0, temp, 0, 8); + Array.Reverse(temp); + lastSector = (long)BitConverter.ToUInt64(temp, 0); + } + else + { + sense = dev.ReadCapacity(out cmdBuf, out _, dev.Timeout, out _); if(!sense) - { - byte[] temp = new byte[8]; - - Array.Copy(cmdBuf, 0, temp, 0, 8); - Array.Reverse(temp); - lastSector = (long)BitConverter.ToUInt64(temp, 0); - } - else - { - sense = dev.ReadCapacity(out cmdBuf, out _, dev.Timeout, out _); - - if(!sense) - lastSector = ((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & 0xFFFFFFFF; - } - - if(lastSector > 0) - return trackList.ToArray(); - - if(!force) - { - stoppingErrorMessage?. - Invoke("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors..."); - - dumpLog?.WriteLine("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors..."); - - return null; - } - - updateStatus?. - Invoke("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before..."); - - dumpLog?.WriteLine("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before..."); - lastSector = 360000; - - return trackList.ToArray(); + lastSector = ((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & 0xFFFFFFFF; } + + if(lastSector > 0) + return trackList.ToArray(); + + if(!force) + { + stoppingErrorMessage?. + Invoke("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors..."); + + dumpLog?.WriteLine("Could not find Lead-Out, if you want to continue use force option and will continue until 360000 sectors..."); + + return null; + } + + updateStatus?. + Invoke("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before..."); + + dumpLog?.WriteLine("WARNING: Could not find Lead-Out start, will try to read up to 360000 sectors, probably will fail before..."); + lastSector = 360000; + + return trackList.ToArray(); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs b/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs index 60f1fbb11..4dcadf4df 100644 --- a/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs +++ b/Aaru.Core/Devices/Dumping/CompactDisc/Trim.cs @@ -46,263 +46,262 @@ using Schemas; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Trims errored sectors in a CompactDisc + /// Extents with audio sectors + /// Size of the read sector in bytes + /// Current dump hardware try + /// Extents + /// Is trim a new one? + /// Read offset + /// Device supports READ(6) + /// Device supports READ(10) + /// Device supports READ(12) + /// Device supports READ(16) + /// Device supports READ CD + /// Sectors needed to fix offset + /// Subchannel size in bytes + /// Drive's maximum supported subchannel + /// Supports reading EDC and ECC + /// Total commands duration + /// Disc tracks + /// Subchannel log + /// Subchannel desired to save + /// List of disc ISRCs + /// Disc media catalogue number + /// List of subchannels not yet dumped correctly + /// List of smallest pregap relative address per track + void TrimCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, + ExtentsULong extents, bool newTrim, int offsetBytes, bool read6, bool read10, bool read12, + bool read16, bool readcd, int sectorsForOffset, uint subSize, + MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration, + SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks, + Dictionary isrcs, ref string mcn, HashSet subchannelExtents, + Dictionary smallestPregapLbaPerTrack) { - /// Trims errored sectors in a CompactDisc - /// Extents with audio sectors - /// Size of the read sector in bytes - /// Current dump hardware try - /// Extents - /// Is trim a new one? - /// Read offset - /// Device supports READ(6) - /// Device supports READ(10) - /// Device supports READ(12) - /// Device supports READ(16) - /// Device supports READ CD - /// Sectors needed to fix offset - /// Subchannel size in bytes - /// Drive's maximum supported subchannel - /// Supports reading EDC and ECC - /// Total commands duration - /// Disc tracks - /// Subchannel log - /// Subchannel desired to save - /// List of disc ISRCs - /// Disc media catalogue number - /// List of subchannels not yet dumped correctly - /// List of smallest pregap relative address per track - void TrimCdUserData(ExtentsULong audioExtents, uint blockSize, DumpHardwareType currentTry, - ExtentsULong extents, bool newTrim, int offsetBytes, bool read6, bool read10, bool read12, - bool read16, bool readcd, int sectorsForOffset, uint subSize, - MmcSubchannel supportedSubchannel, bool supportsLongSectors, ref double totalDuration, - SubchannelLog subLog, MmcSubchannel desiredSubchannel, Track[] tracks, - Dictionary isrcs, ref string mcn, HashSet subchannelExtents, - Dictionary smallestPregapLbaPerTrack) + DateTime start; + DateTime end; + bool sense = true; // Sense indicator + byte[] cmdBuf = null; // Data buffer + double cmdDuration = 0; // Command execution time + const uint sectorSize = 2352; // Full sector size + PlextorSubchannel supportedPlextorSubchannel; + byte[] senseBuf = null; + var outputOptical = _outputPlugin as IWritableOpticalImage; + + switch(supportedSubchannel) { - DateTime start; - DateTime end; - bool sense = true; // Sense indicator - byte[] cmdBuf = null; // Data buffer - double cmdDuration = 0; // Command execution time - const uint sectorSize = 2352; // Full sector size - PlextorSubchannel supportedPlextorSubchannel; - byte[] senseBuf = null; - var outputOptical = _outputPlugin as IWritableOpticalImage; + case MmcSubchannel.None: + supportedPlextorSubchannel = PlextorSubchannel.None; - switch(supportedSubchannel) + break; + case MmcSubchannel.Raw: + supportedPlextorSubchannel = PlextorSubchannel.Pack; + + break; + case MmcSubchannel.Q16: + supportedPlextorSubchannel = PlextorSubchannel.Q16; + + break; + default: + supportedPlextorSubchannel = PlextorSubchannel.None; + + break; + } + + if(_resume.BadBlocks.Count <= 0 || + _aborted || + !_trim || + !newTrim) + return; + + start = DateTime.UtcNow; + UpdateStatus?.Invoke("Trimming skipped sectors"); + _dumpLog.WriteLine("Trimming skipped sectors"); + InitProgress?.Invoke(); + + trimStart: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); + + for(int b = 0; b < tmpArray.Length; b++) + { + ulong badSector = tmpArray[b]; + + if(_aborted) { - case MmcSubchannel.None: - supportedPlextorSubchannel = PlextorSubchannel.None; + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - break; - case MmcSubchannel.Raw: - supportedPlextorSubchannel = PlextorSubchannel.Pack; - - break; - case MmcSubchannel.Q16: - supportedPlextorSubchannel = PlextorSubchannel.Q16; - - break; - default: - supportedPlextorSubchannel = PlextorSubchannel.None; - - break; + break; } - if(_resume.BadBlocks.Count <= 0 || - _aborted || - !_trim || - !newTrim) - return; + PulseProgress?.Invoke($"Trimming sector {badSector}"); - start = DateTime.UtcNow; - UpdateStatus?.Invoke("Trimming skipped sectors"); - _dumpLog.WriteLine("Trimming skipped sectors"); - InitProgress?.Invoke(); + Track track = tracks.OrderBy(t => t.StartSector). + LastOrDefault(t => badSector >= t.StartSector); - trimStart: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); + byte sectorsToTrim = 1; + uint badSectorToRead = (uint)badSector; - for(int b = 0; b < tmpArray.Length; b++) + if(_fixOffset && + audioExtents.Contains(badSector) && + offsetBytes != 0) { - ulong badSector = tmpArray[b]; - - if(_aborted) + if(offsetBytes > 0) { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; + badSectorToRead -= (uint)sectorsForOffset; } - PulseProgress?.Invoke($"Trimming sector {badSector}"); + sectorsToTrim = (byte)(sectorsForOffset + 1); + } - Track track = tracks.OrderBy(t => t.StartSector). - LastOrDefault(t => badSector >= t.StartSector); + bool forceFixOffset = false; - byte sectorsToTrim = 1; - uint badSectorToRead = (uint)badSector; - - if(_fixOffset && - audioExtents.Contains(badSector) && - offsetBytes != 0) + if(_supportsPlextorD8 && audioExtents.Contains(badSector)) + sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, badSectorToRead, blockSize, + sectorsToTrim, supportedPlextorSubchannel, out cmdDuration); + else if(readcd) + { + if(audioExtents.Contains(badSector)) { - if(offsetBytes > 0) + sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim, + MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); + + if(sense) { - badSectorToRead -= (uint)sectorsForOffset; - } + DecodedSense? decSense = Sense.Decode(senseBuf); - sectorsToTrim = (byte)(sectorsForOffset + 1); - } - - bool forceFixOffset = false; - - if(_supportsPlextorD8 && audioExtents.Contains(badSector)) - sense = ReadPlextorWithSubchannel(out cmdBuf, out senseBuf, badSectorToRead, blockSize, - sectorsToTrim, supportedPlextorSubchannel, out cmdDuration); - else if(readcd) - { - if(audioExtents.Contains(badSector)) - { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim, - MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, supportedSubchannel, _dev.Timeout, out cmdDuration); - - if(sense) + // Try to workaround firmware + if((decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05) || + decSense?.ASC == 0x64) { - DecodedSense? decSense = Sense.Decode(senseBuf); + sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + supportedSubchannel, _dev.Timeout, out double cmdDuration2); - // Try to workaround firmware - if((decSense?.ASC == 0x11 && decSense?.ASCQ == 0x05) || - decSense?.ASC == 0x64) - { - sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - supportedSubchannel, _dev.Timeout, out double cmdDuration2); - - cmdDuration += cmdDuration2; - } + cmdDuration += cmdDuration2; } } - else + } + else + { + sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, + true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, + out cmdDuration); + + if(sense) { - sense = _dev.ReadCd(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, - true, true, MmcErrorField.None, supportedSubchannel, _dev.Timeout, - out cmdDuration); + DecodedSense? decSense = Sense.Decode(senseBuf); - if(sense) + // Try to workaround firmware + if(decSense?.ASC == 0x64) { - DecodedSense? decSense = Sense.Decode(senseBuf); + sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim, + MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, supportedSubchannel, _dev.Timeout, + out double cmdDuration2); - // Try to workaround firmware - if(decSense?.ASC == 0x64) - { - sense = _dev.ReadCd(out cmdBuf, out _, badSectorToRead, blockSize, sectorsToTrim, - MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, supportedSubchannel, _dev.Timeout, - out double cmdDuration2); - - cmdDuration += cmdDuration2; - } + cmdDuration += cmdDuration2; } } - - totalDuration += cmdDuration; } - else if(read16) - sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, badSectorToRead, blockSize, 0, - sectorsToTrim, false, _dev.Timeout, out cmdDuration); - else if(read12) - sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, badSectorToRead, - blockSize, 0, sectorsToTrim, false, _dev.Timeout, out cmdDuration); - else if(read10) - sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, badSectorToRead, - blockSize, 0, sectorsToTrim, _dev.Timeout, out cmdDuration); - else if(read6) - sense = _dev.Read6(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim, - _dev.Timeout, out cmdDuration); totalDuration += cmdDuration; + } + else if(read16) + sense = _dev.Read16(out cmdBuf, out senseBuf, 0, false, true, false, badSectorToRead, blockSize, 0, + sectorsToTrim, false, _dev.Timeout, out cmdDuration); + else if(read12) + sense = _dev.Read12(out cmdBuf, out senseBuf, 0, false, true, false, false, badSectorToRead, + blockSize, 0, sectorsToTrim, false, _dev.Timeout, out cmdDuration); + else if(read10) + sense = _dev.Read10(out cmdBuf, out senseBuf, 0, false, true, false, false, badSectorToRead, + blockSize, 0, sectorsToTrim, _dev.Timeout, out cmdDuration); + else if(read6) + sense = _dev.Read6(out cmdBuf, out senseBuf, badSectorToRead, blockSize, sectorsToTrim, + _dev.Timeout, out cmdDuration); - if(sense || _dev.Error) - { - _errorLog?.WriteLine(badSectorToRead, _dev.Error, _dev.LastError, senseBuf); + totalDuration += cmdDuration; - continue; - } + if(sense || _dev.Error) + { + _errorLog?.WriteLine(badSectorToRead, _dev.Error, _dev.LastError, senseBuf); - if(!sense && - !_dev.Error) - { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - } - - // Because one block has been partially used to fix the offset - if(_fixOffset && - audioExtents.Contains(badSector) && - offsetBytes != 0) - { - uint blocksToRead = sectorsToTrim; - - FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, - subSize, ref cmdBuf, blockSize, false); - } - - if(supportedSubchannel != MmcSubchannel.None) - { - byte[] data = new byte[sectorSize]; - byte[] sub = new byte[subSize]; - Array.Copy(cmdBuf, 0, data, 0, sectorSize); - Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); - - if(supportsLongSectors) - outputOptical.WriteSectorLong(data, badSector); - else - outputOptical.WriteSector(Sector.GetUserData(data), badSector); - - ulong trkStartBefore = track.StartSector; - - bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, - desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.Sequence, ref mcn, - tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, - _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); - - // Set tracks and go back - if(!indexesChanged) - continue; - - (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); - - if(track.StartSector != trkStartBefore && - !_resume.BadBlocks.Contains(track.StartSector)) - { - _resume.BadBlocks.Add(track.StartSector); - - goto trimStart; - } - - b--; - - continue; - } - - if(supportsLongSectors) - outputOptical.WriteSectorLong(cmdBuf, badSector); - else - outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector); + continue; } - EndProgress?.Invoke(); - end = DateTime.UtcNow; - UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); - _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + if(!sense && + !_dev.Error) + { + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + } + + // Because one block has been partially used to fix the offset + if(_fixOffset && + audioExtents.Contains(badSector) && + offsetBytes != 0) + { + uint blocksToRead = sectorsToTrim; + + FixOffsetData(offsetBytes, sectorSize, sectorsForOffset, supportedSubchannel, ref blocksToRead, + subSize, ref cmdBuf, blockSize, false); + } + + if(supportedSubchannel != MmcSubchannel.None) + { + byte[] data = new byte[sectorSize]; + byte[] sub = new byte[subSize]; + Array.Copy(cmdBuf, 0, data, 0, sectorSize); + Array.Copy(cmdBuf, sectorSize, sub, 0, subSize); + + if(supportsLongSectors) + outputOptical.WriteSectorLong(data, badSector); + else + outputOptical.WriteSector(Sector.GetUserData(data), badSector); + + ulong trkStartBefore = track.StartSector; + + bool indexesChanged = Media.CompactDisc.WriteSubchannelToImage(supportedSubchannel, + desiredSubchannel, sub, badSector, 1, subLog, isrcs, (byte)track.Sequence, ref mcn, + tracks, subchannelExtents, _fixSubchannelPosition, outputOptical, _fixSubchannel, + _fixSubchannelCrc, _dumpLog, UpdateStatus, smallestPregapLbaPerTrack, true); + + // Set tracks and go back + if(!indexesChanged) + continue; + + (outputOptical as IWritableOpticalImage).SetTracks(tracks.ToList()); + + if(track.StartSector != trkStartBefore && + !_resume.BadBlocks.Contains(track.StartSector)) + { + _resume.BadBlocks.Add(track.StartSector); + + goto trimStart; + } + + b--; + + continue; + } + + if(supportsLongSectors) + outputOptical.WriteSectorLong(cmdBuf, badSector); + else + outputOptical.WriteSector(Sector.GetUserData(cmdBuf), badSector); } + + EndProgress?.Invoke(); + end = DateTime.UtcNow; + UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); + _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/Dump.cs b/Aaru.Core/Devices/Dumping/Dump.cs index 3bc405a78..ce4b9652b 100644 --- a/Aaru.Core/Devices/Dumping/Dump.cs +++ b/Aaru.Core/Devices/Dumping/Dump.cs @@ -46,268 +46,267 @@ using Aaru.Database; using Aaru.Devices; using Schemas; -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Subchannel requested to dump +public enum DumpSubchannel { - /// Subchannel requested to dump - public enum DumpSubchannel + /// Any available subchannel, in order: raw P to W, PQ, none + Any, + /// Raw P to W + Rw, + /// Raw P to W or PQ if not possible + RwOrPq, + /// PQ + Pq, + /// None + None +} + +public partial class Dump +{ + readonly bool _debug; + readonly Device _dev; + readonly string _devicePath; + readonly bool _doResume; + readonly DumpLog _dumpLog; + readonly bool _dumpRaw; + readonly Encoding _encoding; + readonly ErrorLog _errorLog; + readonly bool _fixSubchannel; + readonly bool _fixSubchannelCrc; + readonly bool _fixSubchannelPosition; + readonly bool _force; + readonly Dictionary _formatOptions; + readonly bool _generateSubchannels; + readonly bool _metadata; + readonly string _outputPath; + readonly IBaseWritableImage _outputPlugin; + readonly string _outputPrefix; + readonly bool _persistent; + readonly CICMMetadataType _preSidecar; + readonly bool _private; + readonly ushort _retryPasses; + readonly bool _retrySubchannel; + readonly bool _stopOnError; + readonly bool _storeEncrypted; + readonly DumpSubchannel _subchannel; + readonly bool _titleKeys; + readonly bool _trim; + bool _useBufferedReads; + bool _aborted; + AaruContext _ctx; // Main database context + Database.Models.Device _dbDev; // Device database entry + bool _dumpFirstTrackPregap; + bool _fixOffset; + uint _maximumReadable; // Maximum number of sectors drive can read at once + Resume _resume; + Sidecar _sidecarClass; + uint _skip; + bool _skipCdireadyHole; + int _speed; + int _speedMultiplier; + bool _supportsPlextorD8; + + /// Initializes dumpers + /// Should resume? + /// Device + /// Path to the device + /// Prefix for output log files + /// Plugin for output file + /// How many times to retry + /// Force to continue dump whenever possible + /// Dump long sectors + /// Store whatever data the drive returned on error + /// Stop dump on first error + /// Information for dump resuming + /// Dump logger + /// Encoding to use when analyzing dump + /// Path to output file + /// Formats to pass to output file plugin + /// Trim errors from skipped sectors + /// Try to read and dump as much first track pregap as possible + /// Sidecar to store in dumped image + /// How many sectors to skip reading on error + /// Create metadata sidecar after dump? + /// Fix audio offset + /// Debug mode + /// Desired subchannel to save to image + /// Desired drive speed + /// Disable saving paths or serial numbers in images and logs + /// Fix subchannel position (save where it says it belongs) + /// Retry reading incorrect or missing subchannels + /// Try to fix subchannel errors (but not Q CRC) + /// Try to fix subchannel Q CRC errors + /// Skip gap between CD-i Ready hidden track and track 1 audio + /// Error log + /// Generate missing subchannels + /// Number of maximum blocks to be read at once (can be overriden by database) + /// + /// If MMC/SD does not support CMD23, use OS buffered reads instead of multiple single block + /// commands + /// + /// Store encrypted data as is + /// Dump DVD CSS title keys + public Dump(bool doResume, Device dev, string devicePath, IBaseWritableImage outputPlugin, ushort retryPasses, + bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, DumpLog dumpLog, + Encoding encoding, string outputPrefix, string outputPath, Dictionary formatOptions, + CICMMetadataType preSidecar, uint skip, bool metadata, bool trim, bool dumpFirstTrackPregap, + bool fixOffset, bool debug, DumpSubchannel subchannel, int speed, bool @private, + bool fixSubchannelPosition, bool retrySubchannel, bool fixSubchannel, bool fixSubchannelCrc, + bool skipCdireadyHole, ErrorLog errorLog, bool generateSubchannels, uint maximumReadable, + bool useBufferedReads, bool storeEncrypted, bool titleKeys) { - /// Any available subchannel, in order: raw P to W, PQ, none - Any, - /// Raw P to W - Rw, - /// Raw P to W or PQ if not possible - RwOrPq, - /// PQ - Pq, - /// None - None + _doResume = doResume; + _dev = dev; + _devicePath = devicePath; + _outputPlugin = outputPlugin; + _retryPasses = retryPasses; + _force = force; + _dumpRaw = dumpRaw; + _persistent = persistent; + _stopOnError = stopOnError; + _resume = resume; + _dumpLog = dumpLog; + _encoding = encoding; + _outputPrefix = outputPrefix; + _outputPath = outputPath; + _formatOptions = formatOptions; + _preSidecar = preSidecar; + _skip = skip; + _metadata = metadata; + _trim = trim; + _dumpFirstTrackPregap = dumpFirstTrackPregap; + _aborted = false; + _fixOffset = fixOffset; + _debug = debug; + _maximumReadable = maximumReadable; + _subchannel = subchannel; + _speedMultiplier = -1; + _speed = speed; + _private = @private; + _fixSubchannelPosition = fixSubchannelPosition; + _retrySubchannel = retrySubchannel; + _fixSubchannel = fixSubchannel; + _fixSubchannelCrc = fixSubchannelCrc; + _skipCdireadyHole = skipCdireadyHole; + _errorLog = errorLog; + _generateSubchannels = generateSubchannels; + _useBufferedReads = useBufferedReads; + _storeEncrypted = storeEncrypted; + _titleKeys = titleKeys; } - public partial class Dump + /// Starts dumping with the established fields and autodetecting the device type + public void Start() { - readonly bool _debug; - readonly Device _dev; - readonly string _devicePath; - readonly bool _doResume; - readonly DumpLog _dumpLog; - readonly bool _dumpRaw; - readonly Encoding _encoding; - readonly ErrorLog _errorLog; - readonly bool _fixSubchannel; - readonly bool _fixSubchannelCrc; - readonly bool _fixSubchannelPosition; - readonly bool _force; - readonly Dictionary _formatOptions; - readonly bool _generateSubchannels; - readonly bool _metadata; - readonly string _outputPath; - readonly IBaseWritableImage _outputPlugin; - readonly string _outputPrefix; - readonly bool _persistent; - readonly CICMMetadataType _preSidecar; - readonly bool _private; - readonly ushort _retryPasses; - readonly bool _retrySubchannel; - readonly bool _stopOnError; - readonly bool _storeEncrypted; - readonly DumpSubchannel _subchannel; - readonly bool _titleKeys; - readonly bool _trim; - bool _useBufferedReads; - bool _aborted; - AaruContext _ctx; // Main database context - Database.Models.Device _dbDev; // Device database entry - bool _dumpFirstTrackPregap; - bool _fixOffset; - uint _maximumReadable; // Maximum number of sectors drive can read at once - Resume _resume; - Sidecar _sidecarClass; - uint _skip; - bool _skipCdireadyHole; - int _speed; - int _speedMultiplier; - bool _supportsPlextorD8; + // Open main database + _ctx = AaruContext.Create(Settings.Settings.MainDbPath); - /// Initializes dumpers - /// Should resume? - /// Device - /// Path to the device - /// Prefix for output log files - /// Plugin for output file - /// How many times to retry - /// Force to continue dump whenever possible - /// Dump long sectors - /// Store whatever data the drive returned on error - /// Stop dump on first error - /// Information for dump resuming - /// Dump logger - /// Encoding to use when analyzing dump - /// Path to output file - /// Formats to pass to output file plugin - /// Trim errors from skipped sectors - /// Try to read and dump as much first track pregap as possible - /// Sidecar to store in dumped image - /// How many sectors to skip reading on error - /// Create metadata sidecar after dump? - /// Fix audio offset - /// Debug mode - /// Desired subchannel to save to image - /// Desired drive speed - /// Disable saving paths or serial numbers in images and logs - /// Fix subchannel position (save where it says it belongs) - /// Retry reading incorrect or missing subchannels - /// Try to fix subchannel errors (but not Q CRC) - /// Try to fix subchannel Q CRC errors - /// Skip gap between CD-i Ready hidden track and track 1 audio - /// Error log - /// Generate missing subchannels - /// Number of maximum blocks to be read at once (can be overriden by database) - /// - /// If MMC/SD does not support CMD23, use OS buffered reads instead of multiple single block - /// commands - /// - /// Store encrypted data as is - /// Dump DVD CSS title keys - public Dump(bool doResume, Device dev, string devicePath, IBaseWritableImage outputPlugin, ushort retryPasses, - bool force, bool dumpRaw, bool persistent, bool stopOnError, Resume resume, DumpLog dumpLog, - Encoding encoding, string outputPrefix, string outputPath, Dictionary formatOptions, - CICMMetadataType preSidecar, uint skip, bool metadata, bool trim, bool dumpFirstTrackPregap, - bool fixOffset, bool debug, DumpSubchannel subchannel, int speed, bool @private, - bool fixSubchannelPosition, bool retrySubchannel, bool fixSubchannel, bool fixSubchannelCrc, - bool skipCdireadyHole, ErrorLog errorLog, bool generateSubchannels, uint maximumReadable, - bool useBufferedReads, bool storeEncrypted, bool titleKeys) + // Search for device in main database + _dbDev = _ctx.Devices.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model && + d.Revision == _dev.FirmwareRevision); + + if(_dbDev is null) { - _doResume = doResume; - _dev = dev; - _devicePath = devicePath; - _outputPlugin = outputPlugin; - _retryPasses = retryPasses; - _force = force; - _dumpRaw = dumpRaw; - _persistent = persistent; - _stopOnError = stopOnError; - _resume = resume; - _dumpLog = dumpLog; - _encoding = encoding; - _outputPrefix = outputPrefix; - _outputPath = outputPath; - _formatOptions = formatOptions; - _preSidecar = preSidecar; - _skip = skip; - _metadata = metadata; - _trim = trim; - _dumpFirstTrackPregap = dumpFirstTrackPregap; - _aborted = false; - _fixOffset = fixOffset; - _debug = debug; - _maximumReadable = maximumReadable; - _subchannel = subchannel; - _speedMultiplier = -1; - _speed = speed; - _private = @private; - _fixSubchannelPosition = fixSubchannelPosition; - _retrySubchannel = retrySubchannel; - _fixSubchannel = fixSubchannel; - _fixSubchannelCrc = fixSubchannelCrc; - _skipCdireadyHole = skipCdireadyHole; - _errorLog = errorLog; - _generateSubchannels = generateSubchannels; - _useBufferedReads = useBufferedReads; - _storeEncrypted = storeEncrypted; - _titleKeys = titleKeys; + _dumpLog.WriteLine("Device not in database, please create a device report and attach it to a Github issue."); + + UpdateStatus?. + Invoke("Device not in database, please create a device report and attach it to a Github issue."); + } + else + { + _dumpLog.WriteLine($"Device in database since {_dbDev.LastSynchronized}."); + UpdateStatus?.Invoke($"Device in database since {_dbDev.LastSynchronized}."); + + if(_dbDev.OptimalMultipleSectorsRead > 0) + _maximumReadable = (uint)_dbDev.OptimalMultipleSectorsRead; } - /// Starts dumping with the established fields and autodetecting the device type - public void Start() + switch(_dev.IsUsb) { - // Open main database - _ctx = AaruContext.Create(Settings.Settings.MainDbPath); + case true when _dev.UsbVendorId == 0x054C && _dev.UsbProductId is 0x01C8 or 0x01C9 or 0x02D2: PlayStationPortable(); - // Search for device in main database - _dbDev = _ctx.Devices.FirstOrDefault(d => d.Manufacturer == _dev.Manufacturer && d.Model == _dev.Model && - d.Revision == _dev.FirmwareRevision); + break; + case true when _dev.UsbVendorId ==0x0403 && _dev.UsbProductId ==0x97C1: Retrode(); - if(_dbDev is null) - { - _dumpLog.WriteLine("Device not in database, please create a device report and attach it to a Github issue."); + break; + default: + switch(_dev.Type) + { + case DeviceType.ATA: + Ata(); - UpdateStatus?. - Invoke("Device not in database, please create a device report and attach it to a Github issue."); - } - else - { - _dumpLog.WriteLine($"Device in database since {_dbDev.LastSynchronized}."); - UpdateStatus?.Invoke($"Device in database since {_dbDev.LastSynchronized}."); + break; + case DeviceType.MMC: + case DeviceType.SecureDigital: + SecureDigital(); - if(_dbDev.OptimalMultipleSectorsRead > 0) - _maximumReadable = (uint)_dbDev.OptimalMultipleSectorsRead; - } + break; + case DeviceType.NVMe: + NVMe(); - switch(_dev.IsUsb) - { - case true when _dev.UsbVendorId == 0x054C && _dev.UsbProductId is 0x01C8 or 0x01C9 or 0x02D2: PlayStationPortable(); + break; + case DeviceType.ATAPI: + case DeviceType.SCSI: + Scsi(); - break; - case true when _dev.UsbVendorId ==0x0403 && _dev.UsbProductId ==0x97C1: Retrode(); + break; + default: + _dumpLog.WriteLine("Unknown device type."); + _dumpLog.Close(); + StoppingErrorMessage?.Invoke("Unknown device type."); - break; - default: - switch(_dev.Type) - { - case DeviceType.ATA: - Ata(); + return; + } - break; - case DeviceType.MMC: - case DeviceType.SecureDigital: - SecureDigital(); - - break; - case DeviceType.NVMe: - NVMe(); - - break; - case DeviceType.ATAPI: - case DeviceType.SCSI: - Scsi(); - - break; - default: - _dumpLog.WriteLine("Unknown device type."); - _dumpLog.Close(); - StoppingErrorMessage?.Invoke("Unknown device type."); - - return; - } - - break; - } - - _errorLog.Close(); - _dumpLog.Close(); - - if(_resume == null || - !_doResume) - return; - - _resume.LastWriteDate = DateTime.UtcNow; - _resume.BadBlocks.Sort(); - - if(File.Exists(_outputPrefix + ".resume.xml")) - File.Delete(_outputPrefix + ".resume.xml"); - - var fs = new FileStream(_outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite); - var xs = new XmlSerializer(_resume.GetType()); - xs.Serialize(fs, _resume); - fs.Close(); + break; } - /// Aborts the dump in progress - public void Abort() - { - _aborted = true; - _sidecarClass?.Abort(); - } + _errorLog.Close(); + _dumpLog.Close(); - /// Event raised when the progress bar is not longer needed - public event EndProgressHandler EndProgress; - /// Event raised when a progress bar is needed - public event InitProgressHandler InitProgress; - /// Event raised to report status updates - public event UpdateStatusHandler UpdateStatus; - /// Event raised to report a non-fatal error - public event ErrorMessageHandler ErrorMessage; - /// Event raised to report a fatal error that stops the dumping operation and should call user's attention - public event ErrorMessageHandler StoppingErrorMessage; - /// Event raised to update the values of a determinate progress bar - public event UpdateProgressHandler UpdateProgress; - /// Event raised to update the status of an undeterminate progress bar - public event PulseProgressHandler PulseProgress; - /// Event raised when the progress bar is not longer needed - public event EndProgressHandler2 EndProgress2; - /// Event raised when a progress bar is needed - public event InitProgressHandler2 InitProgress2; - /// Event raised to update the values of a determinate progress bar - public event UpdateProgressHandler2 UpdateProgress2; + if(_resume == null || + !_doResume) + return; + + _resume.LastWriteDate = DateTime.UtcNow; + _resume.BadBlocks.Sort(); + + if(File.Exists(_outputPrefix + ".resume.xml")) + File.Delete(_outputPrefix + ".resume.xml"); + + var fs = new FileStream(_outputPrefix + ".resume.xml", FileMode.Create, FileAccess.ReadWrite); + var xs = new XmlSerializer(_resume.GetType()); + xs.Serialize(fs, _resume); + fs.Close(); } + + /// Aborts the dump in progress + public void Abort() + { + _aborted = true; + _sidecarClass?.Abort(); + } + + /// Event raised when the progress bar is not longer needed + public event EndProgressHandler EndProgress; + /// Event raised when a progress bar is needed + public event InitProgressHandler InitProgress; + /// Event raised to report status updates + public event UpdateStatusHandler UpdateStatus; + /// Event raised to report a non-fatal error + public event ErrorMessageHandler ErrorMessage; + /// Event raised to report a fatal error that stops the dumping operation and should call user's attention + public event ErrorMessageHandler StoppingErrorMessage; + /// Event raised to update the values of a determinate progress bar + public event UpdateProgressHandler UpdateProgress; + /// Event raised to update the status of an undeterminate progress bar + public event PulseProgressHandler PulseProgress; + /// Event raised when the progress bar is not longer needed + public event EndProgressHandler2 EndProgress2; + /// Event raised when a progress bar is needed + public event InitProgressHandler2 InitProgress2; + /// Event raised to update the values of a determinate progress bar + public event UpdateProgressHandler2 UpdateProgress2; } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/MMC.cs b/Aaru.Core/Devices/Dumping/MMC.cs index 88a1342c2..817ffa25a 100644 --- a/Aaru.Core/Devices/Dumping/MMC.cs +++ b/Aaru.Core/Devices/Dumping/MMC.cs @@ -52,200 +52,197 @@ using Spare = Aaru.Decoders.DVD.Spare; // ReSharper disable JoinDeclarationAndInitializer -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Implement dumping optical discs from MultiMedia devices +partial class Dump { - /// Implement dumping optical discs from MultiMedia devices - partial class Dump + /// Dumps an optical disc + void Mmc() { - /// Dumps an optical disc - void Mmc() + MediaType dskType = MediaType.Unknown; + bool sense; + byte[] tmpBuf; + bool compactDisc = true; + bool gotConfiguration = false; + bool isXbox = false; + DVDDecryption dvdDecrypt = null; + _speedMultiplier = 1; + + // TODO: Log not only what is it reading, but if it was read correctly or not. + sense = _dev.GetConfiguration(out byte[] cmdBuf, out _, 0, MmcGetConfigurationRt.Current, _dev.Timeout, + out _); + + if(!sense) { - MediaType dskType = MediaType.Unknown; - bool sense; - byte[] tmpBuf; - bool compactDisc = true; - bool gotConfiguration = false; - bool isXbox = false; - DVDDecryption dvdDecrypt = null; - _speedMultiplier = 1; + gotConfiguration = true; + Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); + _dumpLog.WriteLine("Device reports current profile is 0x{0:X4}", ftr.CurrentProfile); - // TODO: Log not only what is it reading, but if it was read correctly or not. - sense = _dev.GetConfiguration(out byte[] cmdBuf, out _, 0, MmcGetConfigurationRt.Current, _dev.Timeout, - out _); - - if(!sense) + switch(ftr.CurrentProfile) { - gotConfiguration = true; - Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); - _dumpLog.WriteLine("Device reports current profile is 0x{0:X4}", ftr.CurrentProfile); + case 0x0001: + dskType = MediaType.GENERIC_HDD; + _speedMultiplier = -1; + goto default; + case 0x0002: + dskType = MediaType.PD650; + _speedMultiplier = -1; + goto default; + case 0x0005: + dskType = MediaType.CDMO; - switch(ftr.CurrentProfile) - { - case 0x0001: - dskType = MediaType.GENERIC_HDD; - _speedMultiplier = -1; - goto default; - case 0x0002: - dskType = MediaType.PD650; - _speedMultiplier = -1; - goto default; - case 0x0005: - dskType = MediaType.CDMO; + break; + case 0x0008: + dskType = MediaType.CD; - break; - case 0x0008: - dskType = MediaType.CD; + break; + case 0x0009: + dskType = MediaType.CDR; - break; - case 0x0009: - dskType = MediaType.CDR; + break; + case 0x000A: + dskType = MediaType.CDRW; - break; - case 0x000A: - dskType = MediaType.CDRW; + break; + case 0x0010: + dskType = MediaType.DVDROM; + _speedMultiplier = 9; + goto default; + case 0x0011: + dskType = MediaType.DVDR; + _speedMultiplier = 9; + goto default; + case 0x0012: + dskType = MediaType.DVDRAM; + _speedMultiplier = 9; + goto default; + case 0x0013: + case 0x0014: + dskType = MediaType.DVDRW; + _speedMultiplier = 9; + goto default; + case 0x0015: + case 0x0016: + dskType = MediaType.DVDRDL; + _speedMultiplier = 9; + goto default; + case 0x0017: + dskType = MediaType.DVDRWDL; + _speedMultiplier = 9; + goto default; + case 0x0018: + dskType = MediaType.DVDDownload; + _speedMultiplier = 9; + goto default; + case 0x001A: + dskType = MediaType.DVDPRW; + _speedMultiplier = 9; + goto default; + case 0x001B: + dskType = MediaType.DVDPR; + _speedMultiplier = 9; + goto default; + case 0x0020: + dskType = MediaType.DDCD; + goto default; + case 0x0021: + dskType = MediaType.DDCDR; + goto default; + case 0x0022: + dskType = MediaType.DDCDRW; + goto default; + case 0x002A: + dskType = MediaType.DVDPRWDL; + _speedMultiplier = 9; + goto default; + case 0x002B: + dskType = MediaType.DVDPRDL; + _speedMultiplier = 9; + goto default; + case 0x0040: + dskType = MediaType.BDROM; + _speedMultiplier = 30; + goto default; + case 0x0041: + case 0x0042: + dskType = MediaType.BDR; + _speedMultiplier = 30; + goto default; + case 0x0043: + dskType = MediaType.BDRE; + _speedMultiplier = 30; + goto default; + case 0x0050: + dskType = MediaType.HDDVDROM; + _speedMultiplier = 30; + goto default; + case 0x0051: + dskType = MediaType.HDDVDR; + _speedMultiplier = 30; + goto default; + case 0x0052: + dskType = MediaType.HDDVDRAM; + _speedMultiplier = 30; + goto default; + case 0x0053: + dskType = MediaType.HDDVDRW; + _speedMultiplier = 30; + goto default; + case 0x0058: + dskType = MediaType.HDDVDRDL; + _speedMultiplier = 30; + goto default; + case 0x005A: + dskType = MediaType.HDDVDRWDL; + _speedMultiplier = 30; + goto default; + default: + compactDisc = false; - break; - case 0x0010: - dskType = MediaType.DVDROM; - _speedMultiplier = 9; - goto default; - case 0x0011: - dskType = MediaType.DVDR; - _speedMultiplier = 9; - goto default; - case 0x0012: - dskType = MediaType.DVDRAM; - _speedMultiplier = 9; - goto default; - case 0x0013: - case 0x0014: - dskType = MediaType.DVDRW; - _speedMultiplier = 9; - goto default; - case 0x0015: - case 0x0016: - dskType = MediaType.DVDRDL; - _speedMultiplier = 9; - goto default; - case 0x0017: - dskType = MediaType.DVDRWDL; - _speedMultiplier = 9; - goto default; - case 0x0018: - dskType = MediaType.DVDDownload; - _speedMultiplier = 9; - goto default; - case 0x001A: - dskType = MediaType.DVDPRW; - _speedMultiplier = 9; - goto default; - case 0x001B: - dskType = MediaType.DVDPR; - _speedMultiplier = 9; - goto default; - case 0x0020: - dskType = MediaType.DDCD; - goto default; - case 0x0021: - dskType = MediaType.DDCDR; - goto default; - case 0x0022: - dskType = MediaType.DDCDRW; - goto default; - case 0x002A: - dskType = MediaType.DVDPRWDL; - _speedMultiplier = 9; - goto default; - case 0x002B: - dskType = MediaType.DVDPRDL; - _speedMultiplier = 9; - goto default; - case 0x0040: - dskType = MediaType.BDROM; - _speedMultiplier = 30; - goto default; - case 0x0041: - case 0x0042: - dskType = MediaType.BDR; - _speedMultiplier = 30; - goto default; - case 0x0043: - dskType = MediaType.BDRE; - _speedMultiplier = 30; - goto default; - case 0x0050: - dskType = MediaType.HDDVDROM; - _speedMultiplier = 30; - goto default; - case 0x0051: - dskType = MediaType.HDDVDR; - _speedMultiplier = 30; - goto default; - case 0x0052: - dskType = MediaType.HDDVDRAM; - _speedMultiplier = 30; - goto default; - case 0x0053: - dskType = MediaType.HDDVDRW; - _speedMultiplier = 30; - goto default; - case 0x0058: - dskType = MediaType.HDDVDRDL; - _speedMultiplier = 30; - goto default; - case 0x005A: - dskType = MediaType.HDDVDRWDL; - _speedMultiplier = 30; - goto default; - default: - compactDisc = false; - - break; - } + break; } + } - Modes.DecodedMode? decMode = null; + Modes.DecodedMode? decMode = null; - sense = _dev.ModeSense6(out cmdBuf, out _, true, ScsiModeSensePageControl.Current, 0x00, _dev.Timeout, + sense = _dev.ModeSense6(out cmdBuf, out _, true, ScsiModeSensePageControl.Current, 0x00, _dev.Timeout, + out _); + + if(sense || _dev.Error) + { + sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x00, _dev.Timeout, out _); + if(!sense && + !_dev.Error) + decMode = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); + } + else + decMode = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); + + if(decMode is null) + { + sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, + _dev.Timeout, out _); + if(sense || _dev.Error) { - sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x00, _dev.Timeout, - out _); - - if(!sense && - !_dev.Error) - decMode = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); - } - else - decMode = Modes.DecodeMode6(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); - - if(decMode is null) - { - sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, - _dev.Timeout, out _); + sense = _dev.ModeSense10(out cmdBuf, out _, false, false, ScsiModeSensePageControl.Current, 0x3F, + 0x00, _dev.Timeout, out _); if(sense || _dev.Error) { - sense = _dev.ModeSense10(out cmdBuf, out _, false, false, ScsiModeSensePageControl.Current, 0x3F, + sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x00, 0x00, _dev.Timeout, out _); if(sense || _dev.Error) { - sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x00, - 0x00, _dev.Timeout, out _); + sense = _dev.ModeSense10(out cmdBuf, out _, false, false, ScsiModeSensePageControl.Current, + 0x00, 0x00, _dev.Timeout, out _); - if(sense || _dev.Error) - { - sense = _dev.ModeSense10(out cmdBuf, out _, false, false, ScsiModeSensePageControl.Current, - 0x00, 0x00, _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); - } - else + if(!sense && + !_dev.Error) decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); } else @@ -254,847 +251,849 @@ namespace Aaru.Core.Devices.Dumping else decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); } + else + decMode = Modes.DecodeMode10(cmdBuf, PeripheralDeviceTypes.MultiMediaDevice); + } - if(decMode.HasValue && - _dev.IsUsb && - !gotConfiguration && - (decMode.Value.Header.MediumType == MediumTypes.UnknownBlockDevice || - decMode.Value.Header.MediumType == MediumTypes.ReadOnlyBlockDevice || - decMode.Value.Header.MediumType == MediumTypes.ReadWriteBlockDevice)) + if(decMode.HasValue && + _dev.IsUsb && + !gotConfiguration && + (decMode.Value.Header.MediumType == MediumTypes.UnknownBlockDevice || + decMode.Value.Header.MediumType == MediumTypes.ReadOnlyBlockDevice || + decMode.Value.Header.MediumType == MediumTypes.ReadWriteBlockDevice)) + { + _speedMultiplier = -1; + Sbc(null, MediaType.Unknown, false); + + return; + } + + if(compactDisc) + { + _speedMultiplier *= 177; + CompactDisc(); + + return; + } + + _speedMultiplier *= 150; + + var scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog, _dumpRaw); + ulong blocks = scsiReader.GetDeviceBlocks(); + _dumpLog.WriteLine("Device reports disc has {0} blocks", blocks); + Dictionary mediaTags = new Dictionary(); + + if(dskType == MediaType.PD650) + switch(blocks + 1) { - _speedMultiplier = -1; - Sbc(null, MediaType.Unknown, false); + case 1281856: + dskType = MediaType.PD650_WORM; - return; + break; + case 58620544: + dskType = MediaType.REV120; + + break; + case 17090880: + dskType = MediaType.REV35; + + break; + + case 34185728: + dskType = MediaType.REV70; + + break; } - if(compactDisc) - { - _speedMultiplier *= 177; - CompactDisc(); + #region Nintendo + switch(dskType) + { + case MediaType.Unknown when blocks > 0: + _dumpLog.WriteLine("Reading Physical Format Information"); - return; - } + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, _dev.Timeout, out _); - _speedMultiplier *= 150; - - var scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog, _dumpRaw); - ulong blocks = scsiReader.GetDeviceBlocks(); - _dumpLog.WriteLine("Device reports disc has {0} blocks", blocks); - Dictionary mediaTags = new Dictionary(); - - if(dskType == MediaType.PD650) - switch(blocks + 1) + if(!sense) { - case 1281856: - dskType = MediaType.PD650_WORM; + PFI.PhysicalFormatInformation? nintendoPfi = PFI.Decode(cmdBuf, dskType); - break; - case 58620544: - dskType = MediaType.REV120; + if(nintendoPfi?.DiskCategory == DiskCategory.Nintendo && + nintendoPfi.Value.PartVersion == 15) + { + _dumpLog.WriteLine("Dumping Nintendo GameCube or Wii discs is not yet implemented."); - break; - case 17090880: - dskType = MediaType.REV35; + StoppingErrorMessage?. + Invoke("Dumping Nintendo GameCube or Wii discs is not yet implemented."); - break; - - case 34185728: - dskType = MediaType.REV70; - - break; + return; + } } - #region Nintendo - switch(dskType) - { - case MediaType.Unknown when blocks > 0: - _dumpLog.WriteLine("Reading Physical Format Information"); + break; + case MediaType.DVDDownload: + case MediaType.DVDPR: + case MediaType.DVDPRDL: + case MediaType.DVDPRW: + case MediaType.DVDPRWDL: + case MediaType.DVDR: + case MediaType.DVDRAM: + case MediaType.DVDRDL: + case MediaType.DVDROM: + case MediaType.DVDRW: + case MediaType.DVDRWDL: + case MediaType.HDDVDR: + case MediaType.HDDVDRAM: + case MediaType.HDDVDRDL: + case MediaType.HDDVDROM: + case MediaType.HDDVDRW: + case MediaType.HDDVDRWDL: + _dumpLog.WriteLine("Reading Physical Format Information"); - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, _dev.Timeout, out _); + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, _dev.Timeout, out _); - if(!sense) + if(!sense) + { + PFI.PhysicalFormatInformation? nullablePfi = PFI.Decode(cmdBuf, dskType); + + if(nullablePfi.HasValue) { - PFI.PhysicalFormatInformation? nintendoPfi = PFI.Decode(cmdBuf, dskType); + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf); - if(nintendoPfi?.DiskCategory == DiskCategory.Nintendo && - nintendoPfi.Value.PartVersion == 15) + PFI.PhysicalFormatInformation decPfi = nullablePfi.Value; + UpdateStatus?.Invoke($"PFI:\n{PFI.Prettify(decPfi)}"); + + // False book types + switch(decPfi.DiskCategory) { - _dumpLog.WriteLine("Dumping Nintendo GameCube or Wii discs is not yet implemented."); + case DiskCategory.DVDPR: + dskType = MediaType.DVDPR; + + break; + case DiskCategory.DVDPRDL: + dskType = MediaType.DVDPRDL; + + break; + case DiskCategory.DVDPRW: + dskType = MediaType.DVDPRW; + + break; + case DiskCategory.DVDPRWDL: + dskType = MediaType.DVDPRWDL; + + break; + case DiskCategory.DVDR: + dskType = decPfi.PartVersion >= 6 ? MediaType.DVDRDL : MediaType.DVDR; + + break; + case DiskCategory.DVDRAM: + dskType = MediaType.DVDRAM; + + break; + default: + dskType = MediaType.DVDROM; + + break; + case DiskCategory.DVDRW: + dskType = decPfi.PartVersion >= 15 ? MediaType.DVDRWDL : MediaType.DVDRW; + + break; + case DiskCategory.HDDVDR: + dskType = MediaType.HDDVDR; + + break; + case DiskCategory.HDDVDRAM: + dskType = MediaType.HDDVDRAM; + + break; + case DiskCategory.HDDVDROM: + dskType = MediaType.HDDVDROM; + + break; + case DiskCategory.HDDVDRW: + dskType = MediaType.HDDVDRW; + + break; + case DiskCategory.Nintendo: + dskType = decPfi.DiscSize == DVDSize.Eighty ? MediaType.GOD : MediaType.WOD; + + break; + case DiskCategory.UMD: + dskType = MediaType.UMD; + + break; + } + } + } + + _dumpLog.WriteLine("Reading Disc Manufacturing Information"); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DiscManufacturingInformation, 0, _dev.Timeout, + out _); + + if(!sense) + { + if(DMI.IsXbox(cmdBuf) || + DMI.IsXbox360(cmdBuf)) + { + if(DMI.IsXbox(cmdBuf)) + { + dskType = MediaType.XGD; + } + else if(DMI.IsXbox360(cmdBuf)) + { + dskType = MediaType.XGD2; + + // All XGD3 all have the same number of blocks + if(blocks + 1 == 25063 || // Locked (or non compatible drive) + blocks + 1 == 4229664 || // Xtreme unlock + blocks + 1 == 4246304) // Wxripper unlock + dskType = MediaType.XGD3; + } + + isXbox = true; + + sense = _dev.ScsiInquiry(out byte[] inqBuf, out _); + + if(sense || Inquiry.Decode(inqBuf)?.KreonPresent != true) + { + _dumpLog.WriteLine("Dumping Xbox Game Discs requires a drive with Kreon firmware."); StoppingErrorMessage?. - Invoke("Dumping Nintendo GameCube or Wii discs is not yet implemented."); + Invoke("Dumping Xbox Game Discs requires a drive with Kreon firmware."); + if(!_force) + return; + + isXbox = false; + } + + if(_dumpRaw && !_force) + { + StoppingErrorMessage?. + Invoke("Not continuing. If you want to continue reading cooked data when raw is not available use the force option."); + + // TODO: Exit more gracefully return; } } - break; - case MediaType.DVDDownload: - case MediaType.DVDPR: - case MediaType.DVDPRDL: - case MediaType.DVDPRW: - case MediaType.DVDPRWDL: - case MediaType.DVDR: - case MediaType.DVDRAM: - case MediaType.DVDRDL: - case MediaType.DVDROM: - case MediaType.DVDRW: - case MediaType.DVDRWDL: - case MediaType.HDDVDR: - case MediaType.HDDVDRAM: - case MediaType.HDDVDRDL: - case MediaType.HDDVDROM: - case MediaType.HDDVDRW: - case MediaType.HDDVDRWDL: - _dumpLog.WriteLine("Reading Physical Format Information"); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, _dev.Timeout, out _); - - if(!sense) - { - PFI.PhysicalFormatInformation? nullablePfi = PFI.Decode(cmdBuf, dskType); - - if(nullablePfi.HasValue) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf); - - PFI.PhysicalFormatInformation decPfi = nullablePfi.Value; - UpdateStatus?.Invoke($"PFI:\n{PFI.Prettify(decPfi)}"); - - // False book types - switch(decPfi.DiskCategory) - { - case DiskCategory.DVDPR: - dskType = MediaType.DVDPR; - - break; - case DiskCategory.DVDPRDL: - dskType = MediaType.DVDPRDL; - - break; - case DiskCategory.DVDPRW: - dskType = MediaType.DVDPRW; - - break; - case DiskCategory.DVDPRWDL: - dskType = MediaType.DVDPRWDL; - - break; - case DiskCategory.DVDR: - dskType = decPfi.PartVersion >= 6 ? MediaType.DVDRDL : MediaType.DVDR; - - break; - case DiskCategory.DVDRAM: - dskType = MediaType.DVDRAM; - - break; - default: - dskType = MediaType.DVDROM; - - break; - case DiskCategory.DVDRW: - dskType = decPfi.PartVersion >= 15 ? MediaType.DVDRWDL : MediaType.DVDRW; - - break; - case DiskCategory.HDDVDR: - dskType = MediaType.HDDVDR; - - break; - case DiskCategory.HDDVDRAM: - dskType = MediaType.HDDVDRAM; - - break; - case DiskCategory.HDDVDROM: - dskType = MediaType.HDDVDROM; - - break; - case DiskCategory.HDDVDRW: - dskType = MediaType.HDDVDRW; - - break; - case DiskCategory.Nintendo: - dskType = decPfi.DiscSize == DVDSize.Eighty ? MediaType.GOD : MediaType.WOD; - - break; - case DiskCategory.UMD: - dskType = MediaType.UMD; - - break; - } - } - } - - _dumpLog.WriteLine("Reading Disc Manufacturing Information"); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DiscManufacturingInformation, 0, _dev.Timeout, - out _); - - if(!sense) - { - if(DMI.IsXbox(cmdBuf) || - DMI.IsXbox360(cmdBuf)) - { - if(DMI.IsXbox(cmdBuf)) - { - dskType = MediaType.XGD; - } - else if(DMI.IsXbox360(cmdBuf)) - { - dskType = MediaType.XGD2; - - // All XGD3 all have the same number of blocks - if(blocks + 1 == 25063 || // Locked (or non compatible drive) - blocks + 1 == 4229664 || // Xtreme unlock - blocks + 1 == 4246304) // Wxripper unlock - dskType = MediaType.XGD3; - } - - isXbox = true; - - sense = _dev.ScsiInquiry(out byte[] inqBuf, out _); - - if(sense || Inquiry.Decode(inqBuf)?.KreonPresent != true) - { - _dumpLog.WriteLine("Dumping Xbox Game Discs requires a drive with Kreon firmware."); - - StoppingErrorMessage?. - Invoke("Dumping Xbox Game Discs requires a drive with Kreon firmware."); - - if(!_force) - return; - - isXbox = false; - } - - if(_dumpRaw && !_force) - { - StoppingErrorMessage?. - Invoke("Not continuing. If you want to continue reading cooked data when raw is not available use the force option."); - - // TODO: Exit more gracefully - return; - } - } - - if(cmdBuf.Length == 2052) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf); - } - } - - break; - } - #endregion Nintendo - - #region All DVD and HD DVD types - #endregion All DVD and HD DVD types - - #region DVD-ROM - if(dskType == MediaType.DVDDownload || - dskType == MediaType.DVDROM) - { - _dumpLog.WriteLine("Reading Lead-in Copyright Information."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.CopyrightInformation, 0, _dev.Timeout, out _); - - if(!sense) - if(CSS_CPRM.DecodeLeadInCopyright(cmdBuf).HasValue) + if(cmdBuf.Length == 2052) { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVD_CMI, tmpBuf); + mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf); + } + } - CSS_CPRM.LeadInCopyright? cmi = CSS_CPRM.DecodeLeadInCopyright(cmdBuf); + break; + } + #endregion Nintendo - if(cmi?.CopyrightType == CopyrightType.NoProtection) - UpdateStatus?.Invoke("Drive reports no copy protection on disc."); + #region All DVD and HD DVD types + #endregion All DVD and HD DVD types + + #region DVD-ROM + if(dskType == MediaType.DVDDownload || + dskType == MediaType.DVDROM) + { + _dumpLog.WriteLine("Reading Lead-in Copyright Information."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.CopyrightInformation, 0, _dev.Timeout, out _); + + if(!sense) + if(CSS_CPRM.DecodeLeadInCopyright(cmdBuf).HasValue) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.DVD_CMI, tmpBuf); + + CSS_CPRM.LeadInCopyright? cmi = CSS_CPRM.DecodeLeadInCopyright(cmdBuf); + + if(cmi?.CopyrightType == CopyrightType.NoProtection) + UpdateStatus?.Invoke("Drive reports no copy protection on disc."); + else + { + if(!Settings.Settings.Current.EnableDecryption) + UpdateStatus?.Invoke("Drive reports the disc uses copy protection. " + + "The dump will be incorrect unless decryption is enabled."); else { - if(!Settings.Settings.Current.EnableDecryption) - UpdateStatus?.Invoke("Drive reports the disc uses copy protection. " + - "The dump will be incorrect unless decryption is enabled."); - else + if(cmi?.CopyrightType == CopyrightType.CSS) { - if(cmi?.CopyrightType == CopyrightType.CSS) + UpdateStatus?.Invoke("Drive reports disc uses CSS copy protection."); + + dvdDecrypt = new DVDDecryption(_dev); + + sense = dvdDecrypt.ReadBusKey(out cmdBuf, out _, + CSS_CPRM.DecodeLeadInCopyright(cmdBuf)?. + CopyrightType ?? CopyrightType.NoProtection, + _dev.Timeout, out _); + + if(!sense) { - UpdateStatus?.Invoke("Drive reports disc uses CSS copy protection."); + byte[] busKey = cmdBuf; - dvdDecrypt = new DVDDecryption(_dev); - - sense = dvdDecrypt.ReadBusKey(out cmdBuf, out _, - CSS_CPRM.DecodeLeadInCopyright(cmdBuf)?. - CopyrightType ?? CopyrightType.NoProtection, - _dev.Timeout, out _); + UpdateStatus?.Invoke("Reading disc key."); + sense = dvdDecrypt.ReadDiscKey(out cmdBuf, out _, _dev.Timeout, out _); if(!sense) { - byte[] busKey = cmdBuf; + CSS_CPRM.DiscKey? decodedDiscKey = CSS.DecodeDiscKey(cmdBuf, busKey); - UpdateStatus?.Invoke("Reading disc key."); - sense = dvdDecrypt.ReadDiscKey(out cmdBuf, out _, _dev.Timeout, out _); + sense = dvdDecrypt.ReadAsf(out cmdBuf, out _, + DvdCssKeyClass.DvdCssCppmOrCprm, _dev.Timeout, + out _); if(!sense) { - CSS_CPRM.DiscKey? decodedDiscKey = CSS.DecodeDiscKey(cmdBuf, busKey); - - sense = dvdDecrypt.ReadAsf(out cmdBuf, out _, - DvdCssKeyClass.DvdCssCppmOrCprm, _dev.Timeout, - out _); - - if(!sense) + if(cmdBuf[7] == 1) { - if(cmdBuf[7] == 1) + UpdateStatus?.Invoke("Disc and drive authentication succeeded."); + + sense = dvdDecrypt.ReadRpc(out cmdBuf, out _, + DvdCssKeyClass.DvdCssCppmOrCprm, + _dev.Timeout, out _); + + if(!sense) { - UpdateStatus?.Invoke("Disc and drive authentication succeeded."); + CSS_CPRM.RegionalPlaybackControlState? rpc = + CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf); - sense = dvdDecrypt.ReadRpc(out cmdBuf, out _, - DvdCssKeyClass.DvdCssCppmOrCprm, - _dev.Timeout, out _); - - if(!sense) + if(rpc.HasValue) { - CSS_CPRM.RegionalPlaybackControlState? rpc = - CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf); - - if(rpc.HasValue) - { - UpdateStatus?.Invoke(CSS.CheckRegion(rpc.Value, cmi.Value) - ? "Disc and drive regions match." - : "Disc and drive regions do not match. The dump will be incorrect"); - } + UpdateStatus?.Invoke(CSS.CheckRegion(rpc.Value, cmi.Value) + ? "Disc and drive regions match." + : "Disc and drive regions do not match. The dump will be incorrect"); } + } - if(decodedDiscKey.HasValue) + if(decodedDiscKey.HasValue) + { + mediaTags.Add(MediaTagType.DVD_DiscKey, + decodedDiscKey.Value.Key); + + UpdateStatus?.Invoke("Decrypting disc key."); + + CSS.DecryptDiscKey(decodedDiscKey.Value.Key, + out byte[] discKey); + + if(discKey != null) { - mediaTags.Add(MediaTagType.DVD_DiscKey, - decodedDiscKey.Value.Key); - - UpdateStatus?.Invoke("Decrypting disc key."); - - CSS.DecryptDiscKey(decodedDiscKey.Value.Key, - out byte[] discKey); - - if(discKey != null) - { - UpdateStatus?.Invoke("Decryption of disc key succeeded."); - mediaTags.Add(MediaTagType.DVD_DiscKey_Decrypted, discKey); - } - else - UpdateStatus?.Invoke("Decryption of disc key failed."); + UpdateStatus?.Invoke("Decryption of disc key succeeded."); + mediaTags.Add(MediaTagType.DVD_DiscKey_Decrypted, discKey); } + else + UpdateStatus?.Invoke("Decryption of disc key failed."); } } } } } - else - { - UpdateStatus?. - Invoke($"Drive reports disc uses {(CSS_CPRM.DecodeLeadInCopyright(cmdBuf)?.CopyrightType ?? CopyrightType.NoProtection).ToString()} copy protection. " + - "This is not yet supported and the dump will be incorrect."); - } + } + else + { + UpdateStatus?. + Invoke($"Drive reports disc uses {(CSS_CPRM.DecodeLeadInCopyright(cmdBuf)?.CopyrightType ?? CopyrightType.NoProtection).ToString()} copy protection. " + + "This is not yet supported and the dump will be incorrect."); } } } - } - #endregion DVD-ROM + } + } + #endregion DVD-ROM - switch(dskType) - { - #region DVD-ROM and HD DVD-ROM - case MediaType.DVDDownload: - case MediaType.DVDROM: - case MediaType.HDDVDROM: - _dumpLog.WriteLine("Reading Burst Cutting Area."); + switch(dskType) + { + #region DVD-ROM and HD DVD-ROM + case MediaType.DVDDownload: + case MediaType.DVDROM: + case MediaType.HDDVDROM: + _dumpLog.WriteLine("Reading Burst Cutting Area."); - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.BurstCuttingArea, 0, _dev.Timeout, out _); + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.BurstCuttingArea, 0, _dev.Timeout, out _); - if(!sense) + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.DVD_BCA, tmpBuf); + } + + break; + #endregion DVD-ROM and HD DVD-ROM + + #region DVD-RAM and HD DVD-RAM + case MediaType.DVDRAM: + case MediaType.HDDVDRAM: + _dumpLog.WriteLine("Reading Disc Description Structure."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdramDds, 0, _dev.Timeout, out _); + + if(!sense) + if(DDS.Decode(cmdBuf).HasValue) { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVD_BCA, tmpBuf); + mediaTags.Add(MediaTagType.DVDRAM_DDS, tmpBuf); } - break; - #endregion DVD-ROM and HD DVD-ROM + _dumpLog.WriteLine("Reading Spare Area Information."); - #region DVD-RAM and HD DVD-RAM - case MediaType.DVDRAM: - case MediaType.HDDVDRAM: - _dumpLog.WriteLine("Reading Disc Description Structure."); + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdramSpareAreaInformation, 0, _dev.Timeout, + out _); - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdramDds, 0, _dev.Timeout, out _); - - if(!sense) - if(DDS.Decode(cmdBuf).HasValue) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVDRAM_DDS, tmpBuf); - } - - _dumpLog.WriteLine("Reading Spare Area Information."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdramSpareAreaInformation, 0, _dev.Timeout, - out _); - - if(!sense) - if(Spare.Decode(cmdBuf).HasValue) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVDRAM_SpareArea, tmpBuf); - } - - break; - #endregion DVD-RAM and HD DVD-RAM - - #region DVD-R and DVD-RW - case MediaType.DVDR: - case MediaType.DVDRW: - _dumpLog.WriteLine("Reading Pre-Recorded Information."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PreRecordedInfo, 0, _dev.Timeout, out _); - - if(!sense) + if(!sense) + if(Spare.Decode(cmdBuf).HasValue) { tmpBuf = new byte[cmdBuf.Length - 4]; Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVDR_PreRecordedInfo, tmpBuf); + mediaTags.Add(MediaTagType.DVDRAM_SpareArea, tmpBuf); } - break; - #endregion DVD-R and DVD-RW - } + break; + #endregion DVD-RAM and HD DVD-RAM - switch(dskType) - { - #region DVD-R, DVD-RW and HD DVD-R - case MediaType.DVDR: - case MediaType.DVDRW: - case MediaType.HDDVDR: - _dumpLog.WriteLine("Reading Media Identifier."); + #region DVD-R and DVD-RW + case MediaType.DVDR: + case MediaType.DVDRW: + _dumpLog.WriteLine("Reading Pre-Recorded Information."); - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdrMediaIdentifier, 0, _dev.Timeout, out _); + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PreRecordedInfo, 0, _dev.Timeout, out _); - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVDR_MediaIdentifier, tmpBuf); - } + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.DVDR_PreRecordedInfo, tmpBuf); + } - _dumpLog.WriteLine("Reading Recordable Physical Information."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdrPhysicalInformation, 0, _dev.Timeout, - out _); - - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVDR_PFI, tmpBuf); - } - - break; - #endregion DVD-R, DVD-RW and HD DVD-R - - #region All DVD+ - case MediaType.DVDPR: - case MediaType.DVDPRDL: - case MediaType.DVDPRW: - case MediaType.DVDPRWDL: - _dumpLog.WriteLine("Reading ADdress In Pregroove."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.Adip, 0, _dev.Timeout, out _); - - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DVD_ADIP, tmpBuf); - } - - _dumpLog.WriteLine("Reading Disc Control Blocks."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.Dcb, 0, _dev.Timeout, out _); - - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.DCB, tmpBuf); - } - - break; - #endregion All DVD+ - - #region HD DVD-ROM - case MediaType.HDDVDROM: - _dumpLog.WriteLine("Reading Lead-in Copyright Information."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.HddvdCopyrightInformation, 0, _dev.Timeout, - out _); - - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.HDDVD_CPI, tmpBuf); - } - - break; - #endregion HD DVD-ROM - - #region All Blu-ray - case MediaType.BDR: - case MediaType.BDRE: - case MediaType.BDROM: - case MediaType.BDRXL: - case MediaType.BDREXL: - case MediaType.UHDBD: - _dumpLog.WriteLine("Reading Disc Information."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.DiscInformation, 0, _dev.Timeout, out _); - - if(!sense) - if(DI.Decode(cmdBuf).HasValue) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.BD_DI, tmpBuf); - } - - // TODO: PAC - /* - dumpLog.WriteLine("Reading PAC."); - sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.Pac, 0, dev.Timeout, out _); - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.PAC, tmpBuf); - }*/ - break; - #endregion All Blu-ray - } - - switch(dskType) - { - #region BD-ROM only - case MediaType.BDROM: - case MediaType.UHDBD: - _dumpLog.WriteLine("Reading Burst Cutting Area."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdBurstCuttingArea, 0, _dev.Timeout, out _); - - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.BD_BCA, tmpBuf); - } - - break; - #endregion BD-ROM only - - #region Writable Blu-ray only - case MediaType.BDR: - case MediaType.BDRE: - case MediaType.BDRXL: - case MediaType.BDREXL: - _dumpLog.WriteLine("Reading Disc Definition Structure."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdDds, 0, _dev.Timeout, out _); - - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.BD_DDS, tmpBuf); - } - - _dumpLog.WriteLine("Reading Spare Area Information."); - - sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdSpareAreaInformation, 0, _dev.Timeout, - out _); - - if(!sense) - { - tmpBuf = new byte[cmdBuf.Length - 4]; - Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); - mediaTags.Add(MediaTagType.BD_SpareArea, tmpBuf); - } - - break; - #endregion Writable Blu-ray only - } - - if(isXbox) - { - Xgd(mediaTags, dskType); - - return; - } - - Sbc(mediaTags, dskType, true, dvdDecrypt); + break; + #endregion DVD-R and DVD-RW } - // TODO: Move somewhere else - internal static void AddMediaTagToSidecar(string outputPath, MediaTagType tagType, byte[] tag, - ref CICMMetadataType sidecar) + switch(dskType) { - switch(tagType) - { - case MediaTagType.DVD_PFI: - sidecar.OpticalDisc[0].PFI = new DumpType + #region DVD-R, DVD-RW and HD DVD-R + case MediaType.DVDR: + case MediaType.DVDRW: + case MediaType.HDDVDR: + _dumpLog.WriteLine("Reading Media Identifier."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdrMediaIdentifier, 0, _dev.Timeout, out _); + + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.DVDR_MediaIdentifier, tmpBuf); + } + + _dumpLog.WriteLine("Reading Recordable Physical Information."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdrPhysicalInformation, 0, _dev.Timeout, + out _); + + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.DVDR_PFI, tmpBuf); + } + + break; + #endregion DVD-R, DVD-RW and HD DVD-R + + #region All DVD+ + case MediaType.DVDPR: + case MediaType.DVDPRDL: + case MediaType.DVDPRW: + case MediaType.DVDPRWDL: + _dumpLog.WriteLine("Reading ADdress In Pregroove."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.Adip, 0, _dev.Timeout, out _); + + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.DVD_ADIP, tmpBuf); + } + + _dumpLog.WriteLine("Reading Disc Control Blocks."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.Dcb, 0, _dev.Timeout, out _); + + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.DCB, tmpBuf); + } + + break; + #endregion All DVD+ + + #region HD DVD-ROM + case MediaType.HDDVDROM: + _dumpLog.WriteLine("Reading Lead-in Copyright Information."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.HddvdCopyrightInformation, 0, _dev.Timeout, + out _); + + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.HDDVD_CPI, tmpBuf); + } + + break; + #endregion HD DVD-ROM + + #region All Blu-ray + case MediaType.BDR: + case MediaType.BDRE: + case MediaType.BDROM: + case MediaType.BDRXL: + case MediaType.BDREXL: + case MediaType.UHDBD: + _dumpLog.WriteLine("Reading Disc Information."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.DiscInformation, 0, _dev.Timeout, out _); + + if(!sense) + if(DI.Decode(cmdBuf).HasValue) { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.BD_DI, tmpBuf); + } - break; - case MediaTagType.DVD_DMI: - sidecar.OpticalDisc[0].DMI = new DumpType + // TODO: PAC + /* + dumpLog.WriteLine("Reading PAC."); + sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.Pac, 0, dev.Timeout, out _); + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.PAC, tmpBuf); + }*/ + break; + #endregion All Blu-ray + } + + switch(dskType) + { + #region BD-ROM only + case MediaType.BDROM: + case MediaType.UHDBD: + _dumpLog.WriteLine("Reading Burst Cutting Area."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdBurstCuttingArea, 0, _dev.Timeout, out _); + + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.BD_BCA, tmpBuf); + } + + break; + #endregion BD-ROM only + + #region Writable Blu-ray only + case MediaType.BDR: + case MediaType.BDRE: + case MediaType.BDRXL: + case MediaType.BDREXL: + _dumpLog.WriteLine("Reading Disc Definition Structure."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdDds, 0, _dev.Timeout, out _); + + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.BD_DDS, tmpBuf); + } + + _dumpLog.WriteLine("Reading Spare Area Information."); + + sense = _dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdSpareAreaInformation, 0, _dev.Timeout, + out _); + + if(!sense) + { + tmpBuf = new byte[cmdBuf.Length - 4]; + Array.Copy(cmdBuf, 4, tmpBuf, 0, cmdBuf.Length - 4); + mediaTags.Add(MediaTagType.BD_SpareArea, tmpBuf); + } + + break; + #endregion Writable Blu-ray only + } + + if(isXbox) + { + Xgd(mediaTags, dskType); + + return; + } + + Sbc(mediaTags, dskType, true, dvdDecrypt); + } + + // TODO: Move somewhere else + internal static void AddMediaTagToSidecar(string outputPath, MediaTagType tagType, byte[] tag, + ref CICMMetadataType sidecar) + { + switch(tagType) + { + case MediaTagType.DVD_PFI: + sidecar.OpticalDisc[0].PFI = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.DVD_DMI: + sidecar.OpticalDisc[0].DMI = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.DVD_CMI: + case MediaTagType.HDDVD_CPI: + sidecar.OpticalDisc[0].CMI = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + byte[] tmp = new byte[tag.Length + 4]; + Array.Copy(tag, 0, tmp, 4, tag.Length); + tmp[0] = (byte)((tag.Length & 0xFF00) >> 8); + tmp[1] = (byte)(tag.Length & 0xFF); + + CSS_CPRM.LeadInCopyright? cpy = CSS_CPRM.DecodeLeadInCopyright(tmp); + + if(cpy.HasValue && + cpy.Value.CopyrightType != CopyrightType.NoProtection) + sidecar.OpticalDisc[0].CopyProtection = cpy.Value.CopyrightType.ToString(); + + break; + case MediaTagType.DVD_BCA: + case MediaTagType.BD_BCA: + sidecar.OpticalDisc[0].BCA = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.BD_DDS: + case MediaTagType.DVDRAM_DDS: + sidecar.OpticalDisc[0].DDS = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.DVDRAM_SpareArea: + case MediaTagType.BD_SpareArea: + sidecar.OpticalDisc[0].SAI = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.DVDR_PreRecordedInfo: + sidecar.OpticalDisc[0].PRI = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.DVD_MediaIdentifier: + sidecar.OpticalDisc[0].MediaID = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.DVDR_PFI: + sidecar.OpticalDisc[0].PFIR = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.DVD_ADIP: + sidecar.OpticalDisc[0].ADIP = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.DCB: + sidecar.OpticalDisc[0].DCB = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.BD_DI: + sidecar.OpticalDisc[0].DI = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.Xbox_SecuritySector: + sidecar.OpticalDisc[0].Xbox ??= new XboxType(); + + sidecar.OpticalDisc[0].Xbox.SecuritySectors = new[] + { + new XboxSecuritySectorsType { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.DVD_CMI: - case MediaTagType.HDDVD_CPI: - sidecar.OpticalDisc[0].CMI = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - byte[] tmp = new byte[tag.Length + 4]; - Array.Copy(tag, 0, tmp, 4, tag.Length); - tmp[0] = (byte)((tag.Length & 0xFF00) >> 8); - tmp[1] = (byte)(tag.Length & 0xFF); - - CSS_CPRM.LeadInCopyright? cpy = CSS_CPRM.DecodeLeadInCopyright(tmp); - - if(cpy.HasValue && - cpy.Value.CopyrightType != CopyrightType.NoProtection) - sidecar.OpticalDisc[0].CopyProtection = cpy.Value.CopyrightType.ToString(); - - break; - case MediaTagType.DVD_BCA: - case MediaTagType.BD_BCA: - sidecar.OpticalDisc[0].BCA = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.BD_DDS: - case MediaTagType.DVDRAM_DDS: - sidecar.OpticalDisc[0].DDS = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.DVDRAM_SpareArea: - case MediaTagType.BD_SpareArea: - sidecar.OpticalDisc[0].SAI = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.DVDR_PreRecordedInfo: - sidecar.OpticalDisc[0].PRI = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.DVD_MediaIdentifier: - sidecar.OpticalDisc[0].MediaID = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.DVDR_PFI: - sidecar.OpticalDisc[0].PFIR = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.DVD_ADIP: - sidecar.OpticalDisc[0].ADIP = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.DCB: - sidecar.OpticalDisc[0].DCB = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.BD_DI: - sidecar.OpticalDisc[0].DI = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.Xbox_SecuritySector: - sidecar.OpticalDisc[0].Xbox ??= new XboxType(); - - sidecar.OpticalDisc[0].Xbox.SecuritySectors = new[] - { - new XboxSecuritySectorsType - { - RequestNumber = 0, - RequestVersion = 1, - SecuritySectors = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - } - } - }; - - break; - case MediaTagType.Xbox_PFI: - sidecar.OpticalDisc[0].Xbox ??= new XboxType(); - - sidecar.OpticalDisc[0].Xbox.PFI = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.Xbox_DMI: - sidecar.OpticalDisc[0].Xbox ??= new XboxType(); - - sidecar.OpticalDisc[0].Xbox.DMI = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.CD_FullTOC: - sidecar.OpticalDisc[0].TOC = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.CD_ATIP: - sidecar.OpticalDisc[0].ATIP = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.CD_PMA: - sidecar.OpticalDisc[0].PMA = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.CD_TEXT: - sidecar.OpticalDisc[0].LeadInCdText = new DumpType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - }; - - break; - case MediaTagType.CD_FirstTrackPregap: - sidecar.OpticalDisc[0].FirstTrackPregrap = new[] - { - new BorderType + RequestNumber = 0, + RequestVersion = 1, + SecuritySectors = new DumpType { Image = outputPath, Size = (ulong)tag.Length, Checksums = Checksum.GetChecksums(tag).ToArray() } - }; + } + }; - break; - case MediaTagType.CD_LeadIn: - sidecar.OpticalDisc[0].LeadIn = new[] + break; + case MediaTagType.Xbox_PFI: + sidecar.OpticalDisc[0].Xbox ??= new XboxType(); + + sidecar.OpticalDisc[0].Xbox.PFI = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.Xbox_DMI: + sidecar.OpticalDisc[0].Xbox ??= new XboxType(); + + sidecar.OpticalDisc[0].Xbox.DMI = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.CD_FullTOC: + sidecar.OpticalDisc[0].TOC = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.CD_ATIP: + sidecar.OpticalDisc[0].ATIP = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.CD_PMA: + sidecar.OpticalDisc[0].PMA = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.CD_TEXT: + sidecar.OpticalDisc[0].LeadInCdText = new DumpType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + }; + + break; + case MediaTagType.CD_FirstTrackPregap: + sidecar.OpticalDisc[0].FirstTrackPregrap = new[] + { + new BorderType { - new BorderType - { - Image = outputPath, - Size = (ulong)tag.Length, - Checksums = Checksum.GetChecksums(tag).ToArray() - } - }; + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + } + }; - break; - } + break; + case MediaTagType.CD_LeadIn: + sidecar.OpticalDisc[0].LeadIn = new[] + { + new BorderType + { + Image = outputPath, + Size = (ulong)tag.Length, + Checksums = Checksum.GetChecksums(tag).ToArray() + } + }; + + break; } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/Metadata.cs b/Aaru.Core/Devices/Dumping/Metadata.cs index 5fcc5fdf8..2fe54bc0f 100644 --- a/Aaru.Core/Devices/Dumping/Metadata.cs +++ b/Aaru.Core/Devices/Dumping/Metadata.cs @@ -42,109 +42,108 @@ using Aaru.CommonTypes.Metadata; using Schemas; using MediaType = Aaru.CommonTypes.MediaType; -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Creates optical metadata sidecar + /// Size of the read sector in bytes + /// Total number of positive sectors + /// Disc type + /// Disc layers + /// Media tags + /// Disc sessions + /// Total time spent doing checksums + /// Disc write offset + void WriteOpticalSidecar(uint blockSize, ulong blocks, MediaType mediaType, LayersType layers, + Dictionary mediaTags, int sessions, out double totalChkDuration, + int? discOffset) { - /// Creates optical metadata sidecar - /// Size of the read sector in bytes - /// Total number of positive sectors - /// Disc type - /// Disc layers - /// Media tags - /// Disc sessions - /// Total time spent doing checksums - /// Disc write offset - void WriteOpticalSidecar(uint blockSize, ulong blocks, MediaType mediaType, LayersType layers, - Dictionary mediaTags, int sessions, out double totalChkDuration, - int? discOffset) + _dumpLog.WriteLine("Creating sidecar."); + var filters = new FiltersList(); + IFilter filter = filters.GetFilter(_outputPath); + IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; + totalChkDuration = 0; + ErrorNumber opened = inputPlugin.Open(filter); + + if(opened != ErrorNumber.NoError) { - _dumpLog.WriteLine("Creating sidecar."); - var filters = new FiltersList(); - IFilter filter = filters.GetFilter(_outputPath); - IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; - totalChkDuration = 0; - ErrorNumber opened = inputPlugin.Open(filter); + StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); - if(opened != ErrorNumber.NoError) - { - StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); - - return; - } - - DateTime chkStart = DateTime.UtcNow; - - // ReSharper disable once UseObjectOrCollectionInitializer - _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); - _sidecarClass.InitProgressEvent += InitProgress; - _sidecarClass.UpdateProgressEvent += UpdateProgress; - _sidecarClass.EndProgressEvent += EndProgress; - _sidecarClass.InitProgressEvent2 += InitProgress2; - _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; - _sidecarClass.EndProgressEvent2 += EndProgress2; - _sidecarClass.UpdateStatusEvent += UpdateStatus; - CICMMetadataType sidecar = _sidecarClass.Create(); - DateTime end = DateTime.UtcNow; - - if(_aborted) - return; - - totalChkDuration = (end - chkStart).TotalMilliseconds; - _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); - - _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); - - if(_preSidecar != null) - { - _preSidecar.OpticalDisc = sidecar.OpticalDisc; - sidecar = _preSidecar; - } - - List<(ulong start, string type)> filesystems = new(); - - if(sidecar.OpticalDisc[0].Track != null) - filesystems.AddRange(from xmlTrack in sidecar.OpticalDisc[0].Track - where xmlTrack.FileSystemInformation != null - from partition in xmlTrack.FileSystemInformation - where partition.FileSystems != null from fileSystem in partition.FileSystems - select (partition.StartSector, fileSystem.Type)); - - if(filesystems.Count > 0) - foreach(var filesystem in filesystems.Select(o => new - { - o.start, - o.type - }).Distinct()) - _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); - - sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(mediaType); - (string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType); - sidecar.OpticalDisc[0].DiscType = discType.type; - sidecar.OpticalDisc[0].DiscSubType = discType.subType; - sidecar.OpticalDisc[0].DumpHardwareArray = _resume.Tries.ToArray(); - sidecar.OpticalDisc[0].Sessions = (uint)sessions; - sidecar.OpticalDisc[0].Layers = layers; - - if(discOffset.HasValue) - { - sidecar.OpticalDisc[0].Offset = (int)(discOffset / 4); - sidecar.OpticalDisc[0].OffsetSpecified = true; - } - - if(mediaTags != null) - foreach(KeyValuePair tag in mediaTags.Where(tag => _outputPlugin. - SupportedMediaTags.Contains(tag.Key))) - AddMediaTagToSidecar(_outputPath, tag.Key, tag.Value, ref sidecar); - - UpdateStatus?.Invoke("Writing metadata sidecar"); - - var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - - var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); - xmlSer.Serialize(xmlFs, sidecar); - xmlFs.Close(); + return; } + + DateTime chkStart = DateTime.UtcNow; + + // ReSharper disable once UseObjectOrCollectionInitializer + _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); + _sidecarClass.InitProgressEvent += InitProgress; + _sidecarClass.UpdateProgressEvent += UpdateProgress; + _sidecarClass.EndProgressEvent += EndProgress; + _sidecarClass.InitProgressEvent2 += InitProgress2; + _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; + _sidecarClass.EndProgressEvent2 += EndProgress2; + _sidecarClass.UpdateStatusEvent += UpdateStatus; + CICMMetadataType sidecar = _sidecarClass.Create(); + DateTime end = DateTime.UtcNow; + + if(_aborted) + return; + + totalChkDuration = (end - chkStart).TotalMilliseconds; + _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); + + _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + + if(_preSidecar != null) + { + _preSidecar.OpticalDisc = sidecar.OpticalDisc; + sidecar = _preSidecar; + } + + List<(ulong start, string type)> filesystems = new(); + + if(sidecar.OpticalDisc[0].Track != null) + filesystems.AddRange(from xmlTrack in sidecar.OpticalDisc[0].Track + where xmlTrack.FileSystemInformation != null + from partition in xmlTrack.FileSystemInformation + where partition.FileSystems != null from fileSystem in partition.FileSystems + select (partition.StartSector, fileSystem.Type)); + + if(filesystems.Count > 0) + foreach(var filesystem in filesystems.Select(o => new + { + o.start, + o.type + }).Distinct()) + _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); + + sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(mediaType); + (string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(mediaType); + sidecar.OpticalDisc[0].DiscType = discType.type; + sidecar.OpticalDisc[0].DiscSubType = discType.subType; + sidecar.OpticalDisc[0].DumpHardwareArray = _resume.Tries.ToArray(); + sidecar.OpticalDisc[0].Sessions = (uint)sessions; + sidecar.OpticalDisc[0].Layers = layers; + + if(discOffset.HasValue) + { + sidecar.OpticalDisc[0].Offset = (int)(discOffset / 4); + sidecar.OpticalDisc[0].OffsetSpecified = true; + } + + if(mediaTags != null) + foreach(KeyValuePair tag in mediaTags.Where(tag => _outputPlugin. + SupportedMediaTags.Contains(tag.Key))) + AddMediaTagToSidecar(_outputPath, tag.Key, tag.Value, ref sidecar); + + UpdateStatus?.Invoke("Writing metadata sidecar"); + + var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); + + var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/MiniDisc.cs b/Aaru.Core/Devices/Dumping/MiniDisc.cs index 98b1c83fd..38c3f4ad2 100644 --- a/Aaru.Core/Devices/Dumping/MiniDisc.cs +++ b/Aaru.Core/Devices/Dumping/MiniDisc.cs @@ -50,284 +50,400 @@ using Version = Aaru.CommonTypes.Interop.Version; // ReSharper disable JoinDeclarationAndInitializer -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Implements dumping MiniDisc Data devices +partial class Dump { - /// Implements dumping MiniDisc Data devices - partial class Dump + /// Dumps a MiniDisc Data device + void MiniDisc() { - /// Dumps a MiniDisc Data device - void MiniDisc() + bool sense; + byte scsiMediumType = 0; + const ushort sbcProfile = 0x0001; + DateTime start; + DateTime end; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + byte[] readBuffer; + Modes.DecodedMode? decMode = null; + Dictionary mediaTags = new(); + byte[] cmdBuf; + bool ret; + var outputFormat = _outputPlugin as IWritableImage; + + _dumpLog.WriteLine("Initializing reader."); + var scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog); + ulong blocks = scsiReader.GetDeviceBlocks(); + uint blockSize = scsiReader.LogicalBlockSize; + + _dumpLog.WriteLine("Requesting MODE SENSE (6)."); + UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); + + sense = _dev.ModeSense6(out cmdBuf, out _, true, ScsiModeSensePageControl.Current, 0x3F, 5, out _); + + if(!sense && + !_dev.Error && + Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) + decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType); + + if(decMode.HasValue) + scsiMediumType = (byte)decMode.Value.Header.MediumType; + + if(blockSize != 2048) { - bool sense; - byte scsiMediumType = 0; - const ushort sbcProfile = 0x0001; - DateTime start; - DateTime end; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; - byte[] readBuffer; - Modes.DecodedMode? decMode = null; - Dictionary mediaTags = new(); - byte[] cmdBuf; - bool ret; - var outputFormat = _outputPlugin as IWritableImage; + _dumpLog.WriteLine("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); - _dumpLog.WriteLine("Initializing reader."); - var scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog); - ulong blocks = scsiReader.GetDeviceBlocks(); - uint blockSize = scsiReader.LogicalBlockSize; + StoppingErrorMessage?. + Invoke("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); - _dumpLog.WriteLine("Requesting MODE SENSE (6)."); - UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); + return; + } - sense = _dev.ModeSense6(out cmdBuf, out _, true, ScsiModeSensePageControl.Current, 0x3F, 5, out _); + MediaType dskType = MediaType.MDData; - if(!sense && - !_dev.Error && - Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) - decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType); + if(scsiReader.FindReadCommand()) + { + _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage); + StoppingErrorMessage?.Invoke("Unable to read medium."); - if(decMode.HasValue) - scsiMediumType = (byte)decMode.Value.Header.MediumType; + return; + } - if(blockSize != 2048) + if(blocks != 0 && + blockSize != 0) + { + blocks++; + + ulong totalSize = blocks * blockSize; + + if(totalSize > 1099511627776) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); + else if(totalSize > 1073741824) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); + else if(totalSize > 1048576) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); + else if(totalSize > 1024) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); + else + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); + } + + // Check how many blocks to read, if error show and return + // 64 works, gets maximum speed (150KiB/s), slow I know... + if(scsiReader.GetBlocksToRead()) + { + _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage); + StoppingErrorMessage?.Invoke(scsiReader.ErrorMessage); + + return; + } + + uint blocksToRead = scsiReader.BlocksToRead; + uint logicalBlockSize = blockSize; + uint physicalBlockSize = scsiReader.PhysicalBlockSize; + + if(blocks == 0) + { + _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); + StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); + + return; + } + + UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); + UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); + UpdateStatus?.Invoke($"Device reports {scsiReader.LongBlockSize} bytes per physical block."); + UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); + UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumType}."); + UpdateStatus?.Invoke($"Media identified as {dskType}"); + + _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); + _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); + _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); + _dumpLog.WriteLine("Device reports {0} bytes per physical block.", scsiReader.LongBlockSize); + _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); + _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumType); + _dumpLog.WriteLine("Media identified as {0}.", dskType); + + sense = _dev.MiniDiscGetType(out cmdBuf, out _, _dev.Timeout, out _); + + if(!sense && + !_dev.Error) + mediaTags.Add(MediaTagType.MiniDiscType, cmdBuf); + + sense = _dev.MiniDiscD5(out cmdBuf, out _, _dev.Timeout, out _); + + if(!sense && + !_dev.Error) + mediaTags.Add(MediaTagType.MiniDiscD5, cmdBuf); + + sense = _dev.MiniDiscReadDataTOC(out cmdBuf, out _, _dev.Timeout, out _); + + if(!sense && + !_dev.Error) + mediaTags.Add(MediaTagType.MiniDiscDTOC, cmdBuf); + + var utocMs = new MemoryStream(); + + for(uint i = 0; i < 3; i++) + { + sense = _dev.MiniDiscReadUserTOC(out cmdBuf, out _, i, _dev.Timeout, out _); + + if(sense || _dev.Error) + break; + + utocMs.Write(cmdBuf, 0, 2336); + } + + if(utocMs.Length > 0) + mediaTags.Add(MediaTagType.MiniDiscUTOC, utocMs.ToArray()); + + ret = true; + + foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputFormat.SupportedMediaTags.Contains(tag))) + { + ret = false; + _dumpLog.WriteLine($"Output format does not support {tag}."); + ErrorMessage?.Invoke($"Output format does not support {tag}."); + } + + if(!ret) + { + if(_force) { - _dumpLog.WriteLine("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); - - StoppingErrorMessage?. - Invoke("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); + _dumpLog.WriteLine("Several media tags not supported, continuing..."); + ErrorMessage?.Invoke("Several media tags not supported, continuing..."); + } + else + { + _dumpLog.WriteLine("Several media tags not supported, not continuing..."); + StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); return; } + } - MediaType dskType = MediaType.MDData; + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + _dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); - if(scsiReader.FindReadCommand()) - { - _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage); - StoppingErrorMessage?.Invoke("Unable to read medium."); + var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); + var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile); + ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); - return; - } + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputFormat.ErrorMessage); - if(blocks != 0 && - blockSize != 0) - { - blocks++; + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputFormat.ErrorMessage); - ulong totalSize = blocks * blockSize; + return; + } - if(totalSize > 1099511627776) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); - else if(totalSize > 1073741824) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); - else if(totalSize > 1048576) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); - else if(totalSize > 1024) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); - else - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); - } + start = DateTime.UtcNow; + double imageWriteDuration = 0; - // Check how many blocks to read, if error show and return - // 64 works, gets maximum speed (150KiB/s), slow I know... - if(scsiReader.GetBlocksToRead()) - { - _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage); - StoppingErrorMessage?.Invoke(scsiReader.ErrorMessage); + if(decMode?.Pages != null) + { + bool setGeometry = false; - return; - } - - uint blocksToRead = scsiReader.BlocksToRead; - uint logicalBlockSize = blockSize; - uint physicalBlockSize = scsiReader.PhysicalBlockSize; - - if(blocks == 0) - { - _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); - StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); - - return; - } - - UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); - UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); - UpdateStatus?.Invoke($"Device reports {scsiReader.LongBlockSize} bytes per physical block."); - UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); - UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumType}."); - UpdateStatus?.Invoke($"Media identified as {dskType}"); - - _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); - _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); - _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); - _dumpLog.WriteLine("Device reports {0} bytes per physical block.", scsiReader.LongBlockSize); - _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); - _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumType); - _dumpLog.WriteLine("Media identified as {0}.", dskType); - - sense = _dev.MiniDiscGetType(out cmdBuf, out _, _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - mediaTags.Add(MediaTagType.MiniDiscType, cmdBuf); - - sense = _dev.MiniDiscD5(out cmdBuf, out _, _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - mediaTags.Add(MediaTagType.MiniDiscD5, cmdBuf); - - sense = _dev.MiniDiscReadDataTOC(out cmdBuf, out _, _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - mediaTags.Add(MediaTagType.MiniDiscDTOC, cmdBuf); - - var utocMs = new MemoryStream(); - - for(uint i = 0; i < 3; i++) - { - sense = _dev.MiniDiscReadUserTOC(out cmdBuf, out _, i, _dev.Timeout, out _); - - if(sense || _dev.Error) - break; - - utocMs.Write(cmdBuf, 0, 2336); - } - - if(utocMs.Length > 0) - mediaTags.Add(MediaTagType.MiniDiscUTOC, utocMs.ToArray()); - - ret = true; - - foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputFormat.SupportedMediaTags.Contains(tag))) - { - ret = false; - _dumpLog.WriteLine($"Output format does not support {tag}."); - ErrorMessage?.Invoke($"Output format does not support {tag}."); - } - - if(!ret) - { - if(_force) + foreach(Modes.ModePage page in decMode.Value.Pages) + if(page.Page == 0x04 && + page.Subpage == 0x00) { - _dumpLog.WriteLine("Several media tags not supported, continuing..."); - ErrorMessage?.Invoke("Several media tags not supported, continuing..."); + Modes.ModePage_04? rigidPage = Modes.DecodeModePage_04(page.PageResponse); + + if(!rigidPage.HasValue || setGeometry) + continue; + + _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", + rigidPage.Value.Cylinders, rigidPage.Value.Heads, + (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); + + UpdateStatus?. + Invoke($"Setting geometry to {rigidPage.Value.Cylinders} cylinders, {rigidPage.Value.Heads} heads, {(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))} sectors per track"); + + outputFormat.SetGeometry(rigidPage.Value.Cylinders, rigidPage.Value.Heads, + (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); + + setGeometry = true; } - else + else if(page.Page == 0x05 && + page.Subpage == 0x00) { - _dumpLog.WriteLine("Several media tags not supported, not continuing..."); - StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + Modes.ModePage_05? flexiblePage = Modes.DecodeModePage_05(page.PageResponse); - return; + if(!flexiblePage.HasValue) + continue; + + _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", + flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, + flexiblePage.Value.SectorsPerTrack); + + UpdateStatus?. + Invoke($"Setting geometry to {flexiblePage.Value.Cylinders} cylinders, {flexiblePage.Value.Heads} heads, {flexiblePage.Value.SectorsPerTrack} sectors per track"); + + outputFormat.SetGeometry(flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, + flexiblePage.Value.SectorsPerTrack); + + setGeometry = true; } - } + } - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); - _dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); + DumpHardwareType currentTry = null; + ExtentsULong extents = null; - var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); - var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile); - ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); + ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, + _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, + _private, _force); - // Cannot create image - if(!ret) + if(currentTry == null || + extents == null) + { + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + + return; + } + + if(_resume.NextBlock > 0) + { + UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); + _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + } + + bool newTrim = false; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + { + if(_aborted) { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputFormat.ErrorMessage); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + - outputFormat.ErrorMessage); - - return; + break; } + if(blocks - i < blocksToRead) + blocksToRead = (uint)(blocks - i); + + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)blocks); + + sense = _dev.Read6(out readBuffer, out _, (uint)i, blockSize, (byte)blocksToRead, _dev.Timeout, + out double cmdDuration); + + totalDuration += cmdDuration; + + if(!sense && + !_dev.Error) + { + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(readBuffer, i, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(i, blocksToRead, true); + } + else + { + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + if(i + _skip > blocks) + _skip = (uint)(blocks - i); + + // Write empty data + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); + + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; + } + + sectorSpeedStart += blocksToRead; + _resume.NextBlock = i + blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + + end = DateTime.UtcNow; + EndProgress?.Invoke(); + mhddLog.Close(); + + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + + #region Trimming + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _trim && + newTrim) + { start = DateTime.UtcNow; - double imageWriteDuration = 0; + UpdateStatus?.Invoke("Trimming skipped sectors"); + _dumpLog.WriteLine("Trimming skipped sectors"); - if(decMode?.Pages != null) - { - bool setGeometry = false; - - foreach(Modes.ModePage page in decMode.Value.Pages) - if(page.Page == 0x04 && - page.Subpage == 0x00) - { - Modes.ModePage_04? rigidPage = Modes.DecodeModePage_04(page.PageResponse); - - if(!rigidPage.HasValue || setGeometry) - continue; - - _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", - rigidPage.Value.Cylinders, rigidPage.Value.Heads, - (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); - - UpdateStatus?. - Invoke($"Setting geometry to {rigidPage.Value.Cylinders} cylinders, {rigidPage.Value.Heads} heads, {(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))} sectors per track"); - - outputFormat.SetGeometry(rigidPage.Value.Cylinders, rigidPage.Value.Heads, - (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); - - setGeometry = true; - } - else if(page.Page == 0x05 && - page.Subpage == 0x00) - { - Modes.ModePage_05? flexiblePage = Modes.DecodeModePage_05(page.PageResponse); - - if(!flexiblePage.HasValue) - continue; - - _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", - flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, - flexiblePage.Value.SectorsPerTrack); - - UpdateStatus?. - Invoke($"Setting geometry to {flexiblePage.Value.Cylinders} cylinders, {flexiblePage.Value.Heads} heads, {flexiblePage.Value.SectorsPerTrack} sectors per track"); - - outputFormat.SetGeometry(flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, - flexiblePage.Value.SectorsPerTrack); - - setGeometry = true; - } - } - - DumpHardwareType currentTry = null; - ExtentsULong extents = null; - - ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, - _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, - _private, _force); - - if(currentTry == null || - extents == null) - { - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); - - return; - } - - if(_resume.NextBlock > 0) - { - UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); - _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); - } - - bool newTrim = false; - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; + ulong[] tmpArray = _resume.BadBlocks.ToArray(); InitProgress?.Invoke(); - for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + foreach(ulong badSector in tmpArray) { if(_aborted) { @@ -338,21 +454,147 @@ namespace Aaru.Core.Devices.Dumping break; } - if(blocks - i < blocksToRead) - blocksToRead = (uint)(blocks - i); + PulseProgress?.Invoke($"Trimming sector {badSector}"); - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; + sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout, + out double _); - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; + if(sense || _dev.Error) + continue; - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)blocks); + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(readBuffer, badSector); + } - sense = _dev.Read6(out readBuffer, out _, (uint)i, blockSize, (byte)blocksToRead, _dev.Timeout, + EndProgress?.Invoke(); + end = DateTime.UtcNow; + UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); + _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + } + #endregion Trimming + + #region Error handling + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _retryPasses > 0) + { + int pass = 1; + bool forward = true; + bool runningPersistent = false; + + Modes.ModePage? currentModePage = null; + byte[] md6; + + if(_persistent) + { + Modes.ModePage_01 pg; + + sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, + _dev.Timeout, out _); + + if(!sense) + { + Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); + + if(dcMode6?.Pages != null) + foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) + currentModePage = modePage; + } + + if(currentModePage == null) + { + pg = new Modes.ModePage_01 + { + PS = false, + AWRE = true, + ARRE = true, + TB = false, + RC = false, + EER = true, + PER = false, + DTE = true, + DCR = false, + ReadRetryCount = 32 + }; + + currentModePage = new Modes.ModePage + { + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01(pg) + }; + } + + pg = new Modes.ModePage_01 + { + PS = false, + AWRE = false, + ARRE = false, + TB = true, + RC = false, + EER = true, + PER = false, + DTE = false, + DCR = false, + ReadRetryCount = 255 + }; + + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + new Modes.ModePage + { + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01(pg) + } + } + }; + + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); + sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _); + + if(sense) + { + UpdateStatus?. + Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + + AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); + + _dumpLog. + WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + } + else + runningPersistent = true; + } + + InitProgress?.Invoke(); + repeatRetry: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); + + foreach(ulong badSector in tmpArray) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); + + sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout, out double cmdDuration); totalDuration += cmdDuration; @@ -360,459 +602,216 @@ namespace Aaru.Core.Devices.Dumping if(!sense && !_dev.Error) { - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(readBuffer, i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(i, blocksToRead, true); - } - else - { - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly - - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - // Write empty data - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; - } - - sectorSpeedStart += blocksToRead; - _resume.NextBlock = i + blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - - end = DateTime.UtcNow; - EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - #region Trimming - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _trim && - newTrim) - { - start = DateTime.UtcNow; - UpdateStatus?.Invoke("Trimming skipped sectors"); - _dumpLog.WriteLine("Trimming skipped sectors"); - - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - InitProgress?.Invoke(); - - foreach(ulong badSector in tmpArray) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke($"Trimming sector {badSector}"); - - sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout, - out double _); - - if(sense || _dev.Error) - continue; - _resume.BadBlocks.Remove(badSector); extents.Add(badSector); outputFormat.WriteSector(readBuffer, badSector); + UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } - - EndProgress?.Invoke(); - end = DateTime.UtcNow; - UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); - _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + else if(runningPersistent) + outputFormat.WriteSector(readBuffer, badSector); } - #endregion Trimming - #region Error handling - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _retryPasses > 0) + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) { - int pass = 1; - bool forward = true; - bool runningPersistent = false; + pass++; + forward = !forward; + _resume.BadBlocks.Sort(); - Modes.ModePage? currentModePage = null; - byte[] md6; + if(!forward) + _resume.BadBlocks.Reverse(); - if(_persistent) - { - Modes.ModePage_01 pg; - - sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - _dev.Timeout, out _); - - if(!sense) - { - Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); - - if(dcMode6?.Pages != null) - foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) - currentModePage = modePage; - } - - if(currentModePage == null) - { - pg = new Modes.ModePage_01 - { - PS = false, - AWRE = true, - ARRE = true, - TB = false, - RC = false, - EER = true, - PER = false, - DTE = true, - DCR = false, - ReadRetryCount = 32 - }; - - currentModePage = new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01(pg) - }; - } - - pg = new Modes.ModePage_01 - { - PS = false, - AWRE = false, - ARRE = false, - TB = true, - RC = false, - EER = true, - PER = false, - DTE = false, - DCR = false, - ReadRetryCount = 255 - }; - - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01(pg) - } - } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); - sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - { - UpdateStatus?. - Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - - AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - - _dumpLog. - WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - } - else - runningPersistent = true; - } - - InitProgress?.Invoke(); - repeatRetry: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - - foreach(ulong badSector in tmpArray) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, - forward ? "forward" : "reverse", - runningPersistent ? "recovering partial data, " : "")); - - sense = _dev.Read6(out readBuffer, out _, (uint)badSector, blockSize, 1, _dev.Timeout, - out double cmdDuration); - - totalDuration += cmdDuration; - - if(!sense && - !_dev.Error) - { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(readBuffer, badSector); - UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); - } - else if(runningPersistent) - outputFormat.WriteSector(readBuffer, badSector); - } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto repeatRetry; - } - - if(runningPersistent && currentModePage.HasValue) - { - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - currentModePage.Value - } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); - _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); - } - - EndProgress?.Invoke(); + goto repeatRetry; } - #endregion Error handling - _resume.BadBlocks.Sort(); - - foreach(ulong bad in _resume.BadBlocks) - _dumpLog.WriteLine("Sector {0} could not be read.", bad); - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - outputFormat.SetDumpHardware(_resume.Tries); - - var metadata = new CommonTypes.Structs.ImageInfo + if(runningPersistent && currentModePage.HasValue) { - Application = "Aaru", - ApplicationVersion = Version.GetVersion() - }; + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + currentModePage.Value + } + }; - if(!outputFormat.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputFormat.ErrorMessage); + md6 = Modes.EncodeMode6(md, _dev.ScsiType); - if(_preSidecar != null) - outputFormat.SetCicmMetadata(_preSidecar); + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); + _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); + } - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputFormat.Close(); - DateTime closeEnd = DateTime.Now; - UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + EndProgress?.Invoke(); + } + #endregion Error handling - if(_aborted) + _resume.BadBlocks.Sort(); + + foreach(ulong bad in _resume.BadBlocks) + _dumpLog.WriteLine("Sector {0} could not be read.", bad); + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + outputFormat.SetDumpHardware(_resume.Tries); + + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion() + }; + + if(!outputFormat.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputFormat.ErrorMessage); + + if(_preSidecar != null) + outputFormat.SetCicmMetadata(_preSidecar); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputFormat.Close(); + DateTime closeEnd = DateTime.Now; + UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); + _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + if(_metadata) + { + UpdateStatus?.Invoke("Creating sidecar."); + _dumpLog.WriteLine("Creating sidecar."); + var filters = new FiltersList(); + IFilter filter = filters.GetFilter(_outputPath); + IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; + ErrorNumber opened = inputPlugin.Open(filter); + + if(opened != ErrorNumber.NoError) { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); return; } - double totalChkDuration = 0; + DateTime chkStart = DateTime.UtcNow; + _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); + _sidecarClass.InitProgressEvent += InitProgress; + _sidecarClass.UpdateProgressEvent += UpdateProgress; + _sidecarClass.EndProgressEvent += EndProgress; + _sidecarClass.InitProgressEvent2 += InitProgress2; + _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; + _sidecarClass.EndProgressEvent2 += EndProgress2; + _sidecarClass.UpdateStatusEvent += UpdateStatus; + CICMMetadataType sidecar = _sidecarClass.Create(); + end = DateTime.UtcNow; - if(_metadata) + if(!_aborted) { - UpdateStatus?.Invoke("Creating sidecar."); - _dumpLog.WriteLine("Creating sidecar."); - var filters = new FiltersList(); - IFilter filter = filters.GetFilter(_outputPath); - IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; - ErrorNumber opened = inputPlugin.Open(filter); + totalChkDuration = (end - chkStart).TotalMilliseconds; + UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); - if(opened != ErrorNumber.NoError) + UpdateStatus?. + Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + + _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); + + _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + + if(_preSidecar != null) { - StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); - - return; + _preSidecar.BlockMedia = sidecar.BlockMedia; + sidecar = _preSidecar; } - DateTime chkStart = DateTime.UtcNow; - _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); - _sidecarClass.InitProgressEvent += InitProgress; - _sidecarClass.UpdateProgressEvent += UpdateProgress; - _sidecarClass.EndProgressEvent += EndProgress; - _sidecarClass.InitProgressEvent2 += InitProgress2; - _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; - _sidecarClass.EndProgressEvent2 += EndProgress2; - _sidecarClass.UpdateStatusEvent += UpdateStatus; - CICMMetadataType sidecar = _sidecarClass.Create(); - end = DateTime.UtcNow; + List<(ulong start, string type)> filesystems = new(); - if(!_aborted) - { - totalChkDuration = (end - chkStart).TotalMilliseconds; - UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); + if(sidecar.BlockMedia[0].FileSystemInformation != null) + filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation + where partition.FileSystems != null + from fileSystem in partition.FileSystems + select (partition.StartSector, fileSystem.Type)); - UpdateStatus?. - Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); - - _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); - - _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); - - if(_preSidecar != null) + if(filesystems.Count > 0) + foreach(var filesystem in filesystems.Select(o => new + { + o.start, + o.type + }).Distinct()) { - _preSidecar.BlockMedia = sidecar.BlockMedia; - sidecar = _preSidecar; + UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); + _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); } - List<(ulong start, string type)> filesystems = new(); + sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); + (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + sidecar.BlockMedia[0].DiskType = xmlType.type; + sidecar.BlockMedia[0].DiskSubType = xmlType.subType; - if(sidecar.BlockMedia[0].FileSystemInformation != null) - filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation - where partition.FileSystems != null - from fileSystem in partition.FileSystems - select (partition.StartSector, fileSystem.Type)); + if(!_dev.IsRemovable || + _dev.IsUsb) + if(_dev.Type == DeviceType.ATAPI) + sidecar.BlockMedia[0].Interface = "ATAPI"; + else if(_dev.IsUsb) + sidecar.BlockMedia[0].Interface = "USB"; + else if(_dev.IsFireWire) + sidecar.BlockMedia[0].Interface = "FireWire"; + else + sidecar.BlockMedia[0].Interface = "SCSI"; - if(filesystems.Count > 0) - foreach(var filesystem in filesystems.Select(o => new - { - o.start, - o.type - }).Distinct()) - { - UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); - _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); - } + sidecar.BlockMedia[0].LogicalBlocks = blocks; + sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize; + sidecar.BlockMedia[0].LogicalBlockSize = logicalBlockSize; + sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; + sidecar.BlockMedia[0].Model = _dev.Model; - sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); - (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); - sidecar.BlockMedia[0].DiskType = xmlType.type; - sidecar.BlockMedia[0].DiskSubType = xmlType.subType; + if(!_private) + sidecar.BlockMedia[0].Serial = _dev.Serial; - if(!_dev.IsRemovable || - _dev.IsUsb) - if(_dev.Type == DeviceType.ATAPI) - sidecar.BlockMedia[0].Interface = "ATAPI"; - else if(_dev.IsUsb) - sidecar.BlockMedia[0].Interface = "USB"; - else if(_dev.IsFireWire) - sidecar.BlockMedia[0].Interface = "FireWire"; - else - sidecar.BlockMedia[0].Interface = "SCSI"; + sidecar.BlockMedia[0].Size = blocks * blockSize; - sidecar.BlockMedia[0].LogicalBlocks = blocks; - sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize; - sidecar.BlockMedia[0].LogicalBlockSize = logicalBlockSize; - sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; - sidecar.BlockMedia[0].Model = _dev.Model; + if(_dev.IsRemovable) + sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); - if(!_private) - sidecar.BlockMedia[0].Serial = _dev.Serial; + UpdateStatus?.Invoke("Writing metadata sidecar"); - sidecar.BlockMedia[0].Size = blocks * blockSize; + var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - if(_dev.IsRemovable) - sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); - - UpdateStatus?.Invoke("Writing metadata sidecar"); - - var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - - var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); - xmlSer.Serialize(xmlFs, sidecar); - xmlFs.Close(); - } + var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); } - - UpdateStatus?.Invoke(""); - - UpdateStatus?. - Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); - - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); - - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); - - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); - - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - UpdateStatus?.Invoke(""); - - Statistics.AddMedia(dskType, true); } + + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + UpdateStatus?.Invoke(""); + + Statistics.AddMedia(dskType, true); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/NVMe.cs b/Aaru.Core/Devices/Dumping/NVMe.cs index 628161b22..614c881f7 100644 --- a/Aaru.Core/Devices/Dumping/NVMe.cs +++ b/Aaru.Core/Devices/Dumping/NVMe.cs @@ -32,11 +32,10 @@ // ReSharper disable InconsistentNaming -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +public partial class Dump { - public partial class Dump - { - /// Dumps an NVMe device - public void NVMe() => StoppingErrorMessage?.Invoke("NVMe devices not yet supported."); - } + /// Dumps an NVMe device + public void NVMe() => StoppingErrorMessage?.Invoke("NVMe devices not yet supported."); } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/PlayStationPortable/MemoryStick.cs b/Aaru.Core/Devices/Dumping/PlayStationPortable/MemoryStick.cs index 513fad0b4..4e1f2b74a 100644 --- a/Aaru.Core/Devices/Dumping/PlayStationPortable/MemoryStick.cs +++ b/Aaru.Core/Devices/Dumping/PlayStationPortable/MemoryStick.cs @@ -50,131 +50,248 @@ using Schemas; using MediaType = Aaru.CommonTypes.MediaType; using Version = Aaru.CommonTypes.Interop.Version; -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +public partial class Dump { - public partial class Dump + [SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")] + void DumpMs() { - [SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")] - void DumpMs() + const ushort sbcProfile = 0x0001; + const uint blockSize = 512; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + uint blocksToRead = 64; + DateTime start; + DateTime end; + MediaType dskType; + bool sense; + byte[] senseBuf; + var outputFormat = _outputPlugin as IWritableImage; + + sense = _dev.ReadCapacity(out byte[] readBuffer, out _, _dev.Timeout, out _); + + if(sense) { - const ushort sbcProfile = 0x0001; - const uint blockSize = 512; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; - uint blocksToRead = 64; - DateTime start; - DateTime end; - MediaType dskType; - bool sense; - byte[] senseBuf; - var outputFormat = _outputPlugin as IWritableImage; + _dumpLog.WriteLine("Could not detect capacity..."); + StoppingErrorMessage?.Invoke("Could not detect capacity..."); - sense = _dev.ReadCapacity(out byte[] readBuffer, out _, _dev.Timeout, out _); + return; + } - if(sense) + uint blocks = (uint)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]); + + blocks++; + + ulong totalSize = blocks * (ulong)blockSize; + + if(totalSize > 1099511627776) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); + else if(totalSize > 1073741824) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); + else if(totalSize > 1048576) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); + else if(totalSize > 1024) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); + else + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); + + if(blocks == 0) + { + _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); + StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); + + return; + } + + UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); + UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); + UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); + + if(blocks > 262144) + { + dskType = MediaType.MemoryStickProDuo; + _dumpLog.WriteLine("Media detected as MemoryStick Pro Duo..."); + UpdateStatus?.Invoke("Media detected as MemoryStick Pro Duo..."); + } + else + { + dskType = MediaType.MemoryStickDuo; + _dumpLog.WriteLine("Media detected as MemoryStick Duo..."); + UpdateStatus?.Invoke("Media detected as MemoryStick Duo..."); + } + + bool ret; + + var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); + var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile); + ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); + + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputFormat.ErrorMessage); + + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; + } + + start = DateTime.UtcNow; + double imageWriteDuration = 0; + + DumpHardwareType currentTry = null; + ExtentsULong extents = null; + + ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, + _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, + _private, _force); + + if(currentTry == null || + extents == null) + { + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + + return; + } + + if(_resume.NextBlock > 0) + _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + + bool newTrim = false; + + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + { + if(_aborted) { - _dumpLog.WriteLine("Could not detect capacity..."); - StoppingErrorMessage?.Invoke("Could not detect capacity..."); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - return; + break; } - uint blocks = (uint)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]); + if(blocks - i < blocksToRead) + blocksToRead = (uint)(blocks - i); - blocks++; + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; - ulong totalSize = blocks * (ulong)blockSize; + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; - if(totalSize > 1099511627776) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); - else if(totalSize > 1073741824) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); - else if(totalSize > 1048576) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); - else if(totalSize > 1024) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); - else - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, blocks); - if(blocks == 0) + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, 0, + blocksToRead, false, _dev.Timeout, out double cmdDuration); + + totalDuration += cmdDuration; + + if(!sense && + !_dev.Error) { - _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); - StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); - - return; - } - - UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); - UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); - UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); - - if(blocks > 262144) - { - dskType = MediaType.MemoryStickProDuo; - _dumpLog.WriteLine("Media detected as MemoryStick Pro Duo..."); - UpdateStatus?.Invoke("Media detected as MemoryStick Pro Duo..."); + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(readBuffer, i, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(i, blocksToRead, true); } else { - dskType = MediaType.MemoryStickDuo; - _dumpLog.WriteLine("Media detected as MemoryStick Duo..."); - UpdateStatus?.Invoke("Media detected as MemoryStick Duo..."); + _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); + + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + if(i + _skip > blocks) + _skip = (uint)(blocks - i); + + // Write empty data + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); + + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; } - bool ret; + sectorSpeedStart += blocksToRead; + _resume.NextBlock = i + blocksToRead; - var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); - var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile); - ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - // Cannot create image - if(!ret) - { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputFormat.ErrorMessage); + if(elapsed <= 0) + continue; - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + - outputFormat.ErrorMessage); + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } - return; - } + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + end = DateTime.UtcNow; + EndProgress?.Invoke(); + mhddLog.Close(); + + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + + #region Trimming + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _trim && + newTrim) + { start = DateTime.UtcNow; - double imageWriteDuration = 0; + UpdateStatus?.Invoke("Trimming skipped sectors"); + _dumpLog.WriteLine("Trimming skipped sectors"); - DumpHardwareType currentTry = null; - ExtentsULong extents = null; - - ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, - _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, - _private, _force); - - if(currentTry == null || - extents == null) - { - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); - - return; - } - - if(_resume.NextBlock > 0) - _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); - - bool newTrim = false; - - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; + ulong[] tmpArray = _resume.BadBlocks.ToArray(); InitProgress?.Invoke(); - for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + foreach(ulong badSector in tmpArray) { if(_aborted) { @@ -185,491 +302,373 @@ namespace Aaru.Core.Devices.Dumping break; } - if(blocks - i < blocksToRead) - blocksToRead = (uint)(blocks - i); + PulseProgress?.Invoke($"Trimming sector {badSector}"); - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)badSector, + blockSize, 0, 1, false, _dev.Timeout, out double _); - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, blocks); - - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)i, blockSize, 0, - blocksToRead, false, _dev.Timeout, out double cmdDuration); - - totalDuration += cmdDuration; - - if(!sense && - !_dev.Error) + if(sense || _dev.Error) { - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(readBuffer, i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(i, blocksToRead, true); + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); + + continue; + } + + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(readBuffer, badSector); + } + + EndProgress?.Invoke(); + end = DateTime.UtcNow; + _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + } + #endregion Trimming + + #region Error handling + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _retryPasses > 0) + { + int pass = 1; + bool forward = true; + bool runningPersistent = false; + + Modes.ModePage? currentModePage = null; + byte[] md6; + + if(_persistent) + { + Modes.ModePage_01 pg; + + sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, + _dev.Timeout, out _); + + if(sense) + { + sense = _dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, + _dev.Timeout, out _); + + if(!sense) + { + Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(readBuffer, _dev.ScsiType); + + if(dcMode10.HasValue) + foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) + currentModePage = modePage; + } } else { - _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); + Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly - - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - // Write empty data - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; - } - - sectorSpeedStart += blocksToRead; - _resume.NextBlock = i + blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - - end = DateTime.UtcNow; - EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - #region Trimming - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _trim && - newTrim) - { - start = DateTime.UtcNow; - UpdateStatus?.Invoke("Trimming skipped sectors"); - _dumpLog.WriteLine("Trimming skipped sectors"); - - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - InitProgress?.Invoke(); - - foreach(ulong badSector in tmpArray) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke($"Trimming sector {badSector}"); - - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)badSector, - blockSize, 0, 1, false, _dev.Timeout, out double _); - - if(sense || _dev.Error) - { - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - continue; - } - - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(readBuffer, badSector); - } - - EndProgress?.Invoke(); - end = DateTime.UtcNow; - _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); - } - #endregion Trimming - - #region Error handling - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _retryPasses > 0) - { - int pass = 1; - bool forward = true; - bool runningPersistent = false; - - Modes.ModePage? currentModePage = null; - byte[] md6; - - if(_persistent) - { - Modes.ModePage_01 pg; - - sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - _dev.Timeout, out _); - - if(sense) - { - sense = _dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - _dev.Timeout, out _); - - if(!sense) - { - Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(readBuffer, _dev.ScsiType); - - if(dcMode10.HasValue) - foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => + if(dcMode6.HasValue) + foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => modePage.Page == 0x01 && modePage.Subpage == 0x00)) - currentModePage = modePage; - } - } - else + currentModePage = modePage; + } + + if(currentModePage == null) + { + pg = new Modes.ModePage_01 { - Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); + PS = false, + AWRE = true, + ARRE = true, + TB = false, + RC = false, + EER = true, + PER = false, + DTE = true, + DCR = false, + ReadRetryCount = 32 + }; - if(dcMode6.HasValue) - foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) - currentModePage = modePage; - } - - if(currentModePage == null) + currentModePage = new Modes.ModePage { - pg = new Modes.ModePage_01 - { - PS = false, - AWRE = true, - ARRE = true, - TB = false, - RC = false, - EER = true, - PER = false, - DTE = true, - DCR = false, - ReadRetryCount = 32 - }; + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01(pg) + }; + } - currentModePage = new Modes.ModePage + pg = new Modes.ModePage_01 + { + PS = false, + AWRE = false, + ARRE = false, + TB = true, + RC = false, + EER = true, + PER = false, + DTE = false, + DCR = false, + ReadRetryCount = 255 + }; + + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) - }; - } - - pg = new Modes.ModePage_01 - { - PS = false, - AWRE = false, - ARRE = false, - TB = true, - RC = false, - EER = true, - PER = false, - DTE = false, - DCR = false, - ReadRetryCount = 255 - }; - - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01(pg) - } } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); - sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - { - UpdateStatus?. - Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - - AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - - _dumpLog. - WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } - else - runningPersistent = true; - } + }; - InitProgress?.Invoke(); - repeatRetry: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); + md6 = Modes.EncodeMode6(md, _dev.ScsiType); - foreach(ulong badSector in tmpArray) + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); + sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - _dumpLog.WriteLine("Aborted!"); + UpdateStatus?. + Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - break; - } + AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, - forward ? "forward" : "reverse", - runningPersistent ? "recovering partial data, " : "")); - - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)badSector, - blockSize, 0, 1, false, _dev.Timeout, out double cmdDuration); - - totalDuration += cmdDuration; - - if(sense || _dev.Error) - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - if(!sense && - !_dev.Error) - { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(readBuffer, badSector); - UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); - } - else if(runningPersistent) - outputFormat.WriteSector(readBuffer, badSector); + _dumpLog. + WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto repeatRetry; - } - - if(runningPersistent && currentModePage.HasValue) - { - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - currentModePage.Value - } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); - _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); - } - - EndProgress?.Invoke(); + else + runningPersistent = true; } - #endregion Error handling - _resume.BadBlocks.Sort(); + InitProgress?.Invoke(); + repeatRetry: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); - foreach(ulong bad in _resume.BadBlocks) - _dumpLog.WriteLine("Sector {0} could not be read.", bad); - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - var metadata = new CommonTypes.Structs.ImageInfo + foreach(ulong badSector in tmpArray) { - Application = "Aaru", - ApplicationVersion = Version.GetVersion() - }; + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + _dumpLog.WriteLine("Aborted!"); - if(!outputFormat.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputFormat.ErrorMessage); + break; + } - outputFormat.SetDumpHardware(_resume.Tries); + PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); - if(_preSidecar != null) - outputFormat.SetCicmMetadata(_preSidecar); + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, (uint)badSector, + blockSize, 0, 1, false, _dev.Timeout, out double cmdDuration); - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputFormat.Close(); - DateTime closeEnd = DateTime.Now; - UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + totalDuration += cmdDuration; - if(_aborted) + if(sense || _dev.Error) + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); + + if(!sense && + !_dev.Error) + { + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(readBuffer, badSector); + UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); + } + else if(runningPersistent) + outputFormat.WriteSector(readBuffer, badSector); + } + + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + pass++; + forward = !forward; + _resume.BadBlocks.Sort(); + + if(!forward) + _resume.BadBlocks.Reverse(); + + goto repeatRetry; + } + + if(runningPersistent && currentModePage.HasValue) + { + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + currentModePage.Value + } + }; + + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); + _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); + } + + EndProgress?.Invoke(); + } + #endregion Error handling + + _resume.BadBlocks.Sort(); + + foreach(ulong bad in _resume.BadBlocks) + _dumpLog.WriteLine("Sector {0} could not be read.", bad); + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion() + }; + + if(!outputFormat.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputFormat.ErrorMessage); + + outputFormat.SetDumpHardware(_resume.Tries); + + if(_preSidecar != null) + outputFormat.SetCicmMetadata(_preSidecar); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputFormat.Close(); + DateTime closeEnd = DateTime.Now; + UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); + _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + if(_metadata) + { + UpdateStatus?.Invoke("Creating sidecar."); + _dumpLog.WriteLine("Creating sidecar."); + var filters = new FiltersList(); + IFilter filter = filters.GetFilter(_outputPath); + IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; + ErrorNumber opened = inputPlugin.Open(filter); + + if(opened != ErrorNumber.NoError) + { + StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); return; } - double totalChkDuration = 0; + DateTime chkStart = DateTime.UtcNow; + _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); + _sidecarClass.InitProgressEvent += InitProgress; + _sidecarClass.UpdateProgressEvent += UpdateProgress; + _sidecarClass.EndProgressEvent += EndProgress; + _sidecarClass.InitProgressEvent2 += InitProgress2; + _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; + _sidecarClass.EndProgressEvent2 += EndProgress2; + _sidecarClass.UpdateStatusEvent += UpdateStatus; + CICMMetadataType sidecar = _sidecarClass.Create(); + end = DateTime.UtcNow; - if(_metadata) + if(!_aborted) { - UpdateStatus?.Invoke("Creating sidecar."); - _dumpLog.WriteLine("Creating sidecar."); - var filters = new FiltersList(); - IFilter filter = filters.GetFilter(_outputPath); - IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; - ErrorNumber opened = inputPlugin.Open(filter); + totalChkDuration = (end - chkStart).TotalMilliseconds; + UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); - if(opened != ErrorNumber.NoError) + UpdateStatus?. + Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + + _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); + + _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + + if(_preSidecar != null) { - StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); - - return; + _preSidecar.BlockMedia = sidecar.BlockMedia; + sidecar = _preSidecar; } - DateTime chkStart = DateTime.UtcNow; - _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); - _sidecarClass.InitProgressEvent += InitProgress; - _sidecarClass.UpdateProgressEvent += UpdateProgress; - _sidecarClass.EndProgressEvent += EndProgress; - _sidecarClass.InitProgressEvent2 += InitProgress2; - _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; - _sidecarClass.EndProgressEvent2 += EndProgress2; - _sidecarClass.UpdateStatusEvent += UpdateStatus; - CICMMetadataType sidecar = _sidecarClass.Create(); - end = DateTime.UtcNow; + List<(ulong start, string type)> filesystems = new(); - if(!_aborted) - { - totalChkDuration = (end - chkStart).TotalMilliseconds; - UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); + if(sidecar.BlockMedia[0].FileSystemInformation != null) + filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation + where partition.FileSystems != null + from fileSystem in partition.FileSystems + select (partition.StartSector, fileSystem.Type)); - UpdateStatus?. - Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); - - _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); - - _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); - - if(_preSidecar != null) + if(filesystems.Count > 0) + foreach(var filesystem in filesystems.Select(o => new + { + o.start, + o.type + }).Distinct()) { - _preSidecar.BlockMedia = sidecar.BlockMedia; - sidecar = _preSidecar; + UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); + _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); } - List<(ulong start, string type)> filesystems = new(); + sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); + (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + sidecar.BlockMedia[0].DiskType = xmlType.type; + sidecar.BlockMedia[0].DiskSubType = xmlType.subType; + sidecar.BlockMedia[0].Interface = "USB"; + sidecar.BlockMedia[0].LogicalBlocks = blocks; + sidecar.BlockMedia[0].PhysicalBlockSize = (int)blockSize; + sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize; + sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; + sidecar.BlockMedia[0].Model = _dev.Model; - if(sidecar.BlockMedia[0].FileSystemInformation != null) - filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation - where partition.FileSystems != null - from fileSystem in partition.FileSystems - select (partition.StartSector, fileSystem.Type)); + if(!_private) + sidecar.BlockMedia[0].Serial = _dev.Serial; - if(filesystems.Count > 0) - foreach(var filesystem in filesystems.Select(o => new - { - o.start, - o.type - }).Distinct()) - { - UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); - _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); - } + sidecar.BlockMedia[0].Size = blocks * blockSize; - sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); - (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); - sidecar.BlockMedia[0].DiskType = xmlType.type; - sidecar.BlockMedia[0].DiskSubType = xmlType.subType; - sidecar.BlockMedia[0].Interface = "USB"; - sidecar.BlockMedia[0].LogicalBlocks = blocks; - sidecar.BlockMedia[0].PhysicalBlockSize = (int)blockSize; - sidecar.BlockMedia[0].LogicalBlockSize = (int)blockSize; - sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; - sidecar.BlockMedia[0].Model = _dev.Model; + if(_dev.IsRemovable) + sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); - if(!_private) - sidecar.BlockMedia[0].Serial = _dev.Serial; + UpdateStatus?.Invoke("Writing metadata sidecar"); - sidecar.BlockMedia[0].Size = blocks * blockSize; + var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - if(_dev.IsRemovable) - sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); - - UpdateStatus?.Invoke("Writing metadata sidecar"); - - var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - - var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); - xmlSer.Serialize(xmlFs, sidecar); - xmlFs.Close(); - } + var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); } - - UpdateStatus?.Invoke(""); - - UpdateStatus?. - Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); - - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); - - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); - - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); - - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - UpdateStatus?.Invoke(""); - - Statistics.AddMedia(dskType, true); } + + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + UpdateStatus?.Invoke(""); + + Statistics.AddMedia(dskType, true); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/PlayStationPortable/PlayStationPortable.cs b/Aaru.Core/Devices/Dumping/PlayStationPortable/PlayStationPortable.cs index 28c7d662e..1cc05fad5 100644 --- a/Aaru.Core/Devices/Dumping/PlayStationPortable/PlayStationPortable.cs +++ b/Aaru.Core/Devices/Dumping/PlayStationPortable/PlayStationPortable.cs @@ -38,178 +38,177 @@ using Aaru.CommonTypes.Structs.Devices.SCSI; using Aaru.Decoders.SCSI; using Aaru.Devices; -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +public partial class Dump { - public partial class Dump + static readonly byte[] _fatSignature = { - static readonly byte[] _fatSignature = + 0x46, 0x41, 0x54, 0x31, 0x36, 0x20, 0x20, 0x20 + }; + static readonly byte[] _isoExtension = + { + 0x49, 0x53, 0x4F + }; + + /// Dumps a CFW PlayStation Portable UMD + void PlayStationPortable() + { + if(!_outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickDuo) && + !_outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickProDuo) && + !_outputPlugin.SupportedMediaTypes.Contains(MediaType.UMD)) { - 0x46, 0x41, 0x54, 0x31, 0x36, 0x20, 0x20, 0x20 - }; - static readonly byte[] _isoExtension = - { - 0x49, 0x53, 0x4F - }; + _dumpLog.WriteLine("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump..."); - /// Dumps a CFW PlayStation Portable UMD - void PlayStationPortable() - { - if(!_outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickDuo) && - !_outputPlugin.SupportedMediaTypes.Contains(MediaType.MemoryStickProDuo) && - !_outputPlugin.SupportedMediaTypes.Contains(MediaType.UMD)) - { - _dumpLog.WriteLine("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump..."); + StoppingErrorMessage?. + Invoke("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump..."); - StoppingErrorMessage?. - Invoke("Selected output plugin does not support MemoryStick Duo or UMD, cannot dump..."); - - return; - } - - UpdateStatus?.Invoke("Checking if media is UMD or MemoryStick..."); - _dumpLog.WriteLine("Checking if media is UMD or MemoryStick..."); - - bool sense = _dev.ModeSense6(out byte[] buffer, out _, false, ScsiModeSensePageControl.Current, 0, - _dev.Timeout, out _); - - if(sense) - { - _dumpLog.WriteLine("Could not get MODE SENSE..."); - StoppingErrorMessage?.Invoke("Could not get MODE SENSE..."); - - return; - } - - Modes.DecodedMode? decoded = Modes.DecodeMode6(buffer, PeripheralDeviceTypes.DirectAccess); - - if(!decoded.HasValue) - { - _dumpLog.WriteLine("Could not decode MODE SENSE..."); - StoppingErrorMessage?.Invoke("Could not decode MODE SENSE..."); - - return; - } - - // UMDs are always write protected - if(!decoded.Value.Header.WriteProtected) - { - DumpMs(); - - return; - } - - sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false, _dev.Timeout, - out _); - - if(sense) - { - _dumpLog.WriteLine("Could not read..."); - StoppingErrorMessage?.Invoke("Could not read..."); - - return; - } - - byte[] tmp = new byte[8]; - - Array.Copy(buffer, 0x36, tmp, 0, 8); - - // UMDs are stored inside a FAT16 volume - if(!tmp.SequenceEqual(_fatSignature)) - { - DumpMs(); - - return; - } - - ushort fatStart = (ushort)((buffer[0x0F] << 8) + buffer[0x0E]); - ushort sectorsPerFat = (ushort)((buffer[0x17] << 8) + buffer[0x16]); - ushort rootStart = (ushort)((sectorsPerFat * 2) + fatStart); - - UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}..."); - _dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart); - - sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false, - _dev.Timeout, out _); - - if(sense) - { - StoppingErrorMessage?.Invoke("Could not read..."); - _dumpLog.WriteLine("Could not read..."); - - return; - } - - tmp = new byte[3]; - Array.Copy(buffer, 0x28, tmp, 0, 3); - - if(!tmp.SequenceEqual(_isoExtension)) - { - DumpMs(); - - return; - } - - UpdateStatus?.Invoke($"FAT starts at sector {fatStart} and runs for {sectorsPerFat} sectors..."); - _dumpLog.WriteLine("FAT starts at sector {0} and runs for {1} sectors...", fatStart, sectorsPerFat); - - UpdateStatus?.Invoke("Reading FAT..."); - _dumpLog.WriteLine("Reading FAT..."); - - byte[] fat = new byte[sectorsPerFat * 512]; - - uint position = 0; - - while(position < sectorsPerFat) - { - uint transfer = 64; - - if(transfer + position > sectorsPerFat) - transfer = sectorsPerFat - position; - - sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, position + fatStart, 512, 0, - transfer, false, _dev.Timeout, out _); - - if(sense) - { - StoppingErrorMessage?.Invoke("Could not read..."); - _dumpLog.WriteLine("Could not read..."); - - return; - } - - Array.Copy(buffer, 0, fat, position * 512, transfer * 512); - - position += transfer; - } - - UpdateStatus?.Invoke("Traversing FAT..."); - _dumpLog.WriteLine("Traversing FAT..."); - - ushort previousCluster = BitConverter.ToUInt16(fat, 4); - - for(int i = 3; i < fat.Length / 2; i++) - { - ushort nextCluster = BitConverter.ToUInt16(fat, i * 2); - - if(nextCluster == previousCluster + 1) - { - previousCluster = nextCluster; - - continue; - } - - if(nextCluster == 0xFFFF) - break; - - DumpMs(); - - return; - } - - if(_outputPlugin is IWritableOpticalImage) - DumpUmd(); - else - StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images."); + return; } + + UpdateStatus?.Invoke("Checking if media is UMD or MemoryStick..."); + _dumpLog.WriteLine("Checking if media is UMD or MemoryStick..."); + + bool sense = _dev.ModeSense6(out byte[] buffer, out _, false, ScsiModeSensePageControl.Current, 0, + _dev.Timeout, out _); + + if(sense) + { + _dumpLog.WriteLine("Could not get MODE SENSE..."); + StoppingErrorMessage?.Invoke("Could not get MODE SENSE..."); + + return; + } + + Modes.DecodedMode? decoded = Modes.DecodeMode6(buffer, PeripheralDeviceTypes.DirectAccess); + + if(!decoded.HasValue) + { + _dumpLog.WriteLine("Could not decode MODE SENSE..."); + StoppingErrorMessage?.Invoke("Could not decode MODE SENSE..."); + + return; + } + + // UMDs are always write protected + if(!decoded.Value.Header.WriteProtected) + { + DumpMs(); + + return; + } + + sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false, _dev.Timeout, + out _); + + if(sense) + { + _dumpLog.WriteLine("Could not read..."); + StoppingErrorMessage?.Invoke("Could not read..."); + + return; + } + + byte[] tmp = new byte[8]; + + Array.Copy(buffer, 0x36, tmp, 0, 8); + + // UMDs are stored inside a FAT16 volume + if(!tmp.SequenceEqual(_fatSignature)) + { + DumpMs(); + + return; + } + + ushort fatStart = (ushort)((buffer[0x0F] << 8) + buffer[0x0E]); + ushort sectorsPerFat = (ushort)((buffer[0x17] << 8) + buffer[0x16]); + ushort rootStart = (ushort)((sectorsPerFat * 2) + fatStart); + + UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}..."); + _dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart); + + sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false, + _dev.Timeout, out _); + + if(sense) + { + StoppingErrorMessage?.Invoke("Could not read..."); + _dumpLog.WriteLine("Could not read..."); + + return; + } + + tmp = new byte[3]; + Array.Copy(buffer, 0x28, tmp, 0, 3); + + if(!tmp.SequenceEqual(_isoExtension)) + { + DumpMs(); + + return; + } + + UpdateStatus?.Invoke($"FAT starts at sector {fatStart} and runs for {sectorsPerFat} sectors..."); + _dumpLog.WriteLine("FAT starts at sector {0} and runs for {1} sectors...", fatStart, sectorsPerFat); + + UpdateStatus?.Invoke("Reading FAT..."); + _dumpLog.WriteLine("Reading FAT..."); + + byte[] fat = new byte[sectorsPerFat * 512]; + + uint position = 0; + + while(position < sectorsPerFat) + { + uint transfer = 64; + + if(transfer + position > sectorsPerFat) + transfer = sectorsPerFat - position; + + sense = _dev.Read12(out buffer, out _, 0, false, true, false, false, position + fatStart, 512, 0, + transfer, false, _dev.Timeout, out _); + + if(sense) + { + StoppingErrorMessage?.Invoke("Could not read..."); + _dumpLog.WriteLine("Could not read..."); + + return; + } + + Array.Copy(buffer, 0, fat, position * 512, transfer * 512); + + position += transfer; + } + + UpdateStatus?.Invoke("Traversing FAT..."); + _dumpLog.WriteLine("Traversing FAT..."); + + ushort previousCluster = BitConverter.ToUInt16(fat, 4); + + for(int i = 3; i < fat.Length / 2; i++) + { + ushort nextCluster = BitConverter.ToUInt16(fat, i * 2); + + if(nextCluster == previousCluster + 1) + { + previousCluster = nextCluster; + + continue; + } + + if(nextCluster == 0xFFFF) + break; + + DumpMs(); + + return; + } + + if(_outputPlugin is IWritableOpticalImage) + DumpUmd(); + else + StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images."); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/PlayStationPortable/UMD.cs b/Aaru.Core/Devices/Dumping/PlayStationPortable/UMD.cs index 1e2db9634..e601bfef2 100644 --- a/Aaru.Core/Devices/Dumping/PlayStationPortable/UMD.cs +++ b/Aaru.Core/Devices/Dumping/PlayStationPortable/UMD.cs @@ -48,543 +48,542 @@ using Schemas; using TrackType = Aaru.CommonTypes.Enums.TrackType; using Version = Aaru.CommonTypes.Interop.Version; -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +public partial class Dump { - public partial class Dump + [SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")] + void DumpUmd() { - [SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")] - void DumpUmd() + const uint blockSize = 2048; + const MediaType dskType = MediaType.UMD; + uint blocksToRead = 16; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + DateTime start; + DateTime end; + byte[] senseBuf; + var outputOptical = _outputPlugin as IWritableOpticalImage; + + bool sense = _dev.Read12(out byte[] readBuffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false, + _dev.Timeout, out _); + + if(sense) { - const uint blockSize = 2048; - const MediaType dskType = MediaType.UMD; - uint blocksToRead = 16; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; - DateTime start; - DateTime end; - byte[] senseBuf; - var outputOptical = _outputPlugin as IWritableOpticalImage; + _dumpLog.WriteLine("Could not read..."); + StoppingErrorMessage?.Invoke("Could not read..."); - bool sense = _dev.Read12(out byte[] readBuffer, out _, 0, false, true, false, false, 0, 512, 0, 1, false, - _dev.Timeout, out _); + return; + } - if(sense) + ushort fatStart = (ushort)((readBuffer[0x0F] << 8) + readBuffer[0x0E]); + ushort sectorsPerFat = (ushort)((readBuffer[0x17] << 8) + readBuffer[0x16]); + ushort rootStart = (ushort)((sectorsPerFat * 2) + fatStart); + ushort rootSize = (ushort)(((readBuffer[0x12] << 8) + readBuffer[0x11]) * 32 / 512); + ushort umdStart = (ushort)(rootStart + rootSize); + + UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}..."); + _dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart); + + sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false, + _dev.Timeout, out _); + + if(sense) + { + _dumpLog.WriteLine("Could not read..."); + StoppingErrorMessage?.Invoke("Could not read..."); + + return; + } + + uint umdSizeInBytes = BitConverter.ToUInt32(readBuffer, 0x3C); + ulong blocks = umdSizeInBytes / blockSize; + string mediaPartNumber = Encoding.ASCII.GetString(readBuffer, 0, 11).Trim(); + + ulong totalSize = blocks * blockSize; + + if(totalSize > 1099511627776) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); + else if(totalSize > 1073741824) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); + else if(totalSize > 1048576) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); + else if(totalSize > 1024) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); + else + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); + + UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); + UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); + UpdateStatus?.Invoke($"Device reports {2048} bytes per physical block."); + UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); + UpdateStatus?.Invoke($"Media identified as {dskType}."); + UpdateStatus?.Invoke($"Media part number is {mediaPartNumber}."); + _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); + _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); + _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); + _dumpLog.WriteLine("Device reports {0} bytes per physical block.", 2048); + _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); + _dumpLog.WriteLine("Media identified as {0}.", dskType); + _dumpLog.WriteLine("Media part number is {0}.", mediaPartNumber); + + bool ret; + + var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); + var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0010); + ret = outputOptical.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); + + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputOptical.ErrorMessage); + + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputOptical.ErrorMessage); + + return; + } + + start = DateTime.UtcNow; + double imageWriteDuration = 0; + + (outputOptical as IWritableOpticalImage)?.SetTracks(new List + { + new Track { - _dumpLog.WriteLine("Could not read..."); - StoppingErrorMessage?.Invoke("Could not read..."); + BytesPerSector = (int)blockSize, + EndSector = blocks - 1, + Sequence = 1, + RawBytesPerSector = (int)blockSize, + SubchannelType = TrackSubchannelType.None, + Session = 1, + Type = TrackType.Data + } + }); - return; + DumpHardwareType currentTry = null; + ExtentsULong extents = null; + + ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, + _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, + _private, _force); + + if(currentTry == null || + extents == null) + { + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + + return; + } + + if(_resume.NextBlock > 0) + _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + + bool newTrim = false; + + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; } - ushort fatStart = (ushort)((readBuffer[0x0F] << 8) + readBuffer[0x0E]); - ushort sectorsPerFat = (ushort)((readBuffer[0x17] << 8) + readBuffer[0x16]); - ushort rootStart = (ushort)((sectorsPerFat * 2) + fatStart); - ushort rootSize = (ushort)(((readBuffer[0x12] << 8) + readBuffer[0x11]) * 32 / 512); - ushort umdStart = (ushort)(rootStart + rootSize); + if(blocks - i < blocksToRead) + blocksToRead = (uint)(blocks - i); - UpdateStatus?.Invoke($"Reading root directory in sector {rootStart}..."); - _dumpLog.WriteLine("Reading root directory in sector {0}...", rootStart); + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; - sense = _dev.Read12(out readBuffer, out _, 0, false, true, false, false, rootStart, 512, 0, 1, false, - _dev.Timeout, out _); + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; - if(sense) + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)blocks); + + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, + (uint)(umdStart + (i * 4)), 512, 0, blocksToRead * 4, false, _dev.Timeout, + out double cmdDuration); + + totalDuration += cmdDuration; + + if(!sense && + !_dev.Error) { - _dumpLog.WriteLine("Could not read..."); - StoppingErrorMessage?.Invoke("Could not read..."); - - return; + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputOptical.WriteSectors(readBuffer, i, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(i, blocksToRead, true); } - - uint umdSizeInBytes = BitConverter.ToUInt32(readBuffer, 0x3C); - ulong blocks = umdSizeInBytes / blockSize; - string mediaPartNumber = Encoding.ASCII.GetString(readBuffer, 0, 11).Trim(); - - ulong totalSize = blocks * blockSize; - - if(totalSize > 1099511627776) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); - else if(totalSize > 1073741824) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); - else if(totalSize > 1048576) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); - else if(totalSize > 1024) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); else - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); - - UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); - UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); - UpdateStatus?.Invoke($"Device reports {2048} bytes per physical block."); - UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); - UpdateStatus?.Invoke($"Media identified as {dskType}."); - UpdateStatus?.Invoke($"Media part number is {mediaPartNumber}."); - _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); - _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); - _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); - _dumpLog.WriteLine("Device reports {0} bytes per physical block.", 2048); - _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); - _dumpLog.WriteLine("Media identified as {0}.", dskType); - _dumpLog.WriteLine("Media part number is {0}.", mediaPartNumber); - - bool ret; - - var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); - var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0010); - ret = outputOptical.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); - - // Cannot create image - if(!ret) { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputOptical.ErrorMessage); + _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + - outputOptical.ErrorMessage); + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly - return; + if(i + _skip > blocks) + _skip = (uint)(blocks - i); + + // Write empty data + DateTime writeStart = DateTime.Now; + outputOptical.WriteSectors(new byte[blockSize * _skip], i, _skip); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); + + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; } + sectorSpeedStart += blocksToRead; + _resume.NextBlock = i + blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + + end = DateTime.UtcNow; + EndProgress?.Invoke(); + mhddLog.Close(); + + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + + #region Trimming + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _trim && + newTrim) + { start = DateTime.UtcNow; - double imageWriteDuration = 0; + _dumpLog.WriteLine("Trimming skipped sectors"); - (outputOptical as IWritableOpticalImage)?.SetTracks(new List - { - new Track - { - BytesPerSector = (int)blockSize, - EndSector = blocks - 1, - Sequence = 1, - RawBytesPerSector = (int)blockSize, - SubchannelType = TrackSubchannelType.None, - Session = 1, - Type = TrackType.Data - } - }); - - DumpHardwareType currentTry = null; - ExtentsULong extents = null; - - ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, - _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, - _private, _force); - - if(currentTry == null || - extents == null) - { - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); - - return; - } - - if(_resume.NextBlock > 0) - _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); - - bool newTrim = false; - - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; + ulong[] tmpArray = _resume.BadBlocks.ToArray(); InitProgress?.Invoke(); - for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + foreach(ulong badSector in tmpArray) { if(_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); _dumpLog.WriteLine("Aborted!"); break; } - if(blocks - i < blocksToRead) - blocksToRead = (uint)(blocks - i); - - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; - - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)blocks); + PulseProgress?.Invoke($"Trimming sector {badSector}"); sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, - (uint)(umdStart + (i * 4)), 512, 0, blocksToRead * 4, false, _dev.Timeout, - out double cmdDuration); + (uint)(umdStart + (badSector * 4)), 512, 0, 4, false, _dev.Timeout, + out double _); - totalDuration += cmdDuration; - - if(!sense && - !_dev.Error) + if(sense || _dev.Error) { - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputOptical.WriteSectors(readBuffer, i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(i, blocksToRead, true); - } - else - { - _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly - - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - // Write empty data - DateTime writeStart = DateTime.Now; - outputOptical.WriteSectors(new byte[blockSize * _skip], i, _skip); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; - } - - sectorSpeedStart += blocksToRead; - _resume.NextBlock = i + blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - - end = DateTime.UtcNow; - EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - #region Trimming - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _trim && - newTrim) - { - start = DateTime.UtcNow; - _dumpLog.WriteLine("Trimming skipped sectors"); - - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - InitProgress?.Invoke(); - - foreach(ulong badSector in tmpArray) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke($"Trimming sector {badSector}"); - - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, - (uint)(umdStart + (badSector * 4)), 512, 0, 4, false, _dev.Timeout, - out double _); - - if(sense || _dev.Error) - { - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - continue; - } - - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputOptical.WriteSector(readBuffer, badSector); } - EndProgress?.Invoke(); - end = DateTime.UtcNow; - _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputOptical.WriteSector(readBuffer, badSector); } - #endregion Trimming - #region Error handling - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _retryPasses > 0) + EndProgress?.Invoke(); + end = DateTime.UtcNow; + _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + } + #endregion Trimming + + #region Error handling + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _retryPasses > 0) + { + int pass = 1; + bool forward = true; + bool runningPersistent = false; + + Modes.ModePage? currentModePage = null; + byte[] md6; + + if(_persistent) { - int pass = 1; - bool forward = true; - bool runningPersistent = false; + Modes.ModePage_01 pg; - Modes.ModePage? currentModePage = null; - byte[] md6; + sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, + _dev.Timeout, out _); - if(_persistent) + if(!sense) { - Modes.ModePage_01 pg; + Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); - sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - _dev.Timeout, out _); + if(dcMode6.HasValue) + foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) + currentModePage = modePage; + } - if(!sense) + if(currentModePage == null) + { + pg = new Modes.ModePage_01 { - Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(readBuffer, _dev.ScsiType); + PS = false, + AWRE = true, + ARRE = true, + TB = false, + RC = false, + EER = true, + PER = false, + DTE = true, + DCR = false, + ReadRetryCount = 32 + }; - if(dcMode6.HasValue) - foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) - currentModePage = modePage; - } - - if(currentModePage == null) + currentModePage = new Modes.ModePage { - pg = new Modes.ModePage_01 - { - PS = false, - AWRE = true, - ARRE = true, - TB = false, - RC = false, - EER = true, - PER = false, - DTE = true, - DCR = false, - ReadRetryCount = 32 - }; + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01(pg) + }; + } - currentModePage = new Modes.ModePage + pg = new Modes.ModePage_01 + { + PS = false, + AWRE = false, + ARRE = false, + TB = true, + RC = false, + EER = true, + PER = false, + DTE = false, + DCR = false, + ReadRetryCount = 255 + }; + + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01(pg) - }; - } - - pg = new Modes.ModePage_01 - { - PS = false, - AWRE = false, - ARRE = false, - TB = true, - RC = false, - EER = true, - PER = false, - DTE = false, - DCR = false, - ReadRetryCount = 255 - }; - - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01(pg) - } } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - - _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); - sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - { - UpdateStatus?. - Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - - AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - - _dumpLog. - WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } - else - runningPersistent = true; - } + }; - InitProgress?.Invoke(); - repeatRetry: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); + md6 = Modes.EncodeMode6(md, _dev.ScsiType); - foreach(ulong badSector in tmpArray) + _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); + sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - _dumpLog.WriteLine("Aborted!"); + UpdateStatus?. + Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - break; - } + AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - PulseProgress?. - Invoke($"Retrying sector {badSector}, pass {pass}, {(runningPersistent ? "recovering partial data, " : "")}{(forward ? "forward" : "reverse")}"); - - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, - (uint)(umdStart + (badSector * 4)), 512, 0, 4, false, _dev.Timeout, - out double cmdDuration); - - totalDuration += cmdDuration; - - if(sense || _dev.Error) - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - if(!sense && - !_dev.Error) - { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputOptical.WriteSector(readBuffer, badSector); - - UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); - - _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); - } - else if(runningPersistent) - outputOptical.WriteSector(readBuffer, badSector); + _dumpLog. + WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto repeatRetry; - } - - if(runningPersistent && currentModePage.HasValue) - { - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - currentModePage.Value - } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - - _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); - _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); - } - - EndProgress?.Invoke(); - AaruConsole.WriteLine(); - } - #endregion Error handling - - _resume.BadBlocks.Sort(); - - foreach(ulong bad in _resume.BadBlocks) - _dumpLog.WriteLine("Sector {0} could not be read.", bad); - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - var metadata = new CommonTypes.Structs.ImageInfo - { - Application = "Aaru", - ApplicationVersion = Version.GetVersion(), - MediaPartNumber = mediaPartNumber - }; - - if(!outputOptical.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputOptical.ErrorMessage); - - outputOptical.SetDumpHardware(_resume.Tries); - - if(_preSidecar != null) - outputOptical.SetCicmMetadata(_preSidecar); - - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputOptical.Close(); - DateTime closeEnd = DateTime.Now; - _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); - - if(_aborted) - { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - return; + else + runningPersistent = true; } - double totalChkDuration = 0; + InitProgress?.Invoke(); + repeatRetry: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); - if(_metadata) - WriteOpticalSidecar(blockSize, blocks, dskType, null, null, 1, out totalChkDuration, null); + foreach(ulong badSector in tmpArray) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + _dumpLog.WriteLine("Aborted!"); - UpdateStatus?.Invoke(""); + break; + } - UpdateStatus?. - Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + PulseProgress?. + Invoke($"Retrying sector {badSector}, pass {pass}, {(runningPersistent ? "recovering partial data, " : "")}{(forward ? "forward" : "reverse")}"); - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, + (uint)(umdStart + (badSector * 4)), 512, 0, 4, false, _dev.Timeout, + out double cmdDuration); - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + totalDuration += cmdDuration; - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + if(sense || _dev.Error) + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - UpdateStatus?.Invoke(""); + if(!sense && + !_dev.Error) + { + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputOptical.WriteSector(readBuffer, badSector); - Statistics.AddMedia(dskType, true); + UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); + + _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); + } + else if(runningPersistent) + outputOptical.WriteSector(readBuffer, badSector); + } + + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) + { + pass++; + forward = !forward; + _resume.BadBlocks.Sort(); + + if(!forward) + _resume.BadBlocks.Reverse(); + + goto repeatRetry; + } + + if(runningPersistent && currentModePage.HasValue) + { + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + currentModePage.Value + } + }; + + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + + _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); + _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); + } + + EndProgress?.Invoke(); + AaruConsole.WriteLine(); } + #endregion Error handling + + _resume.BadBlocks.Sort(); + + foreach(ulong bad in _resume.BadBlocks) + _dumpLog.WriteLine("Sector {0} could not be read.", bad); + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion(), + MediaPartNumber = mediaPartNumber + }; + + if(!outputOptical.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputOptical.ErrorMessage); + + outputOptical.SetDumpHardware(_resume.Tries); + + if(_preSidecar != null) + outputOptical.SetCicmMetadata(_preSidecar); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputOptical.Close(); + DateTime closeEnd = DateTime.Now; + _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + if(_metadata) + WriteOpticalSidecar(blockSize, blocks, dskType, null, null, 1, out totalChkDuration, null); + + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + UpdateStatus?.Invoke(""); + + Statistics.AddMedia(dskType, true); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/ResumeSupport.cs b/Aaru.Core/Devices/Dumping/ResumeSupport.cs index 4b9b473aa..5d8a92797 100644 --- a/Aaru.Core/Devices/Dumping/ResumeSupport.cs +++ b/Aaru.Core/Devices/Dumping/ResumeSupport.cs @@ -38,138 +38,137 @@ using Schemas; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; using Version = Aaru.CommonTypes.Interop.Version; -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Implements resume support +internal static class ResumeSupport { - /// Implements resume support - internal static class ResumeSupport + /// Process resume + /// If drive is LBA + /// If media is removable from device + /// Media blocks + /// Device manufacturer + /// Device model + /// Device serial + /// Platform where the dump is made + /// Previous resume, or null + /// Current dumping hardware + /// Dumped extents + /// Firmware revision + /// Set to true if device is a streaming tape, false otherwise + /// Disable saving paths or serial numbers in images and logs + /// Force dump enabled + /// If device uses CHS addressing + /// + /// If the provided resume does not correspond with the current in + /// progress dump + /// + internal static void Process(bool isLba, bool removable, ulong blocks, string manufacturer, string model, + string serial, PlatformID platform, ref Resume resume, + ref DumpHardwareType currentTry, ref ExtentsULong extents, string firmware, + bool @private, bool force, bool isTape = false) { - /// Process resume - /// If drive is LBA - /// If media is removable from device - /// Media blocks - /// Device manufacturer - /// Device model - /// Device serial - /// Platform where the dump is made - /// Previous resume, or null - /// Current dumping hardware - /// Dumped extents - /// Firmware revision - /// Set to true if device is a streaming tape, false otherwise - /// Disable saving paths or serial numbers in images and logs - /// Force dump enabled - /// If device uses CHS addressing - /// - /// If the provided resume does not correspond with the current in - /// progress dump - /// - internal static void Process(bool isLba, bool removable, ulong blocks, string manufacturer, string model, - string serial, PlatformID platform, ref Resume resume, - ref DumpHardwareType currentTry, ref ExtentsULong extents, string firmware, - bool @private, bool force, bool isTape = false) + if(@private) + serial = null; + + if(resume != null) { - if(@private) - serial = null; + if(!isLba) + throw new NotImplementedException("Resuming CHS devices is currently not supported."); - if(resume != null) + if(resume.Tape != isTape) + throw new + InvalidOperationException($"Resume file specifies a {(resume.Tape ? "tape" : "not tape")} device but you're requesting to dump a {(isTape ? "tape" : "not tape")} device, not continuing..."); + + if(resume.Removable != removable && + !force) + throw new + InvalidOperationException($"Resume file specifies a {(resume.Removable ? "removable" : "non removable")} device but you're requesting to dump a {(removable ? "removable" : "non removable")} device, not continuing..."); + + if(!isTape && + resume.LastBlock != blocks - 1 && + !force) + throw new + InvalidOperationException($"Resume file specifies a device with {resume.LastBlock + 1} blocks but you're requesting to dump one with {blocks} blocks, not continuing..."); + + foreach(DumpHardwareType oldTry in resume.Tries) { - if(!isLba) - throw new NotImplementedException("Resuming CHS devices is currently not supported."); - - if(resume.Tape != isTape) - throw new - InvalidOperationException($"Resume file specifies a {(resume.Tape ? "tape" : "not tape")} device but you're requesting to dump a {(isTape ? "tape" : "not tape")} device, not continuing..."); - - if(resume.Removable != removable && + if(!removable && !force) - throw new - InvalidOperationException($"Resume file specifies a {(resume.Removable ? "removable" : "non removable")} device but you're requesting to dump a {(removable ? "removable" : "non removable")} device, not continuing..."); - - if(!isTape && - resume.LastBlock != blocks - 1 && - !force) - throw new - InvalidOperationException($"Resume file specifies a device with {resume.LastBlock + 1} blocks but you're requesting to dump one with {blocks} blocks, not continuing..."); - - foreach(DumpHardwareType oldTry in resume.Tries) { - if(!removable && - !force) - { - if(oldTry.Manufacturer != manufacturer) - throw new - InvalidOperationException($"Resume file specifies a device manufactured by {oldTry.Manufacturer} but you're requesting to dump one by {manufacturer}, not continuing..."); + if(oldTry.Manufacturer != manufacturer) + throw new + InvalidOperationException($"Resume file specifies a device manufactured by {oldTry.Manufacturer} but you're requesting to dump one by {manufacturer}, not continuing..."); - if(oldTry.Model != model) - throw new - InvalidOperationException($"Resume file specifies a device model {oldTry.Model} but you're requesting to dump model {model}, not continuing..."); + if(oldTry.Model != model) + throw new + InvalidOperationException($"Resume file specifies a device model {oldTry.Model} but you're requesting to dump model {model}, not continuing..."); - if(oldTry.Serial != serial) - throw new - InvalidOperationException($"Resume file specifies a device with serial {oldTry.Serial} but you're requesting to dump one with serial {serial}, not continuing..."); + if(oldTry.Serial != serial) + throw new + InvalidOperationException($"Resume file specifies a device with serial {oldTry.Serial} but you're requesting to dump one with serial {serial}, not continuing..."); - if(oldTry.Firmware != firmware) - throw new - InvalidOperationException($"Resume file specifies a device with firmware version {oldTry.Firmware} but you're requesting to dump one with firmware version {firmware}, not continuing..."); - } - - if(oldTry.Software == null) - throw new InvalidOperationException("Found corrupt resume file, cannot continue..."); - - if(oldTry.Software.Name != "Aaru" || - oldTry.Software.OperatingSystem != platform.ToString() || - oldTry.Software.Version != Version.GetVersion()) - continue; - - if(removable && (oldTry.Manufacturer != manufacturer || oldTry.Model != model || - oldTry.Serial != serial || oldTry.Firmware != firmware)) - continue; - - currentTry = oldTry; - extents = ExtentsConverter.FromMetadata(currentTry.Extents); - - break; + if(oldTry.Firmware != firmware) + throw new + InvalidOperationException($"Resume file specifies a device with firmware version {oldTry.Firmware} but you're requesting to dump one with firmware version {firmware}, not continuing..."); } - if(currentTry != null) - return; + if(oldTry.Software == null) + throw new InvalidOperationException("Found corrupt resume file, cannot continue..."); - currentTry = new DumpHardwareType - { - Software = CommonTypes.Metadata.Version.GetSoftwareType(), - Manufacturer = manufacturer, - Model = model, - Serial = serial, - Firmware = firmware - }; + if(oldTry.Software.Name != "Aaru" || + oldTry.Software.OperatingSystem != platform.ToString() || + oldTry.Software.Version != Version.GetVersion()) + continue; - resume.Tries.Add(currentTry); - extents = new ExtentsULong(); + if(removable && (oldTry.Manufacturer != manufacturer || oldTry.Model != model || + oldTry.Serial != serial || oldTry.Firmware != firmware)) + continue; + + currentTry = oldTry; + extents = ExtentsConverter.FromMetadata(currentTry.Extents); + + break; } - else + + if(currentTry != null) + return; + + currentTry = new DumpHardwareType { - resume = new Resume - { - Tries = new List(), - CreationDate = DateTime.UtcNow, - BadBlocks = new List(), - LastBlock = isTape ? 0 : blocks - 1, - Tape = isTape - }; + Software = CommonTypes.Metadata.Version.GetSoftwareType(), + Manufacturer = manufacturer, + Model = model, + Serial = serial, + Firmware = firmware + }; - currentTry = new DumpHardwareType - { - Software = CommonTypes.Metadata.Version.GetSoftwareType(), - Manufacturer = manufacturer, - Model = model, - Serial = serial, - Firmware = firmware - }; + resume.Tries.Add(currentTry); + extents = new ExtentsULong(); + } + else + { + resume = new Resume + { + Tries = new List(), + CreationDate = DateTime.UtcNow, + BadBlocks = new List(), + LastBlock = isTape ? 0 : blocks - 1, + Tape = isTape + }; - resume.Tries.Add(currentTry); - extents = new ExtentsULong(); - resume.Removable = removable; - } + currentTry = new DumpHardwareType + { + Software = CommonTypes.Metadata.Version.GetSoftwareType(), + Manufacturer = manufacturer, + Model = model, + Serial = serial, + Firmware = firmware + }; + + resume.Tries.Add(currentTry); + extents = new ExtentsULong(); + resume.Removable = removable; } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/SCSI.cs b/Aaru.Core/Devices/Dumping/SCSI.cs index 83b7c1492..cbdc0dd75 100644 --- a/Aaru.Core/Devices/Dumping/SCSI.cs +++ b/Aaru.Core/Devices/Dumping/SCSI.cs @@ -37,167 +37,159 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs.Devices.SCSI; using Aaru.Decoders.SCSI; -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Implements dumping SCSI and ATAPI devices +public partial class Dump { - /// Implements dumping SCSI and ATAPI devices - public partial class Dump + // TODO: Get cartridge serial number from Certance vendor EVPD + /// Dumps a SCSI Block Commands device or a Reduced Block Commands devices + void Scsi() { - // TODO: Get cartridge serial number from Certance vendor EVPD - /// Dumps a SCSI Block Commands device or a Reduced Block Commands devices - void Scsi() + int resets = 0; + + if(_dev.IsRemovable) { - int resets = 0; + InitProgress?.Invoke(); + deviceGotReset: + bool sense = _dev.ScsiTestUnitReady(out byte[] senseBuf, _dev.Timeout, out _); - if(_dev.IsRemovable) + if(sense) { - InitProgress?.Invoke(); - deviceGotReset: - bool sense = _dev.ScsiTestUnitReady(out byte[] senseBuf, _dev.Timeout, out _); + DecodedSense? decSense = Sense.Decode(senseBuf); - if(sense) + if(decSense.HasValue) { - DecodedSense? decSense = Sense.Decode(senseBuf); + ErrorMessage?. + Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); - if(decSense.HasValue) + _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); + + // Just retry, for 5 times + if(decSense.Value.ASC == 0x29) { - ErrorMessage?. - Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); + resets++; - _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); + if(resets < 5) + goto deviceGotReset; + } - // Just retry, for 5 times - if(decSense.Value.ASC == 0x29) + if(decSense.Value.ASC == 0x3A) + { + int leftRetries = 5; + + while(leftRetries > 0) { - resets++; + PulseProgress?.Invoke("Waiting for drive to become ready"); + Thread.Sleep(2000); + sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); - if(resets < 5) - goto deviceGotReset; + if(!sense) + break; + + decSense = Sense.Decode(senseBuf); + + if(decSense.HasValue) + { + ErrorMessage?. + Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); + + _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, + decSense.Value.ASCQ); + } + + leftRetries--; } - if(decSense.Value.ASC == 0x3A) + if(sense) { - int leftRetries = 5; + StoppingErrorMessage?.Invoke("Please insert media in drive"); - while(leftRetries > 0) - { - PulseProgress?.Invoke("Waiting for drive to become ready"); - Thread.Sleep(2000); - sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); - - if(!sense) - break; - - decSense = Sense.Decode(senseBuf); - - if(decSense.HasValue) - { - ErrorMessage?. - Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); - - _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, - decSense.Value.ASCQ); - } - - leftRetries--; - } - - if(sense) - { - StoppingErrorMessage?.Invoke("Please insert media in drive"); - - return; - } - } - else if(decSense.Value.ASC == 0x04 && - decSense.Value.ASCQ == 0x01) - { - int leftRetries = 50; - - while(leftRetries > 0) - { - PulseProgress?.Invoke("Waiting for drive to become ready"); - Thread.Sleep(2000); - sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); - - if(!sense) - break; - - decSense = Sense.Decode(senseBuf); - - if(decSense.HasValue) - { - ErrorMessage?. - Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, - decSense.Value.ASCQ); - } - - leftRetries--; - } - - if(sense) - { - StoppingErrorMessage?. - Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); - - return; - } - } - /*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00) - { - if (!deviceReset) - { - deviceReset = true; - ErrorMessage?.Invoke("Device did reset, retrying..."); - goto retryTestReady; - } - - StoppingErrorMessage?.Invoke(string.Format("Error testing unit was ready:\n{0}", - Decoders.SCSI.Sense.PrettifySense(senseBuf))); return; - }*/ - // These should be trapped by the OS but seems in some cases they're not - else if(decSense.Value.ASC == 0x28) - { - int leftRetries = 10; - - while(leftRetries > 0) - { - PulseProgress?.Invoke("Waiting for drive to become ready"); - Thread.Sleep(2000); - sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); - - if(!sense) - break; - - decSense = Sense.Decode(senseBuf); - - if(decSense.HasValue) - { - ErrorMessage?. - Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, - decSense.Value.ASCQ); - } - - leftRetries--; - } - - if(sense) - { - StoppingErrorMessage?. - Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); - - return; - } } - else + } + else if(decSense.Value.ASC == 0x04 && + decSense.Value.ASCQ == 0x01) + { + int leftRetries = 50; + + while(leftRetries > 0) + { + PulseProgress?.Invoke("Waiting for drive to become ready"); + Thread.Sleep(2000); + sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); + + if(!sense) + break; + + decSense = Sense.Decode(senseBuf); + + if(decSense.HasValue) + { + ErrorMessage?. + Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, + decSense.Value.ASCQ); + } + + leftRetries--; + } + + if(sense) + { + StoppingErrorMessage?. + Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); + + return; + } + } + /*else if (decSense.Value.ASC == 0x29 && decSense.Value.ASCQ == 0x00) + { + if (!deviceReset) + { + deviceReset = true; + ErrorMessage?.Invoke("Device did reset, retrying..."); + goto retryTestReady; + } + + StoppingErrorMessage?.Invoke(string.Format("Error testing unit was ready:\n{0}", + Decoders.SCSI.Sense.PrettifySense(senseBuf))); + return; + }*/ + // These should be trapped by the OS but seems in some cases they're not + else if(decSense.Value.ASC == 0x28) + { + int leftRetries = 10; + + while(leftRetries > 0) + { + PulseProgress?.Invoke("Waiting for drive to become ready"); + Thread.Sleep(2000); + sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); + + if(!sense) + break; + + decSense = Sense.Decode(senseBuf); + + if(decSense.HasValue) + { + ErrorMessage?. + Invoke($"Device not ready. Sense {decSense.Value.SenseKey} ASC {decSense.Value.ASC:X2}h ASCQ {decSense.Value.ASCQ:X2}h"); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, + decSense.Value.ASCQ); + } + + leftRetries--; + } + + if(sense) { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); @@ -207,51 +199,58 @@ namespace Aaru.Core.Devices.Dumping } else { - StoppingErrorMessage?.Invoke("Unknown testing unit was ready."); + StoppingErrorMessage?. + Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return; } } - - EndProgress?.Invoke(); - } - - switch(_dev.ScsiType) - { - case PeripheralDeviceTypes.SequentialAccess: - if(_dumpRaw) - { - StoppingErrorMessage?.Invoke("Tapes cannot be dumped raw."); - - return; - } - - if(_outputPlugin is IWritableTapeImage) - Ssc(); - else - StoppingErrorMessage?. - Invoke("The specified plugin does not support storing streaming tape images."); + else + { + StoppingErrorMessage?.Invoke("Unknown testing unit was ready."); return; - case PeripheralDeviceTypes.MultiMediaDevice: - if(_outputPlugin is IWritableOpticalImage) - Mmc(); - else - StoppingErrorMessage?. - Invoke("The specified plugin does not support storing optical disc images."); + } + } + + EndProgress?.Invoke(); + } + + switch(_dev.ScsiType) + { + case PeripheralDeviceTypes.SequentialAccess: + if(_dumpRaw) + { + StoppingErrorMessage?.Invoke("Tapes cannot be dumped raw."); return; - case PeripheralDeviceTypes.BridgingExpander - when _dev.Model.StartsWith("MDM", StringComparison.InvariantCulture) || - _dev.Model.StartsWith("MDH", StringComparison.InvariantCulture): - MiniDisc(); + } - break; - default: - Sbc(null, MediaType.Unknown, false); + if(_outputPlugin is IWritableTapeImage) + Ssc(); + else + StoppingErrorMessage?. + Invoke("The specified plugin does not support storing streaming tape images."); - break; - } + return; + case PeripheralDeviceTypes.MultiMediaDevice: + if(_outputPlugin is IWritableOpticalImage) + Mmc(); + else + StoppingErrorMessage?. + Invoke("The specified plugin does not support storing optical disc images."); + + return; + case PeripheralDeviceTypes.BridgingExpander + when _dev.Model.StartsWith("MDM", StringComparison.InvariantCulture) || + _dev.Model.StartsWith("MDH", StringComparison.InvariantCulture): + MiniDisc(); + + break; + default: + Sbc(null, MediaType.Unknown, false); + + break; } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/SSC.cs b/Aaru.Core/Devices/Dumping/SSC.cs index dbe67e838..cbe02c2d9 100644 --- a/Aaru.Core/Devices/Dumping/SSC.cs +++ b/Aaru.Core/Devices/Dumping/SSC.cs @@ -53,67 +53,144 @@ using Version = Aaru.CommonTypes.Interop.Version; // ReSharper disable JoinDeclarationAndInitializer -namespace Aaru.Core.Devices.Dumping -{ - partial class Dump - { - /// Dumps the tape from a SCSI Streaming device - void Ssc() - { - DecodedSense? decSense; - bool sense; - uint blockSize; - ulong blocks = 0; - MediaType dskType = MediaType.Unknown; - DateTime start; - DateTime end; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; - var outputTape = _outputPlugin as IWritableTapeImage; +namespace Aaru.Core.Devices.Dumping; - _dev.RequestSense(out byte[] senseBuf, _dev.Timeout, out double duration); +partial class Dump +{ + /// Dumps the tape from a SCSI Streaming device + void Ssc() + { + DecodedSense? decSense; + bool sense; + uint blockSize; + ulong blocks = 0; + MediaType dskType = MediaType.Unknown; + DateTime start; + DateTime end; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + var outputTape = _outputPlugin as IWritableTapeImage; + + _dev.RequestSense(out byte[] senseBuf, _dev.Timeout, out double duration); + decSense = Sense.Decode(senseBuf); + + InitProgress?.Invoke(); + + if(decSense.HasValue && + decSense.Value.SenseKey != SenseKeys.NoSense) + { + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, + decSense.Value.ASC, decSense.Value.ASCQ); + + StoppingErrorMessage?.Invoke("Drive has status error, please correct. Sense follows..." + + Environment.NewLine + decSense.Value.Description); + + return; + } + + // Not in BOM/P + if(decSense is { ASC: 0x00 } && + decSense.Value.ASCQ != 0x00 && + decSense.Value.ASCQ != 0x04 && + decSense.Value.SenseKey != SenseKeys.IllegalRequest) + { + _dumpLog.WriteLine("Rewinding, please wait..."); + PulseProgress?.Invoke("Rewinding, please wait..."); + + // Rewind, let timeout apply + _dev.Rewind(out senseBuf, _dev.Timeout, out duration); + + // Still rewinding? + // TODO: Pause? + do + { + PulseProgress?.Invoke("Rewinding, please wait..."); + _dev.RequestSense(out senseBuf, _dev.Timeout, out duration); + decSense = Sense.Decode(senseBuf); + } while(decSense is { ASC: 0x00 } && + (decSense.Value.ASCQ == 0x1A || decSense.Value.ASCQ != 0x04 || decSense.Value.ASCQ != 0x00)); + + _dev.RequestSense(out senseBuf, _dev.Timeout, out duration); decSense = Sense.Decode(senseBuf); - InitProgress?.Invoke(); - + // And yet, did not rewind! if(decSense.HasValue && - decSense.Value.SenseKey != SenseKeys.NoSense) + ((decSense.Value.ASC == 0x00 && decSense.Value.ASCQ != 0x04 && decSense.Value.ASCQ != 0x00) || + decSense.Value.ASC != 0x00)) { + StoppingErrorMessage?.Invoke("Drive could not rewind, please correct. Sense follows..." + + Environment.NewLine + decSense.Value.Description); + + _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); - StoppingErrorMessage?.Invoke("Drive has status error, please correct. Sense follows..." + - Environment.NewLine + decSense.Value.Description); + return; + } + } + + // Check position + sense = _dev.ReadPosition(out byte[] cmdBuf, out senseBuf, SscPositionForms.Short, _dev.Timeout, + out duration); + + if(sense) + { + // READ POSITION is mandatory starting SCSI-2, so do not cry if the drive does not recognize the command (SCSI-1 or earlier) + // Anyway, <=SCSI-1 tapes do not support partitions + decSense = Sense.Decode(senseBuf); + + if(decSense.HasValue && + ((decSense.Value.ASC == 0x20 && decSense.Value.ASCQ != 0x00) || + (decSense.Value.ASC != 0x20 && decSense.Value.SenseKey != SenseKeys.IllegalRequest))) + { + StoppingErrorMessage?.Invoke("Could not get position. Sense follows..." + Environment.NewLine + + decSense.Value.Description); + + _dumpLog.WriteLine("Could not get position. Sense follows..."); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, + decSense.Value.ASC, decSense.Value.ASCQ); return; } - - // Not in BOM/P - if(decSense is { ASC: 0x00 } && - decSense.Value.ASCQ != 0x00 && - decSense.Value.ASCQ != 0x04 && - decSense.Value.SenseKey != SenseKeys.IllegalRequest) + } + else + { + // Not in partition 0 + if(cmdBuf[1] != 0) { - _dumpLog.WriteLine("Rewinding, please wait..."); - PulseProgress?.Invoke("Rewinding, please wait..."); + UpdateStatus?.Invoke("Drive not in partition 0. Rewinding, please wait..."); + _dumpLog.WriteLine("Drive not in partition 0. Rewinding, please wait..."); // Rewind, let timeout apply - _dev.Rewind(out senseBuf, _dev.Timeout, out duration); + sense = _dev.Locate(out senseBuf, false, 0, 0, _dev.Timeout, out duration); + + if(sense) + { + StoppingErrorMessage?.Invoke("Drive could not rewind, please correct. Sense follows..." + + Environment.NewLine + decSense?.Description); + + _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense?.SenseKey, + decSense?.ASC, decSense?.ASCQ); + + return; + } // Still rewinding? // TODO: Pause? do { + Thread.Sleep(1000); PulseProgress?.Invoke("Rewinding, please wait..."); _dev.RequestSense(out senseBuf, _dev.Timeout, out duration); decSense = Sense.Decode(senseBuf); } while(decSense is { ASC: 0x00 } && - (decSense.Value.ASCQ == 0x1A || decSense.Value.ASCQ != 0x04 || decSense.Value.ASCQ != 0x00)); - - _dev.RequestSense(out senseBuf, _dev.Timeout, out duration); - decSense = Sense.Decode(senseBuf); + (decSense.Value.ASCQ == 0x1A || decSense.Value.ASCQ == 0x19)); // And yet, did not rewind! if(decSense.HasValue && @@ -125,263 +202,164 @@ namespace Aaru.Core.Devices.Dumping _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, - decSense.Value.ASC, decSense.Value.ASCQ); + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); return; } - } - // Check position - sense = _dev.ReadPosition(out byte[] cmdBuf, out senseBuf, SscPositionForms.Short, _dev.Timeout, - out duration); + sense = _dev.ReadPosition(out cmdBuf, out senseBuf, SscPositionForms.Short, _dev.Timeout, + out duration); - if(sense) - { - // READ POSITION is mandatory starting SCSI-2, so do not cry if the drive does not recognize the command (SCSI-1 or earlier) - // Anyway, <=SCSI-1 tapes do not support partitions - decSense = Sense.Decode(senseBuf); - - if(decSense.HasValue && - ((decSense.Value.ASC == 0x20 && decSense.Value.ASCQ != 0x00) || - (decSense.Value.ASC != 0x20 && decSense.Value.SenseKey != SenseKeys.IllegalRequest))) + if(sense) { - StoppingErrorMessage?.Invoke("Could not get position. Sense follows..." + Environment.NewLine + - decSense.Value.Description); + decSense = Sense.Decode(senseBuf); - _dumpLog.WriteLine("Could not get position. Sense follows..."); + StoppingErrorMessage?.Invoke("Drive could not rewind, please correct. Sense follows..." + + Environment.NewLine + decSense?.Description); - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, - decSense.Value.ASC, decSense.Value.ASCQ); + _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense?.SenseKey, + decSense?.ASC, decSense?.ASCQ); return; } - } - else - { - // Not in partition 0 + + // Still not in partition 0!!!? if(cmdBuf[1] != 0) { - UpdateStatus?.Invoke("Drive not in partition 0. Rewinding, please wait..."); - _dumpLog.WriteLine("Drive not in partition 0. Rewinding, please wait..."); + StoppingErrorMessage?.Invoke("Drive could not rewind to partition 0 but no error occurred..."); + _dumpLog.WriteLine("Drive could not rewind to partition 0 but no error occurred..."); - // Rewind, let timeout apply - sense = _dev.Locate(out senseBuf, false, 0, 0, _dev.Timeout, out duration); - - if(sense) - { - StoppingErrorMessage?.Invoke("Drive could not rewind, please correct. Sense follows..." + - Environment.NewLine + decSense?.Description); - - _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense?.SenseKey, - decSense?.ASC, decSense?.ASCQ); - - return; - } - - // Still rewinding? - // TODO: Pause? - do - { - Thread.Sleep(1000); - PulseProgress?.Invoke("Rewinding, please wait..."); - _dev.RequestSense(out senseBuf, _dev.Timeout, out duration); - decSense = Sense.Decode(senseBuf); - } while(decSense is { ASC: 0x00 } && - (decSense.Value.ASCQ == 0x1A || decSense.Value.ASCQ == 0x19)); - - // And yet, did not rewind! - if(decSense.HasValue && - ((decSense.Value.ASC == 0x00 && decSense.Value.ASCQ != 0x04 && decSense.Value.ASCQ != 0x00) || - decSense.Value.ASC != 0x00)) - { - StoppingErrorMessage?.Invoke("Drive could not rewind, please correct. Sense follows..." + - Environment.NewLine + decSense.Value.Description); - - _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); - - return; - } - - sense = _dev.ReadPosition(out cmdBuf, out senseBuf, SscPositionForms.Short, _dev.Timeout, - out duration); - - if(sense) - { - decSense = Sense.Decode(senseBuf); - - StoppingErrorMessage?.Invoke("Drive could not rewind, please correct. Sense follows..." + - Environment.NewLine + decSense?.Description); - - _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense?.SenseKey, - decSense?.ASC, decSense?.ASCQ); - - return; - } - - // Still not in partition 0!!!? - if(cmdBuf[1] != 0) - { - StoppingErrorMessage?.Invoke("Drive could not rewind to partition 0 but no error occurred..."); - _dumpLog.WriteLine("Drive could not rewind to partition 0 but no error occurred..."); - - return; - } + return; } } + } - EndProgress?.Invoke(); + EndProgress?.Invoke(); - byte scsiMediumTypeTape = 0; - byte scsiDensityCodeTape = 0; - byte[] mode6Data = null; - byte[] mode10Data = null; + byte scsiMediumTypeTape = 0; + byte scsiDensityCodeTape = 0; + byte[] mode6Data = null; + byte[] mode10Data = null; - UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); + UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); + sense = _dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, + 0xFF, 5, out duration); + + if(!sense || + _dev.Error) sense = _dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, - 0xFF, 5, out duration); + 0x00, 5, out duration); - if(!sense || - _dev.Error) - sense = _dev.ModeSense10(out cmdBuf, out senseBuf, false, true, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out duration); + Modes.DecodedMode? decMode = null; - Modes.DecodedMode? decMode = null; + if(!sense && + !_dev.Error) + if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) + decMode = Modes.DecodeMode10(cmdBuf, _dev.ScsiType); - if(!sense && - !_dev.Error) - if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) - decMode = Modes.DecodeMode10(cmdBuf, _dev.ScsiType); + UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); - UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); + sense = _dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, + out duration); - sense = _dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, - out duration); + if(sense || _dev.Error) + sense = _dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, + 5, out duration); - if(sense || _dev.Error) - sense = _dev.ModeSense6(out cmdBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, - 5, out duration); + if(sense || _dev.Error) + sense = _dev.ModeSense(out cmdBuf, out senseBuf, 5, out duration); - if(sense || _dev.Error) - sense = _dev.ModeSense(out cmdBuf, out senseBuf, 5, out duration); + if(!sense && + !_dev.Error) + if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) + decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType); - if(!sense && - !_dev.Error) - if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) - decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType); + // TODO: Check partitions page + if(decMode.HasValue) + { + scsiMediumTypeTape = (byte)decMode.Value.Header.MediumType; - // TODO: Check partitions page - if(decMode.HasValue) - { - scsiMediumTypeTape = (byte)decMode.Value.Header.MediumType; + if(decMode.Value.Header.BlockDescriptors?.Length > 0) + scsiDensityCodeTape = (byte)decMode.Value.Header.BlockDescriptors[0].Density; - if(decMode.Value.Header.BlockDescriptors?.Length > 0) - scsiDensityCodeTape = (byte)decMode.Value.Header.BlockDescriptors[0].Density; + blockSize = decMode.Value.Header.BlockDescriptors?[0].BlockLength ?? 0; - blockSize = decMode.Value.Header.BlockDescriptors?[0].BlockLength ?? 0; + UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); + } + else + blockSize = 1; - UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); - } - else - blockSize = 1; + if(!_dev.ReadBlockLimits(out cmdBuf, out senseBuf, _dev.Timeout, out _)) + { + BlockLimits.BlockLimitsData? blockLimits = BlockLimits.Decode(cmdBuf); - if(!_dev.ReadBlockLimits(out cmdBuf, out senseBuf, _dev.Timeout, out _)) - { - BlockLimits.BlockLimitsData? blockLimits = BlockLimits.Decode(cmdBuf); + if(blockLimits?.minBlockLen > blockSize) + blockSize = blockLimits?.minBlockLen ?? 0; + } - if(blockLimits?.minBlockLen > blockSize) - blockSize = blockLimits?.minBlockLen ?? 0; - } + if(blockSize == 0) + blockSize = 1; - if(blockSize == 0) - blockSize = 1; + if(dskType == MediaType.Unknown) + dskType = MediaTypeFromDevice.GetFromScsi((byte)_dev.ScsiType, _dev.Manufacturer, _dev.Model, + scsiMediumTypeTape, scsiDensityCodeTape, blocks, blockSize, + _dev.IsUsb, false); - if(dskType == MediaType.Unknown) - dskType = MediaTypeFromDevice.GetFromScsi((byte)_dev.ScsiType, _dev.Manufacturer, _dev.Model, - scsiMediumTypeTape, scsiDensityCodeTape, blocks, blockSize, - _dev.IsUsb, false); + if(dskType == MediaType.Unknown) + dskType = MediaType.UnknownTape; - if(dskType == MediaType.Unknown) - dskType = MediaType.UnknownTape; + UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); + UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumTypeTape}."); + UpdateStatus?.Invoke($"SCSI density type: {scsiDensityCodeTape}."); + UpdateStatus?.Invoke($"Media identified as {dskType}."); - UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); - UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumTypeTape}."); - UpdateStatus?.Invoke($"SCSI density type: {scsiDensityCodeTape}."); - UpdateStatus?.Invoke($"Media identified as {dskType}."); + _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); + _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumTypeTape); + _dumpLog.WriteLine("SCSI density type: {0}.", scsiDensityCodeTape); + _dumpLog.WriteLine("Media identified as {0}.", dskType); - _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); - _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumTypeTape); - _dumpLog.WriteLine("SCSI density type: {0}.", scsiDensityCodeTape); - _dumpLog.WriteLine("Media identified as {0}.", dskType); + bool endOfMedia = false; + ulong currentBlock = 0; + uint currentFile = 0; + byte currentPartition = 0; + byte totalPartitions = 1; // TODO: Handle partitions. + bool fixedLen = false; + uint transferLen = blockSize; - bool endOfMedia = false; - ulong currentBlock = 0; - uint currentFile = 0; - byte currentPartition = 0; - byte totalPartitions = 1; // TODO: Handle partitions. - bool fixedLen = false; - uint transferLen = blockSize; + firstRead: - firstRead: + sense = _dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, _dev.Timeout, + out duration); - sense = _dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, _dev.Timeout, - out duration); + if(sense) + { + decSense = Sense.Decode(senseBuf); - if(sense) - { - decSense = Sense.Decode(senseBuf); + if(decSense.HasValue) + if(decSense.Value.SenseKey == SenseKeys.IllegalRequest) + { + sense = _dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, _dev.Timeout, out duration); - if(decSense.HasValue) - if(decSense.Value.SenseKey == SenseKeys.IllegalRequest) + if(sense) { - sense = _dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, _dev.Timeout, out duration); + decSense = Sense.Decode(senseBuf); - if(sense) + bool eom = decSense?.Fixed?.EOM == true; + + if(decSense?.Descriptor != null && + decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] sscDescriptor)) + Sense.DecodeDescriptor04(sscDescriptor, out _, out eom, out _); + + if(!eom) { - decSense = Sense.Decode(senseBuf); - - bool eom = decSense?.Fixed?.EOM == true; - - if(decSense?.Descriptor != null && - decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] sscDescriptor)) - Sense.DecodeDescriptor04(sscDescriptor, out _, out eom, out _); - - if(!eom) - { - StoppingErrorMessage?.Invoke("Drive could not return back. Sense follows..." + - Environment.NewLine + decSense.Value.Description); - - _dumpLog.WriteLine("Drive could not return back. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); - - return; - } - } - - fixedLen = true; - transferLen = 1; - - sense = _dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, - _dev.Timeout, out duration); - - if(sense) - { - decSense = Sense.Decode(senseBuf); - - StoppingErrorMessage?.Invoke("Drive could not read. Sense follows..." + + StoppingErrorMessage?.Invoke("Drive could not return back. Sense follows..." + Environment.NewLine + decSense.Value.Description); - _dumpLog.WriteLine("Drive could not read. Sense follows..."); + _dumpLog.WriteLine("Drive could not return back. Sense follows..."); _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); @@ -389,59 +367,19 @@ namespace Aaru.Core.Devices.Dumping return; } } - else if(decSense.Value.ASC == 0x00 && - decSense.Value.ASCQ == 0x00) + + fixedLen = true; + transferLen = 1; + + sense = _dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, + _dev.Timeout, out duration); + + if(sense) { - bool ili = decSense.Value.Fixed?.ILI == true; - bool valid = decSense.Value.Fixed?.InformationValid == true; - uint information = decSense.Value.Fixed?.Information ?? 0; + decSense = Sense.Decode(senseBuf); - if(decSense.Value.Descriptor.HasValue) - { - valid = decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00); - - if(valid) - information = (uint)Sense.DecodeDescriptor00(desc00); - - if(decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] desc04)) - Sense.DecodeDescriptor04(desc04, out _, out _, out ili); - } - - if(ili && valid) - { - blockSize = (uint)((int)blockSize - - BitConverter.ToInt32(BitConverter.GetBytes(information), 0)); - - transferLen = blockSize; - - UpdateStatus?.Invoke($"Blocksize changed to {blockSize} bytes at block {currentBlock}"); - _dumpLog.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock); - - sense = _dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, _dev.Timeout, - out duration); - - totalDuration += duration; - - if(sense) - { - decSense = Sense.Decode(senseBuf); - - StoppingErrorMessage?.Invoke("Drive could not go back one block. Sense follows..." + - Environment.NewLine + decSense.Value.Description); - - _dumpLog.WriteLine("Drive could not go back one block. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); - - return; - } - - goto firstRead; - } - - StoppingErrorMessage?.Invoke("Drive could not read. Sense follows..." + Environment.NewLine + - decSense.Value.Description); + StoppingErrorMessage?.Invoke("Drive could not read. Sense follows..." + + Environment.NewLine + decSense.Value.Description); _dumpLog.WriteLine("Drive could not read. Sense follows..."); @@ -450,263 +388,307 @@ namespace Aaru.Core.Devices.Dumping return; } - else - { - StoppingErrorMessage?.Invoke("Drive could not read. Sense follows..." + Environment.NewLine + - decSense.Value.Description); - - _dumpLog.WriteLine("Drive could not read. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); - - return; - } - else - { - StoppingErrorMessage?.Invoke("Cannot read device, don't know why, exiting..."); - _dumpLog.WriteLine("Cannot read device, don't know why, exiting..."); - - return; } - } - - sense = _dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, _dev.Timeout, out duration); - - if(sense) - { - decSense = Sense.Decode(senseBuf); - - bool eom = decSense?.Fixed?.EOM == true; - - if(decSense.Value.Descriptor.HasValue && - decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] desc04)) - Sense.DecodeDescriptor04(desc04, out _, out eom, out _); - - if(!eom) + else if(decSense.Value.ASC == 0x00 && + decSense.Value.ASCQ == 0x00) { - StoppingErrorMessage?.Invoke("Drive could not return back. Sense follows..." + Environment.NewLine + + bool ili = decSense.Value.Fixed?.ILI == true; + bool valid = decSense.Value.Fixed?.InformationValid == true; + uint information = decSense.Value.Fixed?.Information ?? 0; + + if(decSense.Value.Descriptor.HasValue) + { + valid = decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00); + + if(valid) + information = (uint)Sense.DecodeDescriptor00(desc00); + + if(decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] desc04)) + Sense.DecodeDescriptor04(desc04, out _, out _, out ili); + } + + if(ili && valid) + { + blockSize = (uint)((int)blockSize - + BitConverter.ToInt32(BitConverter.GetBytes(information), 0)); + + transferLen = blockSize; + + UpdateStatus?.Invoke($"Blocksize changed to {blockSize} bytes at block {currentBlock}"); + _dumpLog.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock); + + sense = _dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, _dev.Timeout, + out duration); + + totalDuration += duration; + + if(sense) + { + decSense = Sense.Decode(senseBuf); + + StoppingErrorMessage?.Invoke("Drive could not go back one block. Sense follows..." + + Environment.NewLine + decSense.Value.Description); + + _dumpLog.WriteLine("Drive could not go back one block. Sense follows..."); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); + + return; + } + + goto firstRead; + } + + StoppingErrorMessage?.Invoke("Drive could not read. Sense follows..." + Environment.NewLine + decSense.Value.Description); - _dumpLog.WriteLine("Drive could not return back. Sense follows..."); + _dumpLog.WriteLine("Drive could not read. Sense follows..."); - _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, - decSense.Value.ASC, decSense.Value.ASCQ); + _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); return; } - } + else + { + StoppingErrorMessage?.Invoke("Drive could not read. Sense follows..." + Environment.NewLine + + decSense.Value.Description); - DumpHardwareType currentTry = null; - ExtentsULong extents = null; + _dumpLog.WriteLine("Drive could not read. Sense follows..."); - ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, - _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, - _private, _force, true); + _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); - if(currentTry == null || - extents == null) + return; + } + else { - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + StoppingErrorMessage?.Invoke("Cannot read device, don't know why, exiting..."); + _dumpLog.WriteLine("Cannot read device, don't know why, exiting..."); return; } + } - bool canLocateLong = false; - bool canLocate = false; + sense = _dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, _dev.Timeout, out duration); - UpdateStatus?.Invoke("Positioning tape to block 1."); - _dumpLog.WriteLine("Positioning tape to block 1"); + if(sense) + { + decSense = Sense.Decode(senseBuf); - sense = _dev.Locate16(out senseBuf, 1, _dev.Timeout, out _); + bool eom = decSense?.Fixed?.EOM == true; + + if(decSense.Value.Descriptor.HasValue && + decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] desc04)) + Sense.DecodeDescriptor04(desc04, out _, out eom, out _); + + if(!eom) + { + StoppingErrorMessage?.Invoke("Drive could not return back. Sense follows..." + Environment.NewLine + + decSense.Value.Description); + + _dumpLog.WriteLine("Drive could not return back. Sense follows..."); + + _dumpLog.WriteLine("Device not ready. Sense {0} ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, + decSense.Value.ASC, decSense.Value.ASCQ); + + return; + } + } + + DumpHardwareType currentTry = null; + ExtentsULong extents = null; + + ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, + _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, + _private, _force, true); + + if(currentTry == null || + extents == null) + { + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + + return; + } + + bool canLocateLong = false; + bool canLocate = false; + + UpdateStatus?.Invoke("Positioning tape to block 1."); + _dumpLog.WriteLine("Positioning tape to block 1"); + + sense = _dev.Locate16(out senseBuf, 1, _dev.Timeout, out _); + + if(!sense) + { + sense = _dev.ReadPositionLong(out cmdBuf, out senseBuf, _dev.Timeout, out _); if(!sense) { - sense = _dev.ReadPositionLong(out cmdBuf, out senseBuf, _dev.Timeout, out _); + ulong position = Swapping.Swap(BitConverter.ToUInt64(cmdBuf, 8)); - if(!sense) + if(position == 1) { - ulong position = Swapping.Swap(BitConverter.ToUInt64(cmdBuf, 8)); - - if(position == 1) - { - canLocateLong = true; - UpdateStatus?.Invoke("LOCATE LONG works."); - _dumpLog.WriteLine("LOCATE LONG works."); - } + canLocateLong = true; + UpdateStatus?.Invoke("LOCATE LONG works."); + _dumpLog.WriteLine("LOCATE LONG works."); } } + } - sense = _dev.Locate(out senseBuf, 1, _dev.Timeout, out _); + sense = _dev.Locate(out senseBuf, 1, _dev.Timeout, out _); + + if(!sense) + { + sense = _dev.ReadPosition(out cmdBuf, out senseBuf, _dev.Timeout, out _); if(!sense) { - sense = _dev.ReadPosition(out cmdBuf, out senseBuf, _dev.Timeout, out _); + ulong position = Swapping.Swap(BitConverter.ToUInt32(cmdBuf, 4)); + + if(position == 1) + { + canLocate = true; + UpdateStatus?.Invoke("LOCATE works."); + _dumpLog.WriteLine("LOCATE works."); + } + } + } + + if(_resume.NextBlock > 0) + { + UpdateStatus?.Invoke($"Positioning tape to block {_resume.NextBlock}."); + _dumpLog.WriteLine("Positioning tape to block {0}.", _resume.NextBlock); + + if(canLocateLong) + { + sense = _dev.Locate16(out senseBuf, _resume.NextBlock, _dev.Timeout, out _); if(!sense) { - ulong position = Swapping.Swap(BitConverter.ToUInt32(cmdBuf, 4)); + sense = _dev.ReadPositionLong(out cmdBuf, out senseBuf, _dev.Timeout, out _); - if(position == 1) + if(sense) { - canLocate = true; - UpdateStatus?.Invoke("LOCATE works."); - _dumpLog.WriteLine("LOCATE works."); + if(!_force) + { + _dumpLog. + WriteLine("Could not check current position, unable to resume. If you want to continue use force."); + + StoppingErrorMessage?. + Invoke("Could not check current position, unable to resume. If you want to continue use force."); + + return; + } + + _dumpLog. + WriteLine("Could not check current position, unable to resume. Dumping from the start."); + + ErrorMessage?. + Invoke("Could not check current position, unable to resume. Dumping from the start."); + + canLocateLong = false; } - } - } - - if(_resume.NextBlock > 0) - { - UpdateStatus?.Invoke($"Positioning tape to block {_resume.NextBlock}."); - _dumpLog.WriteLine("Positioning tape to block {0}.", _resume.NextBlock); - - if(canLocateLong) - { - sense = _dev.Locate16(out senseBuf, _resume.NextBlock, _dev.Timeout, out _); - - if(!sense) + else { - sense = _dev.ReadPositionLong(out cmdBuf, out senseBuf, _dev.Timeout, out _); + ulong position = Swapping.Swap(BitConverter.ToUInt64(cmdBuf, 8)); - if(sense) + if(position != _resume.NextBlock) { if(!_force) { _dumpLog. - WriteLine("Could not check current position, unable to resume. If you want to continue use force."); + WriteLine("Current position is not as expected, unable to resume. If you want to continue use force."); StoppingErrorMessage?. - Invoke("Could not check current position, unable to resume. If you want to continue use force."); + Invoke("Current position is not as expected, unable to resume. If you want to continue use force."); return; } _dumpLog. - WriteLine("Could not check current position, unable to resume. Dumping from the start."); + WriteLine("Current position is not as expected, unable to resume. Dumping from the start."); ErrorMessage?. - Invoke("Could not check current position, unable to resume. Dumping from the start."); + Invoke("Current position is not as expected, unable to resume. Dumping from the start."); canLocateLong = false; } - else - { - ulong position = Swapping.Swap(BitConverter.ToUInt64(cmdBuf, 8)); - - if(position != _resume.NextBlock) - { - if(!_force) - { - _dumpLog. - WriteLine("Current position is not as expected, unable to resume. If you want to continue use force."); - - StoppingErrorMessage?. - Invoke("Current position is not as expected, unable to resume. If you want to continue use force."); - - return; - } - - _dumpLog. - WriteLine("Current position is not as expected, unable to resume. Dumping from the start."); - - ErrorMessage?. - Invoke("Current position is not as expected, unable to resume. Dumping from the start."); - - canLocateLong = false; - } - } } - else + } + else + { + if(!_force) + { + _dumpLog. + WriteLine("Cannot reposition tape, unable to resume. If you want to continue use force."); + + StoppingErrorMessage?. + Invoke("Cannot reposition tape, unable to resume. If you want to continue use force."); + + return; + } + + _dumpLog.WriteLine("Cannot reposition tape, unable to resume. Dumping from the start."); + ErrorMessage?.Invoke("Cannot reposition tape, unable to resume. Dumping from the start."); + canLocateLong = false; + } + } + else if(canLocate) + { + sense = _dev.Locate(out senseBuf, (uint)_resume.NextBlock, _dev.Timeout, out _); + + if(!sense) + { + sense = _dev.ReadPosition(out cmdBuf, out senseBuf, _dev.Timeout, out _); + + if(sense) { if(!_force) { _dumpLog. - WriteLine("Cannot reposition tape, unable to resume. If you want to continue use force."); + WriteLine("Could not check current position, unable to resume. If you want to continue use force."); StoppingErrorMessage?. - Invoke("Cannot reposition tape, unable to resume. If you want to continue use force."); + Invoke("Could not check current position, unable to resume. If you want to continue use force."); return; } - _dumpLog.WriteLine("Cannot reposition tape, unable to resume. Dumping from the start."); - ErrorMessage?.Invoke("Cannot reposition tape, unable to resume. Dumping from the start."); - canLocateLong = false; + _dumpLog. + WriteLine("Could not check current position, unable to resume. Dumping from the start."); + + ErrorMessage?. + Invoke("Could not check current position, unable to resume. Dumping from the start."); + + canLocate = false; } - } - else if(canLocate) - { - sense = _dev.Locate(out senseBuf, (uint)_resume.NextBlock, _dev.Timeout, out _); - - if(!sense) + else { - sense = _dev.ReadPosition(out cmdBuf, out senseBuf, _dev.Timeout, out _); + ulong position = Swapping.Swap(BitConverter.ToUInt32(cmdBuf, 4)); - if(sense) + if(position != _resume.NextBlock) { if(!_force) { _dumpLog. - WriteLine("Could not check current position, unable to resume. If you want to continue use force."); + WriteLine("Current position is not as expected, unable to resume. If you want to continue use force."); StoppingErrorMessage?. - Invoke("Could not check current position, unable to resume. If you want to continue use force."); + Invoke("Current position is not as expected, unable to resume. If you want to continue use force."); return; } _dumpLog. - WriteLine("Could not check current position, unable to resume. Dumping from the start."); + WriteLine("Current position is not as expected, unable to resume. Dumping from the start."); ErrorMessage?. - Invoke("Could not check current position, unable to resume. Dumping from the start."); + Invoke("Current position is not as expected, unable to resume. Dumping from the start."); canLocate = false; } - else - { - ulong position = Swapping.Swap(BitConverter.ToUInt32(cmdBuf, 4)); - - if(position != _resume.NextBlock) - { - if(!_force) - { - _dumpLog. - WriteLine("Current position is not as expected, unable to resume. If you want to continue use force."); - - StoppingErrorMessage?. - Invoke("Current position is not as expected, unable to resume. If you want to continue use force."); - - return; - } - - _dumpLog. - WriteLine("Current position is not as expected, unable to resume. Dumping from the start."); - - ErrorMessage?. - Invoke("Current position is not as expected, unable to resume. Dumping from the start."); - - canLocate = false; - } - } - } - else - { - if(!_force) - { - _dumpLog. - WriteLine("Cannot reposition tape, unable to resume. If you want to continue use force."); - - StoppingErrorMessage?. - Invoke("Cannot reposition tape, unable to resume. If you want to continue use force."); - - return; - } - - _dumpLog.WriteLine("Cannot reposition tape, unable to resume. Dumping from the start."); - ErrorMessage?.Invoke("Cannot reposition tape, unable to resume. Dumping from the start."); - canLocate = false; } } else @@ -729,108 +711,427 @@ namespace Aaru.Core.Devices.Dumping } else { - _ = canLocateLong ? _dev.Locate16(out senseBuf, false, 0, 0, _dev.Timeout, out duration) - : _dev.Locate(out senseBuf, false, 0, 0, _dev.Timeout, out duration); - - do + if(!_force) { - Thread.Sleep(1000); - PulseProgress?.Invoke("Rewinding, please wait..."); - _dev.RequestSense(out senseBuf, _dev.Timeout, out duration); - decSense = Sense.Decode(senseBuf); - } while(decSense is { ASC: 0x00 } && - (decSense.Value.ASCQ == 0x1A || decSense.Value.ASCQ == 0x19)); + _dumpLog. + WriteLine("Cannot reposition tape, unable to resume. If you want to continue use force."); - // And yet, did not rewind! - if(decSense.HasValue && - ((decSense.Value.ASC == 0x00 && decSense.Value.ASCQ != 0x00 && decSense.Value.ASCQ != 0x04) || - decSense.Value.ASC != 0x00)) - { - StoppingErrorMessage?.Invoke("Drive could not rewind, please correct. Sense follows..." + - Environment.NewLine + decSense.Value.Description); - - _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, - decSense.Value.ASC, decSense.Value.ASCQ); + StoppingErrorMessage?. + Invoke("Cannot reposition tape, unable to resume. If you want to continue use force."); return; } + + _dumpLog.WriteLine("Cannot reposition tape, unable to resume. Dumping from the start."); + ErrorMessage?.Invoke("Cannot reposition tape, unable to resume. Dumping from the start."); + canLocate = false; } + } + else + { + _ = canLocateLong ? _dev.Locate16(out senseBuf, false, 0, 0, _dev.Timeout, out duration) + : _dev.Locate(out senseBuf, false, 0, 0, _dev.Timeout, out duration); - bool ret = outputTape.SetTape(); - - // Cannot set image to tape mode - if(!ret) + do { - _dumpLog.WriteLine("Error setting output image in tape mode, not continuing."); - _dumpLog.WriteLine(outputTape.ErrorMessage); + Thread.Sleep(1000); + PulseProgress?.Invoke("Rewinding, please wait..."); + _dev.RequestSense(out senseBuf, _dev.Timeout, out duration); + decSense = Sense.Decode(senseBuf); + } while(decSense is { ASC: 0x00 } && + (decSense.Value.ASCQ == 0x1A || decSense.Value.ASCQ == 0x19)); - StoppingErrorMessage?.Invoke("Error setting output image in tape mode, not continuing." + - Environment.NewLine + outputTape.ErrorMessage); + // And yet, did not rewind! + if(decSense.HasValue && + ((decSense.Value.ASC == 0x00 && decSense.Value.ASCQ != 0x00 && decSense.Value.ASCQ != 0x04) || + decSense.Value.ASC != 0x00)) + { + StoppingErrorMessage?.Invoke("Drive could not rewind, please correct. Sense follows..." + + Environment.NewLine + decSense.Value.Description); + + _dumpLog.WriteLine("Drive could not rewind, please correct. Sense follows..."); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", decSense.Value.SenseKey, + decSense.Value.ASC, decSense.Value.ASCQ); return; } + } - ret = outputTape.Create(_outputPath, dskType, _formatOptions, 0, 0); + bool ret = outputTape.SetTape(); - // Cannot create image - if(!ret) - { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputTape.ErrorMessage); + // Cannot set image to tape mode + if(!ret) + { + _dumpLog.WriteLine("Error setting output image in tape mode, not continuing."); + _dumpLog.WriteLine(outputTape.ErrorMessage); - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + - outputTape.ErrorMessage); + StoppingErrorMessage?.Invoke("Error setting output image in tape mode, not continuing." + + Environment.NewLine + outputTape.ErrorMessage); - return; - } + return; + } - start = DateTime.UtcNow; - var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, 1, _private); - var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008); + ret = outputTape.Create(_outputPath, dskType, _formatOptions, 0, 0); - var currentTapeFile = new TapeFile - { - File = currentFile, - FirstBlock = currentBlock, - Partition = currentPartition - }; + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputTape.ErrorMessage); - var currentTapePartition = new TapePartition - { - Number = currentPartition, - FirstBlock = currentBlock - }; + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputTape.ErrorMessage); - if((canLocate || canLocateLong) && - _resume.NextBlock > 0) - { - currentBlock = _resume.NextBlock; + return; + } - currentTapeFile = - outputTape.Files.FirstOrDefault(f => f.LastBlock == - outputTape?. + start = DateTime.UtcNow; + var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, 1, _private); + var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0008); + + var currentTapeFile = new TapeFile + { + File = currentFile, + FirstBlock = currentBlock, + Partition = currentPartition + }; + + var currentTapePartition = new TapePartition + { + Number = currentPartition, + FirstBlock = currentBlock + }; + + if((canLocate || canLocateLong) && + _resume.NextBlock > 0) + { + currentBlock = _resume.NextBlock; + + currentTapeFile = + outputTape.Files.FirstOrDefault(f => f.LastBlock == + outputTape?. Files.Max(g => g.LastBlock)); - currentTapePartition = - outputTape.TapePartitions.FirstOrDefault(p => p.LastBlock == - outputTape?.TapePartitions.Max(g => g.LastBlock)); + currentTapePartition = + outputTape.TapePartitions.FirstOrDefault(p => p.LastBlock == + outputTape?.TapePartitions.Max(g => g.LastBlock)); + } + + if(mode6Data != null) + outputTape.WriteMediaTag(mode6Data, MediaTagType.SCSI_MODESENSE_6); + + if(mode10Data != null) + outputTape.WriteMediaTag(mode10Data, MediaTagType.SCSI_MODESENSE_10); + + DateTime timeSpeedStart = DateTime.UtcNow; + ulong currentSpeedSize = 0; + double imageWriteDuration = 0; + + InitProgress?.Invoke(); + + while(currentPartition < totalPartitions) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; } - if(mode6Data != null) - outputTape.WriteMediaTag(mode6Data, MediaTagType.SCSI_MODESENSE_6); + if(endOfMedia) + { + UpdateStatus?.Invoke($"Finished partition {currentPartition}"); + _dumpLog.WriteLine("Finished partition {0}", currentPartition); - if(mode10Data != null) - outputTape.WriteMediaTag(mode10Data, MediaTagType.SCSI_MODESENSE_10); + currentTapeFile.LastBlock = currentBlock - 1; - DateTime timeSpeedStart = DateTime.UtcNow; - ulong currentSpeedSize = 0; - double imageWriteDuration = 0; + if(currentTapeFile.LastBlock > currentTapeFile.FirstBlock) + outputTape.AddFile(currentTapeFile); + + currentTapePartition.LastBlock = currentBlock - 1; + outputTape.AddPartition(currentTapePartition); + + currentPartition++; + + if(currentPartition < totalPartitions) + { + currentFile++; + + currentTapeFile = new TapeFile + { + File = currentFile, + FirstBlock = currentBlock, + Partition = currentPartition + }; + + currentTapePartition = new TapePartition + { + Number = currentPartition, + FirstBlock = currentBlock + }; + + UpdateStatus?.Invoke($"Seeking to partition {currentPartition}"); + _dev.Locate(out senseBuf, false, currentPartition, 0, _dev.Timeout, out duration); + totalDuration += duration; + } + + continue; + } + + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + PulseProgress?.Invoke($"Reading block {currentBlock} ({currentSpeed:F3} MiB/sec.)"); + + sense = _dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, _dev.Timeout, + out duration); + + totalDuration += duration; + + if(sense && + senseBuf?.Length != 0 && + !ArrayHelpers.ArrayIsNullOrEmpty(senseBuf)) + { + decSense = Sense.Decode(senseBuf); + + bool ili = decSense?.Fixed?.ILI == true; + bool valid = decSense?.Fixed?.InformationValid == true; + uint information = decSense?.Fixed?.Information ?? 0; + bool eom = decSense?.Fixed?.EOM == true; + bool filemark = decSense?.Fixed?.Filemark == true; + + if(decSense?.Descriptor.HasValue == true) + { + if(decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) + { + valid = true; + information = (uint)Sense.DecodeDescriptor00(desc00); + } + + if(decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] desc04)) + Sense.DecodeDescriptor04(desc04, out filemark, out eom, out ili); + } + + if(decSense.Value.ASC == 0x00 && + decSense.Value.ASCQ == 0x00 && + ili && + valid) + { + blockSize = (uint)((int)blockSize - + BitConverter.ToInt32(BitConverter.GetBytes(information), 0)); + + if(!fixedLen) + transferLen = blockSize; + + UpdateStatus?.Invoke($"Blocksize changed to {blockSize} bytes at block {currentBlock}"); + _dumpLog.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock); + + sense = _dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, _dev.Timeout, out duration); + + totalDuration += duration; + + if(sense) + { + decSense = Sense.Decode(senseBuf); + + StoppingErrorMessage?.Invoke("Drive could not go back one block. Sense follows..." + + Environment.NewLine + decSense.Value.Description); + + outputTape.Close(); + _dumpLog.WriteLine("Drive could not go back one block. Sense follows..."); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); + + return; + } + + continue; + } + + switch(decSense.Value.SenseKey) + { + case SenseKeys.BlankCheck when currentBlock == 0: + StoppingErrorMessage?.Invoke("Cannot dump a blank tape..."); + outputTape.Close(); + _dumpLog.WriteLine("Cannot dump a blank tape..."); + + return; + + // For sure this is an end-of-tape/partition + case SenseKeys.BlankCheck when decSense.Value.ASC == 0x00 && + (decSense.Value.ASCQ == 0x02 || decSense.Value.ASCQ == 0x05 || + eom): + // TODO: Detect end of partition + endOfMedia = true; + UpdateStatus?.Invoke("Found end-of-tape/partition..."); + _dumpLog.WriteLine("Found end-of-tape/partition..."); + + continue; + case SenseKeys.BlankCheck: + StoppingErrorMessage?.Invoke("Blank block found, end of tape?..."); + endOfMedia = true; + _dumpLog.WriteLine("Blank block found, end of tape?..."); + + continue; + } + + if((decSense.Value.SenseKey == SenseKeys.NoSense || + decSense.Value.SenseKey == SenseKeys.RecoveredError) && + (decSense.Value.ASCQ == 0x02 || decSense.Value.ASCQ == 0x05 || eom)) + { + // TODO: Detect end of partition + endOfMedia = true; + UpdateStatus?.Invoke("Found end-of-tape/partition..."); + _dumpLog.WriteLine("Found end-of-tape/partition..."); + + continue; + } + + if((decSense.Value.SenseKey == SenseKeys.NoSense || + decSense.Value.SenseKey == SenseKeys.RecoveredError) && + (decSense.Value.ASCQ == 0x01 || filemark)) + { + currentTapeFile.LastBlock = currentBlock - 1; + outputTape.AddFile(currentTapeFile); + + currentFile++; + + currentTapeFile = new TapeFile + { + File = currentFile, + FirstBlock = currentBlock, + Partition = currentPartition + }; + + UpdateStatus?.Invoke($"Changed to file {currentFile} at block {currentBlock}"); + _dumpLog.WriteLine("Changed to file {0} at block {1}", currentFile, currentBlock); + + continue; + } + + if(decSense is null) + { + StoppingErrorMessage?. + Invoke($"Drive could not read block ${currentBlock}. Sense cannot be decoded, look at log for dump..."); + + _dumpLog.WriteLine($"Drive could not read block ${currentBlock}. Sense bytes follow..."); + _dumpLog.WriteLine(PrintHex.ByteArrayToHexArrayString(senseBuf, 32)); + } + else + { + StoppingErrorMessage?. + Invoke($"Drive could not read block ${currentBlock}. Sense follows...\n{decSense.Value.SenseKey} {decSense.Value.Description}"); + + _dumpLog.WriteLine($"Drive could not read block ${currentBlock}. Sense follows..."); + + _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", + decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); + } + + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + // Write empty data + DateTime writeStart = DateTime.Now; + outputTape.WriteSector(new byte[blockSize], currentBlock); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration); + ibgLog.Write(currentBlock, 0); + _resume.BadBlocks.Add(currentBlock); + } + else + { + mhddLog.Write(currentBlock, duration); + ibgLog.Write(currentBlock, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputTape.WriteSector(cmdBuf, currentBlock); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(currentBlock, 1, true); + } + + currentBlock++; + _resume.NextBlock++; + currentSpeedSize += blockSize; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = currentSpeedSize / (1048576 * elapsed); + currentSpeedSize = 0; + timeSpeedStart = DateTime.UtcNow; + } + + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + blocks = currentBlock + 1; + end = DateTime.UtcNow; + + // If not aborted this is added at the end of medium + if(_aborted) + { + currentTapeFile.LastBlock = currentBlock - 1; + outputTape.AddFile(currentTapeFile); + + currentTapePartition.LastBlock = currentBlock - 1; + outputTape.AddPartition(currentTapePartition); + } + + EndProgress?.Invoke(); + mhddLog.Close(); + + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + + #region Error handling + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _retryPasses > 0 && + (canLocate || canLocateLong)) + { + int pass = 1; + bool forward = false; + bool runningPersistent = false; + + Modes.ModePage? currentModePage = null; + + if(_persistent) + { + // TODO: Implement persistent + } InitProgress?.Invoke(); + repeatRetry: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); - while(currentPartition < totalPartitions) + foreach(ulong badBlock in tmpArray) { if(_aborted) { @@ -841,599 +1142,297 @@ namespace Aaru.Core.Devices.Dumping break; } - if(endOfMedia) + PulseProgress?.Invoke(string.Format("Retrying block {0}, pass {1}, {3}{2}", badBlock, pass, + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); + + UpdateStatus?.Invoke($"Positioning tape to block {badBlock}."); + _dumpLog.WriteLine($"Positioning tape to block {badBlock}."); + + if(canLocateLong) { - UpdateStatus?.Invoke($"Finished partition {currentPartition}"); - _dumpLog.WriteLine("Finished partition {0}", currentPartition); + sense = _dev.Locate16(out senseBuf, _resume.NextBlock, _dev.Timeout, out _); - currentTapeFile.LastBlock = currentBlock - 1; - - if(currentTapeFile.LastBlock > currentTapeFile.FirstBlock) - outputTape.AddFile(currentTapeFile); - - currentTapePartition.LastBlock = currentBlock - 1; - outputTape.AddPartition(currentTapePartition); - - currentPartition++; - - if(currentPartition < totalPartitions) + if(!sense) { - currentFile++; + sense = _dev.ReadPositionLong(out cmdBuf, out senseBuf, _dev.Timeout, out _); - currentTapeFile = new TapeFile + if(sense) { - File = currentFile, - FirstBlock = currentBlock, - Partition = currentPartition - }; + _dumpLog.WriteLine("Could not check current position, continuing."); + StoppingErrorMessage?.Invoke("Could not check current position, continuing."); - currentTapePartition = new TapePartition + continue; + } + + ulong position = Swapping.Swap(BitConverter.ToUInt64(cmdBuf, 8)); + + if(position != _resume.NextBlock) { - Number = currentPartition, - FirstBlock = currentBlock - }; + _dumpLog.WriteLine("Current position is not as expected, continuing."); + StoppingErrorMessage?.Invoke("Current position is not as expected, continuing."); - UpdateStatus?.Invoke($"Seeking to partition {currentPartition}"); - _dev.Locate(out senseBuf, false, currentPartition, 0, _dev.Timeout, out duration); - totalDuration += duration; + continue; + } } + else + { + _dumpLog.WriteLine($"Cannot position tape to block {badBlock}."); + ErrorMessage?.Invoke($"Cannot position tape to block {badBlock}."); - continue; + continue; + } } + else + { + sense = _dev.Locate(out senseBuf, (uint)_resume.NextBlock, _dev.Timeout, out _); - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; + if(!sense) + { + sense = _dev.ReadPosition(out cmdBuf, out senseBuf, _dev.Timeout, out _); - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; + if(sense) + { + _dumpLog.WriteLine("Could not check current position, continuing."); + StoppingErrorMessage?.Invoke("Could not check current position, continuing."); - PulseProgress?.Invoke($"Reading block {currentBlock} ({currentSpeed:F3} MiB/sec.)"); + continue; + } + + ulong position = Swapping.Swap(BitConverter.ToUInt32(cmdBuf, 4)); + + if(position != _resume.NextBlock) + { + _dumpLog.WriteLine("Current position is not as expected, continuing."); + StoppingErrorMessage?.Invoke("Current position is not as expected, continuing."); + + continue; + } + } + else + { + _dumpLog.WriteLine($"Cannot position tape to block {badBlock}."); + ErrorMessage?.Invoke($"Cannot position tape to block {badBlock}."); + + continue; + } + } sense = _dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, _dev.Timeout, out duration); totalDuration += duration; - if(sense && - senseBuf?.Length != 0 && - !ArrayHelpers.ArrayIsNullOrEmpty(senseBuf)) + if(!sense && + !_dev.Error) { - decSense = Sense.Decode(senseBuf); - - bool ili = decSense?.Fixed?.ILI == true; - bool valid = decSense?.Fixed?.InformationValid == true; - uint information = decSense?.Fixed?.Information ?? 0; - bool eom = decSense?.Fixed?.EOM == true; - bool filemark = decSense?.Fixed?.Filemark == true; - - if(decSense?.Descriptor.HasValue == true) - { - if(decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) - { - valid = true; - information = (uint)Sense.DecodeDescriptor00(desc00); - } - - if(decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] desc04)) - Sense.DecodeDescriptor04(desc04, out filemark, out eom, out ili); - } - - if(decSense.Value.ASC == 0x00 && - decSense.Value.ASCQ == 0x00 && - ili && - valid) - { - blockSize = (uint)((int)blockSize - - BitConverter.ToInt32(BitConverter.GetBytes(information), 0)); - - if(!fixedLen) - transferLen = blockSize; - - UpdateStatus?.Invoke($"Blocksize changed to {blockSize} bytes at block {currentBlock}"); - _dumpLog.WriteLine("Blocksize changed to {0} bytes at block {1}", blockSize, currentBlock); - - sense = _dev.Space(out senseBuf, SscSpaceCodes.LogicalBlock, -1, _dev.Timeout, out duration); - - totalDuration += duration; - - if(sense) - { - decSense = Sense.Decode(senseBuf); - - StoppingErrorMessage?.Invoke("Drive could not go back one block. Sense follows..." + - Environment.NewLine + decSense.Value.Description); - - outputTape.Close(); - _dumpLog.WriteLine("Drive could not go back one block. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); - - return; - } - - continue; - } - - switch(decSense.Value.SenseKey) - { - case SenseKeys.BlankCheck when currentBlock == 0: - StoppingErrorMessage?.Invoke("Cannot dump a blank tape..."); - outputTape.Close(); - _dumpLog.WriteLine("Cannot dump a blank tape..."); - - return; - - // For sure this is an end-of-tape/partition - case SenseKeys.BlankCheck when decSense.Value.ASC == 0x00 && - (decSense.Value.ASCQ == 0x02 || decSense.Value.ASCQ == 0x05 || - eom): - // TODO: Detect end of partition - endOfMedia = true; - UpdateStatus?.Invoke("Found end-of-tape/partition..."); - _dumpLog.WriteLine("Found end-of-tape/partition..."); - - continue; - case SenseKeys.BlankCheck: - StoppingErrorMessage?.Invoke("Blank block found, end of tape?..."); - endOfMedia = true; - _dumpLog.WriteLine("Blank block found, end of tape?..."); - - continue; - } - - if((decSense.Value.SenseKey == SenseKeys.NoSense || - decSense.Value.SenseKey == SenseKeys.RecoveredError) && - (decSense.Value.ASCQ == 0x02 || decSense.Value.ASCQ == 0x05 || eom)) - { - // TODO: Detect end of partition - endOfMedia = true; - UpdateStatus?.Invoke("Found end-of-tape/partition..."); - _dumpLog.WriteLine("Found end-of-tape/partition..."); - - continue; - } - - if((decSense.Value.SenseKey == SenseKeys.NoSense || - decSense.Value.SenseKey == SenseKeys.RecoveredError) && - (decSense.Value.ASCQ == 0x01 || filemark)) - { - currentTapeFile.LastBlock = currentBlock - 1; - outputTape.AddFile(currentTapeFile); - - currentFile++; - - currentTapeFile = new TapeFile - { - File = currentFile, - FirstBlock = currentBlock, - Partition = currentPartition - }; - - UpdateStatus?.Invoke($"Changed to file {currentFile} at block {currentBlock}"); - _dumpLog.WriteLine("Changed to file {0} at block {1}", currentFile, currentBlock); - - continue; - } - - if(decSense is null) - { - StoppingErrorMessage?. - Invoke($"Drive could not read block ${currentBlock}. Sense cannot be decoded, look at log for dump..."); - - _dumpLog.WriteLine($"Drive could not read block ${currentBlock}. Sense bytes follow..."); - _dumpLog.WriteLine(PrintHex.ByteArrayToHexArrayString(senseBuf, 32)); - } - else - { - StoppingErrorMessage?. - Invoke($"Drive could not read block ${currentBlock}. Sense follows...\n{decSense.Value.SenseKey} {decSense.Value.Description}"); - - _dumpLog.WriteLine($"Drive could not read block ${currentBlock}. Sense follows..."); - - _dumpLog.WriteLine("Device not ready. Sense {0}h ASC {1:X2}h ASCQ {2:X2}h", - decSense.Value.SenseKey, decSense.Value.ASC, decSense.Value.ASCQ); - } - - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly - - // Write empty data - DateTime writeStart = DateTime.Now; - outputTape.WriteSector(new byte[blockSize], currentBlock); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration); - ibgLog.Write(currentBlock, 0); - _resume.BadBlocks.Add(currentBlock); + _resume.BadBlocks.Remove(badBlock); + extents.Add(badBlock); + outputTape.WriteSector(cmdBuf, badBlock); + UpdateStatus?.Invoke($"Correctly retried block {badBlock} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badBlock, pass); } - else - { - mhddLog.Write(currentBlock, duration); - ibgLog.Write(currentBlock, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputTape.WriteSector(cmdBuf, currentBlock); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(currentBlock, 1, true); - } - - currentBlock++; - _resume.NextBlock++; - currentSpeedSize += blockSize; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = currentSpeedSize / (1048576 * elapsed); - currentSpeedSize = 0; - timeSpeedStart = DateTime.UtcNow; + else if(runningPersistent) + outputTape.WriteSector(cmdBuf, badBlock); } - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - blocks = currentBlock + 1; - end = DateTime.UtcNow; - - // If not aborted this is added at the end of medium - if(_aborted) + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) { - currentTapeFile.LastBlock = currentBlock - 1; - outputTape.AddFile(currentTapeFile); + pass++; + forward = !forward; + _resume.BadBlocks.Sort(); - currentTapePartition.LastBlock = currentBlock - 1; - outputTape.AddPartition(currentTapePartition); + if(!forward) + _resume.BadBlocks.Reverse(); + + goto repeatRetry; + } + + if(runningPersistent && currentModePage.HasValue) + { + // TODO: Persistent mode } EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - #region Error handling - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _retryPasses > 0 && - (canLocate || canLocateLong)) - { - int pass = 1; - bool forward = false; - bool runningPersistent = false; - - Modes.ModePage? currentModePage = null; - - if(_persistent) - { - // TODO: Implement persistent - } - - InitProgress?.Invoke(); - repeatRetry: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - - foreach(ulong badBlock in tmpArray) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke(string.Format("Retrying block {0}, pass {1}, {3}{2}", badBlock, pass, - forward ? "forward" : "reverse", - runningPersistent ? "recovering partial data, " : "")); - - UpdateStatus?.Invoke($"Positioning tape to block {badBlock}."); - _dumpLog.WriteLine($"Positioning tape to block {badBlock}."); - - if(canLocateLong) - { - sense = _dev.Locate16(out senseBuf, _resume.NextBlock, _dev.Timeout, out _); - - if(!sense) - { - sense = _dev.ReadPositionLong(out cmdBuf, out senseBuf, _dev.Timeout, out _); - - if(sense) - { - _dumpLog.WriteLine("Could not check current position, continuing."); - StoppingErrorMessage?.Invoke("Could not check current position, continuing."); - - continue; - } - - ulong position = Swapping.Swap(BitConverter.ToUInt64(cmdBuf, 8)); - - if(position != _resume.NextBlock) - { - _dumpLog.WriteLine("Current position is not as expected, continuing."); - StoppingErrorMessage?.Invoke("Current position is not as expected, continuing."); - - continue; - } - } - else - { - _dumpLog.WriteLine($"Cannot position tape to block {badBlock}."); - ErrorMessage?.Invoke($"Cannot position tape to block {badBlock}."); - - continue; - } - } - else - { - sense = _dev.Locate(out senseBuf, (uint)_resume.NextBlock, _dev.Timeout, out _); - - if(!sense) - { - sense = _dev.ReadPosition(out cmdBuf, out senseBuf, _dev.Timeout, out _); - - if(sense) - { - _dumpLog.WriteLine("Could not check current position, continuing."); - StoppingErrorMessage?.Invoke("Could not check current position, continuing."); - - continue; - } - - ulong position = Swapping.Swap(BitConverter.ToUInt32(cmdBuf, 4)); - - if(position != _resume.NextBlock) - { - _dumpLog.WriteLine("Current position is not as expected, continuing."); - StoppingErrorMessage?.Invoke("Current position is not as expected, continuing."); - - continue; - } - } - else - { - _dumpLog.WriteLine($"Cannot position tape to block {badBlock}."); - ErrorMessage?.Invoke($"Cannot position tape to block {badBlock}."); - - continue; - } - } - - sense = _dev.Read6(out cmdBuf, out senseBuf, false, fixedLen, transferLen, blockSize, _dev.Timeout, - out duration); - - totalDuration += duration; - - if(!sense && - !_dev.Error) - { - _resume.BadBlocks.Remove(badBlock); - extents.Add(badBlock); - outputTape.WriteSector(cmdBuf, badBlock); - UpdateStatus?.Invoke($"Correctly retried block {badBlock} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badBlock, pass); - } - else if(runningPersistent) - outputTape.WriteSector(cmdBuf, badBlock); - } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto repeatRetry; - } - - if(runningPersistent && currentModePage.HasValue) - { - // TODO: Persistent mode - } - - EndProgress?.Invoke(); - } - #endregion Error handling - - _resume.BadBlocks.Sort(); - - foreach(ulong bad in _resume.BadBlocks) - _dumpLog.WriteLine("Block {0} could not be read.", bad); - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - outputTape.SetDumpHardware(_resume.Tries); - - // TODO: Media Serial Number - var metadata = new CommonTypes.Structs.ImageInfo - { - Application = "Aaru", - ApplicationVersion = Version.GetVersion() - }; - - if(!outputTape.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputTape.ErrorMessage); - - if(_preSidecar != null) - outputTape.SetCicmMetadata(_preSidecar); - - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputTape.Close(); - DateTime closeEnd = DateTime.Now; - UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); - - if(_aborted) - { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - return; - } - - if(_aborted) - { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - return; - } - - double totalChkDuration = 0; - - if(_metadata) - { - UpdateStatus?.Invoke("Creating sidecar."); - _dumpLog.WriteLine("Creating sidecar."); - var filters = new FiltersList(); - IFilter filter = filters.GetFilter(_outputPath); - IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; - ErrorNumber opened = inputPlugin.Open(filter); - - if(opened != ErrorNumber.NoError) - { - StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); - - return; - } - - DateTime chkStart = DateTime.UtcNow; - _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); - _sidecarClass.InitProgressEvent += InitProgress; - _sidecarClass.UpdateProgressEvent += UpdateProgress; - _sidecarClass.EndProgressEvent += EndProgress; - _sidecarClass.InitProgressEvent2 += InitProgress2; - _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; - _sidecarClass.EndProgressEvent2 += EndProgress2; - _sidecarClass.UpdateStatusEvent += UpdateStatus; - CICMMetadataType sidecar = _sidecarClass.Create(); - end = DateTime.UtcNow; - - if(!_aborted) - { - totalChkDuration = (end - chkStart).TotalMilliseconds; - UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); - - _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); - - _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); - - if(_preSidecar != null) - { - _preSidecar.BlockMedia = sidecar.BlockMedia; - sidecar = _preSidecar; - } - - List<(ulong start, string type)> filesystems = new(); - - if(sidecar.BlockMedia[0].FileSystemInformation != null) - filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation - where partition.FileSystems != null - from fileSystem in partition.FileSystems - select (partition.StartSector, fileSystem.Type)); - - if(filesystems.Count > 0) - foreach(var filesystem in filesystems.Select(o => new - { - o.start, - o.type - }).Distinct()) - { - UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); - _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); - } - - sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); - - (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); - - sidecar.BlockMedia[0].DiskType = xmlType.type; - sidecar.BlockMedia[0].DiskSubType = xmlType.subType; - - // TODO: Implement device firmware revision - if(!_dev.IsRemovable || - _dev.IsUsb) - if(_dev.Type == DeviceType.ATAPI) - sidecar.BlockMedia[0].Interface = "ATAPI"; - else if(_dev.IsUsb) - sidecar.BlockMedia[0].Interface = "USB"; - else if(_dev.IsFireWire) - sidecar.BlockMedia[0].Interface = "FireWire"; - else - sidecar.BlockMedia[0].Interface = "SCSI"; - - sidecar.BlockMedia[0].LogicalBlocks = blocks; - sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; - sidecar.BlockMedia[0].Model = _dev.Model; - - if(!_private) - sidecar.BlockMedia[0].Serial = _dev.Serial; - - sidecar.BlockMedia[0].Size = blocks * blockSize; - - if(_dev.IsRemovable) - sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); - - UpdateStatus?.Invoke("Writing metadata sidecar"); - - var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - - var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); - xmlSer.Serialize(xmlFs, sidecar); - xmlFs.Close(); - } - } - - UpdateStatus?.Invoke(""); - - UpdateStatus?. - Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); - - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); - - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); - - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); - - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - UpdateStatus?.Invoke(""); - - Statistics.AddMedia(dskType, true); } + #endregion Error handling + + _resume.BadBlocks.Sort(); + + foreach(ulong bad in _resume.BadBlocks) + _dumpLog.WriteLine("Block {0} could not be read.", bad); + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + outputTape.SetDumpHardware(_resume.Tries); + + // TODO: Media Serial Number + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion() + }; + + if(!outputTape.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputTape.ErrorMessage); + + if(_preSidecar != null) + outputTape.SetCicmMetadata(_preSidecar); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputTape.Close(); + DateTime closeEnd = DateTime.Now; + UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); + _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + return; + } + + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + if(_metadata) + { + UpdateStatus?.Invoke("Creating sidecar."); + _dumpLog.WriteLine("Creating sidecar."); + var filters = new FiltersList(); + IFilter filter = filters.GetFilter(_outputPath); + IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; + ErrorNumber opened = inputPlugin.Open(filter); + + if(opened != ErrorNumber.NoError) + { + StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); + + return; + } + + DateTime chkStart = DateTime.UtcNow; + _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); + _sidecarClass.InitProgressEvent += InitProgress; + _sidecarClass.UpdateProgressEvent += UpdateProgress; + _sidecarClass.EndProgressEvent += EndProgress; + _sidecarClass.InitProgressEvent2 += InitProgress2; + _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; + _sidecarClass.EndProgressEvent2 += EndProgress2; + _sidecarClass.UpdateStatusEvent += UpdateStatus; + CICMMetadataType sidecar = _sidecarClass.Create(); + end = DateTime.UtcNow; + + if(!_aborted) + { + totalChkDuration = (end - chkStart).TotalMilliseconds; + UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + + _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); + + _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + + if(_preSidecar != null) + { + _preSidecar.BlockMedia = sidecar.BlockMedia; + sidecar = _preSidecar; + } + + List<(ulong start, string type)> filesystems = new(); + + if(sidecar.BlockMedia[0].FileSystemInformation != null) + filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation + where partition.FileSystems != null + from fileSystem in partition.FileSystems + select (partition.StartSector, fileSystem.Type)); + + if(filesystems.Count > 0) + foreach(var filesystem in filesystems.Select(o => new + { + o.start, + o.type + }).Distinct()) + { + UpdateStatus?.Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); + _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, filesystem.start); + } + + sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); + + (string type, string subType) xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + + sidecar.BlockMedia[0].DiskType = xmlType.type; + sidecar.BlockMedia[0].DiskSubType = xmlType.subType; + + // TODO: Implement device firmware revision + if(!_dev.IsRemovable || + _dev.IsUsb) + if(_dev.Type == DeviceType.ATAPI) + sidecar.BlockMedia[0].Interface = "ATAPI"; + else if(_dev.IsUsb) + sidecar.BlockMedia[0].Interface = "USB"; + else if(_dev.IsFireWire) + sidecar.BlockMedia[0].Interface = "FireWire"; + else + sidecar.BlockMedia[0].Interface = "SCSI"; + + sidecar.BlockMedia[0].LogicalBlocks = blocks; + sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; + sidecar.BlockMedia[0].Model = _dev.Model; + + if(!_private) + sidecar.BlockMedia[0].Serial = _dev.Serial; + + sidecar.BlockMedia[0].Size = blocks * blockSize; + + if(_dev.IsRemovable) + sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); + + UpdateStatus?.Invoke("Writing metadata sidecar"); + + var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); + + var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); + } + } + + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + UpdateStatus?.Invoke(""); + + Statistics.AddMedia(dskType, true); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/Sbc/Data.cs b/Aaru.Core/Devices/Dumping/Sbc/Data.cs index 256cf36dc..5cde02fbf 100644 --- a/Aaru.Core/Devices/Dumping/Sbc/Data.cs +++ b/Aaru.Core/Devices/Dumping/Sbc/Data.cs @@ -41,236 +41,235 @@ using DVDDecryption = Aaru.Decryption.DVD.Dump; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Dumps data when dumping from a SCSI Block Commands compliant device + /// Media blocks + /// Maximum number of blocks to read in a single command + /// Block size in bytes + /// Resume information + /// Correctly dump extents + /// Current speed + /// Minimum speed + /// Maximum speed + /// Total time spent in commands + /// SCSI reader + /// MHDD log + /// ImgBurn log + /// Total time spent writing to image + /// Set if we need to start a trim + /// DVD CSS decryption module + /// The DVD disc key + void ReadSbcData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardwareType currentTry, + ExtentsULong extents, ref double currentSpeed, ref double minSpeed, ref double maxSpeed, + ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog, + ref double imageWriteDuration, ref bool newTrim, ref DVDDecryption dvdDecrypt, byte[] discKey) { - /// Dumps data when dumping from a SCSI Block Commands compliant device - /// Media blocks - /// Maximum number of blocks to read in a single command - /// Block size in bytes - /// Resume information - /// Correctly dump extents - /// Current speed - /// Minimum speed - /// Maximum speed - /// Total time spent in commands - /// SCSI reader - /// MHDD log - /// ImgBurn log - /// Total time spent writing to image - /// Set if we need to start a trim - /// DVD CSS decryption module - /// The DVD disc key - void ReadSbcData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardwareType currentTry, - ExtentsULong extents, ref double currentSpeed, ref double minSpeed, ref double maxSpeed, - ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog, - ref double imageWriteDuration, ref bool newTrim, ref DVDDecryption dvdDecrypt, byte[] discKey) + ulong sectorSpeedStart = 0; + bool sense; + DateTime timeSpeedStart = DateTime.UtcNow; + byte[] buffer; + uint blocksToRead = maxBlocksToRead; + var outputFormat = _outputPlugin as IWritableImage; + + InitProgress?.Invoke(); + + for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) { - ulong sectorSpeedStart = 0; - bool sense; - DateTime timeSpeedStart = DateTime.UtcNow; - byte[] buffer; - uint blocksToRead = maxBlocksToRead; - var outputFormat = _outputPlugin as IWritableImage; - - InitProgress?.Invoke(); - - for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + if(_aborted) { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - break; - } - - if(blocks - i < blocksToRead) - blocksToRead = (uint)(blocks - i); - - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; - - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)blocks); - - sense = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _); - totalDuration += cmdDuration; - - if(!sense && - !_dev.Error) - { - if(Settings.Settings.Current.EnableDecryption && - discKey != null && - _titleKeys) - { - for(ulong j = 0; j < blocksToRead; j++) - { - if(_aborted) - break; - - if(!_resume.MissingTitleKeys.Contains(i + j)) - - // Key is already dumped. - continue; - - byte[] tmpBuf; - - bool tmpSense = dvdDecrypt.ReadTitleKey(out tmpBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm, - i + j, _dev.Timeout, out _); - - if(!tmpSense) - { - CSS_CPRM.TitleKey? titleKey = CSS.DecodeTitleKey(tmpBuf, dvdDecrypt.BusKey); - - if(titleKey.HasValue) - outputFormat.WriteSectorTag(new[] - { - titleKey.Value.CMI - }, i + j, SectorTagType.DvdCmi); - else - continue; - - // If the CMI bit is 1, the sector is using copy protection, else it is not - if((titleKey.Value.CMI & 0x80) >> 7 == 0) - { - // The CMI indicates this sector is not encrypted. - outputFormat.WriteSectorTag(new byte[] - { - 0, 0, 0, 0, 0 - }, i + j, SectorTagType.DvdTitleKey); - - outputFormat.WriteSectorTag(new byte[] - { - 0, 0, 0, 0, 0 - }, i + j, SectorTagType.DvdTitleKeyDecrypted); - - _resume.MissingTitleKeys.Remove(i + j); - - continue; - } - - // According to libdvdcss, if the key is all zeroes, the sector is actually - // not encrypted even if the CMI says it is. - if(titleKey.Value.Key.All(k => k == 0)) - { - outputFormat.WriteSectorTag(new byte[] - { - 0, 0, 0, 0, 0 - }, i + j, SectorTagType.DvdTitleKey); - - outputFormat.WriteSectorTag(new byte[] - { - 0, 0, 0, 0, 0 - }, i + j, SectorTagType.DvdTitleKeyDecrypted); - - _resume.MissingTitleKeys.Remove(i + j); - - continue; - } - - outputFormat.WriteSectorTag(titleKey.Value.Key, i + j, SectorTagType.DvdTitleKey); - _resume.MissingTitleKeys.Remove(i + j); - - CSS.DecryptTitleKey(0, discKey, titleKey.Value.Key, out tmpBuf); - outputFormat.WriteSectorTag(tmpBuf, i + j, SectorTagType.DvdTitleKeyDecrypted); - } - } - - if(!_storeEncrypted) - - // Todo: Flag in the outputFormat that a sector has been decrypted - { - ErrorNumber errno = - outputFormat.ReadSectorsTag(i, blocksToRead, SectorTagType.DvdCmi, out byte[] cmi); - - if(errno != ErrorNumber.NoError) - ErrorMessage?.Invoke($"Error retrieving CMI for sector {i}"); - else - { - errno = outputFormat.ReadSectorsTag(i, blocksToRead, - SectorTagType.DvdTitleKeyDecrypted, - out byte[] titleKey); - - if(errno != ErrorNumber.NoError) - ErrorMessage?.Invoke($"Error retrieving title key for sector {i}"); - else - buffer = CSS.DecryptSector(buffer, cmi, titleKey, blocksToRead, blockSize); - } - } - } - - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(buffer, i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(i, blocksToRead, true); - } - else - { - if(_dev.Manufacturer.ToLowerInvariant() == "insite") - { - _resume.BadBlocks.Add(i); - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - _resume.NextBlock++; - _aborted = true; - - _dumpLog?. - WriteLine("INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately."); - - UpdateStatus?. - Invoke("INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately"); - - continue; - } - - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly - - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - // Write empty data - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; - } - - sectorSpeedStart += blocksToRead; - _resume.NextBlock = i + blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + break; } - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + if(blocks - i < blocksToRead) + blocksToRead = (uint)(blocks - i); - EndProgress?.Invoke(); + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)blocks); + + sense = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _); + totalDuration += cmdDuration; + + if(!sense && + !_dev.Error) + { + if(Settings.Settings.Current.EnableDecryption && + discKey != null && + _titleKeys) + { + for(ulong j = 0; j < blocksToRead; j++) + { + if(_aborted) + break; + + if(!_resume.MissingTitleKeys.Contains(i + j)) + + // Key is already dumped. + continue; + + byte[] tmpBuf; + + bool tmpSense = dvdDecrypt.ReadTitleKey(out tmpBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm, + i + j, _dev.Timeout, out _); + + if(!tmpSense) + { + CSS_CPRM.TitleKey? titleKey = CSS.DecodeTitleKey(tmpBuf, dvdDecrypt.BusKey); + + if(titleKey.HasValue) + outputFormat.WriteSectorTag(new[] + { + titleKey.Value.CMI + }, i + j, SectorTagType.DvdCmi); + else + continue; + + // If the CMI bit is 1, the sector is using copy protection, else it is not + if((titleKey.Value.CMI & 0x80) >> 7 == 0) + { + // The CMI indicates this sector is not encrypted. + outputFormat.WriteSectorTag(new byte[] + { + 0, 0, 0, 0, 0 + }, i + j, SectorTagType.DvdTitleKey); + + outputFormat.WriteSectorTag(new byte[] + { + 0, 0, 0, 0, 0 + }, i + j, SectorTagType.DvdTitleKeyDecrypted); + + _resume.MissingTitleKeys.Remove(i + j); + + continue; + } + + // According to libdvdcss, if the key is all zeroes, the sector is actually + // not encrypted even if the CMI says it is. + if(titleKey.Value.Key.All(k => k == 0)) + { + outputFormat.WriteSectorTag(new byte[] + { + 0, 0, 0, 0, 0 + }, i + j, SectorTagType.DvdTitleKey); + + outputFormat.WriteSectorTag(new byte[] + { + 0, 0, 0, 0, 0 + }, i + j, SectorTagType.DvdTitleKeyDecrypted); + + _resume.MissingTitleKeys.Remove(i + j); + + continue; + } + + outputFormat.WriteSectorTag(titleKey.Value.Key, i + j, SectorTagType.DvdTitleKey); + _resume.MissingTitleKeys.Remove(i + j); + + CSS.DecryptTitleKey(0, discKey, titleKey.Value.Key, out tmpBuf); + outputFormat.WriteSectorTag(tmpBuf, i + j, SectorTagType.DvdTitleKeyDecrypted); + } + } + + if(!_storeEncrypted) + + // Todo: Flag in the outputFormat that a sector has been decrypted + { + ErrorNumber errno = + outputFormat.ReadSectorsTag(i, blocksToRead, SectorTagType.DvdCmi, out byte[] cmi); + + if(errno != ErrorNumber.NoError) + ErrorMessage?.Invoke($"Error retrieving CMI for sector {i}"); + else + { + errno = outputFormat.ReadSectorsTag(i, blocksToRead, + SectorTagType.DvdTitleKeyDecrypted, + out byte[] titleKey); + + if(errno != ErrorNumber.NoError) + ErrorMessage?.Invoke($"Error retrieving title key for sector {i}"); + else + buffer = CSS.DecryptSector(buffer, cmi, titleKey, blocksToRead, blockSize); + } + } + } + + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(buffer, i, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(i, blocksToRead, true); + } + else + { + if(_dev.Manufacturer.ToLowerInvariant() == "insite") + { + _resume.BadBlocks.Add(i); + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + _resume.NextBlock++; + _aborted = true; + + _dumpLog?. + WriteLine("INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately."); + + UpdateStatus?. + Invoke("INSITE floptical drives get crazy on the SCSI bus when an error is found, stopping so you can reboot the computer or reset the scsi bus appropriately"); + + continue; + } + + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + if(i + _skip > blocks) + _skip = (uint)(blocks - i); + + // Write empty data + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); + + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; + } + + sectorSpeedStart += blocksToRead; + _resume.NextBlock = i + blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; } + + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + + EndProgress?.Invoke(); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/Sbc/Dump.cs b/Aaru.Core/Devices/Dumping/Sbc/Dump.cs index 0bb5c4b06..558a82720 100644 --- a/Aaru.Core/Devices/Dumping/Sbc/Dump.cs +++ b/Aaru.Core/Devices/Dumping/Sbc/Dump.cs @@ -59,441 +59,495 @@ using Version = Aaru.CommonTypes.Interop.Version; // ReSharper disable JoinDeclarationAndInitializer -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Implements dumping SCSI Block Commands and Reduced Block Commands devices +partial class Dump { - /// Implements dumping SCSI Block Commands and Reduced Block Commands devices - partial class Dump + /// Dumps a SCSI Block Commands device or a Reduced Block Commands devices + /// If device contains an optical disc (e.g. DVD or BD) + /// Media tags as retrieved in MMC layer + /// Disc type as detected in SCSI or MMC layer + /// DVD CSS decryption module + void Sbc(Dictionary mediaTags, MediaType dskType, bool opticalDisc, + DVDDecryption dvdDecrypt = null) { - /// Dumps a SCSI Block Commands device or a Reduced Block Commands devices - /// If device contains an optical disc (e.g. DVD or BD) - /// Media tags as retrieved in MMC layer - /// Disc type as detected in SCSI or MMC layer - /// DVD CSS decryption module - void Sbc(Dictionary mediaTags, MediaType dskType, bool opticalDisc, - DVDDecryption dvdDecrypt = null) - { - bool sense; - byte scsiMediumType = 0; - byte scsiDensityCode = 0; - bool containsFloppyPage = false; - const ushort sbcProfile = 0x0001; - DateTime start; - DateTime end; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; - byte[] readBuffer; - Modes.DecodedMode? decMode = null; - bool ret; - ExtentsULong blankExtents = null; - var outputFormat = _outputPlugin as IWritableImage; - - if(opticalDisc) - switch(dskType) - { - case MediaType.REV35: - case MediaType.REV70: - case MediaType.REV120: - opticalDisc = false; - - break; - } - - _dumpLog.WriteLine("Initializing reader."); - var scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog, _dumpRaw); - ulong blocks = scsiReader.GetDeviceBlocks(); - uint blockSize = scsiReader.LogicalBlockSize; - - if(!opticalDisc) - { - mediaTags = new Dictionary(); - - if(_dev.IsUsb && - _dev.UsbDescriptors != null) - mediaTags.Add(MediaTagType.USB_Descriptors, null); - - if(_dev.Type == DeviceType.ATAPI) - mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null); - - if(_dev.IsPcmcia && - _dev.Cis != null) - mediaTags.Add(MediaTagType.PCMCIA_CIS, null); - - sense = _dev.ScsiInquiry(out byte[] cmdBuf, out _); - - if(_private) - cmdBuf = DeviceReport.ClearInquiry(cmdBuf); - - mediaTags.Add(MediaTagType.SCSI_INQUIRY, cmdBuf); - - if(!sense) - { - _dumpLog.WriteLine("Requesting MODE SENSE (10)."); - UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); - - sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, - 0xFF, 5, out _); - - if(!sense || - _dev.Error) - sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out _); - - if(!sense && - !_dev.Error) - if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) - { - mediaTags.Add(MediaTagType.SCSI_MODESENSE_10, cmdBuf); - decMode = Modes.DecodeMode10(cmdBuf, _dev.ScsiType); - } - - _dumpLog.WriteLine("Requesting MODE SENSE (6)."); - UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); - - sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, - out _); - - if(sense || _dev.Error) - sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, - 5, out _); - - if(sense || _dev.Error) - sense = _dev.ModeSense(out cmdBuf, out _, 5, out _); - - if(!sense && - !_dev.Error) - if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) - { - mediaTags.Add(MediaTagType.SCSI_MODESENSE_6, cmdBuf); - decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType); - } - - if(decMode.HasValue) - { - scsiMediumType = (byte)decMode.Value.Header.MediumType; - - if(decMode.Value.Header.BlockDescriptors?.Length > 0) - scsiDensityCode = (byte)decMode.Value.Header.BlockDescriptors[0].Density; - - // TODO: Fix this - containsFloppyPage = decMode.Value.Pages?.Aggregate(containsFloppyPage, - (current, modePage) => - current | (modePage.Page == 0x05)) == - true; - } - } - } - - if(dskType == MediaType.Unknown) - dskType = MediaTypeFromDevice.GetFromScsi((byte)_dev.ScsiType, _dev.Manufacturer, _dev.Model, - scsiMediumType, scsiDensityCode, blocks + 1, blockSize, - _dev.IsUsb, opticalDisc); - - if(_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) - MMC.DetectDiscType(ref dskType, 1, null, _dev, out _, out _, 0, blocks + 1); + bool sense; + byte scsiMediumType = 0; + byte scsiDensityCode = 0; + bool containsFloppyPage = false; + const ushort sbcProfile = 0x0001; + DateTime start; + DateTime end; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + byte[] readBuffer; + Modes.DecodedMode? decMode = null; + bool ret; + ExtentsULong blankExtents = null; + var outputFormat = _outputPlugin as IWritableImage; + if(opticalDisc) switch(dskType) { - // Hi-MD devices show the disks while in Hi-MD mode, but they cannot be read using any known command - // SonicStage changes the device mode, so it is no longer a mass storage device, and can only read - // tracks written by that same application ID (changes between computers). - case MediaType.MD: - _dumpLog.WriteLine("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); - - StoppingErrorMessage?. - Invoke("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); - - return; - case MediaType.Unknown when _dev.IsUsb && containsFloppyPage: - dskType = MediaType.FlashDrive; + case MediaType.REV35: + case MediaType.REV70: + case MediaType.REV120: + opticalDisc = false; break; } - if(scsiReader.FindReadCommand()) + _dumpLog.WriteLine("Initializing reader."); + var scsiReader = new Reader(_dev, _dev.Timeout, null, _errorLog, _dumpRaw); + ulong blocks = scsiReader.GetDeviceBlocks(); + uint blockSize = scsiReader.LogicalBlockSize; + + if(!opticalDisc) + { + mediaTags = new Dictionary(); + + if(_dev.IsUsb && + _dev.UsbDescriptors != null) + mediaTags.Add(MediaTagType.USB_Descriptors, null); + + if(_dev.Type == DeviceType.ATAPI) + mediaTags.Add(MediaTagType.ATAPI_IDENTIFY, null); + + if(_dev.IsPcmcia && + _dev.Cis != null) + mediaTags.Add(MediaTagType.PCMCIA_CIS, null); + + sense = _dev.ScsiInquiry(out byte[] cmdBuf, out _); + + if(_private) + cmdBuf = DeviceReport.ClearInquiry(cmdBuf); + + mediaTags.Add(MediaTagType.SCSI_INQUIRY, cmdBuf); + + if(!sense) { - _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage); - StoppingErrorMessage?.Invoke("Unable to read medium."); + _dumpLog.WriteLine("Requesting MODE SENSE (10)."); + UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); - return; - } + sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, + 0xFF, 5, out _); - if(blocks != 0 && - blockSize != 0) - { - blocks++; + if(!sense || + _dev.Error) + sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, + 0x00, 5, out _); - ulong totalSize = blocks * blockSize; - - if(totalSize > 1099511627776) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); - else if(totalSize > 1073741824) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); - else if(totalSize > 1048576) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); - else if(totalSize > 1024) - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); - else - UpdateStatus?. - Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); - } - - // Check how many blocks to read, if error show and return - if(scsiReader.GetBlocksToRead(_maximumReadable)) - { - _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage); - StoppingErrorMessage?.Invoke(scsiReader.ErrorMessage); - - return; - } - - uint blocksToRead = scsiReader.BlocksToRead; - uint logicalBlockSize = blockSize; - uint physicalBlockSize = scsiReader.PhysicalBlockSize; - - if(blocks == 0) - { - _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); - StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); - - return; - } - - UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); - UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); - UpdateStatus?.Invoke($"Device reports {scsiReader.LongBlockSize} bytes per physical block."); - UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); - UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumType}."); - UpdateStatus?.Invoke($"SCSI density type: {scsiDensityCode}."); - UpdateStatus?.Invoke($"SCSI floppy mode page present: {containsFloppyPage}."); - UpdateStatus?.Invoke($"Media identified as {dskType}"); - - _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); - _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); - _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); - _dumpLog.WriteLine("Device reports {0} bytes per physical block.", scsiReader.LongBlockSize); - _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); - _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumType); - _dumpLog.WriteLine("SCSI density type: {0}.", scsiDensityCode); - _dumpLog.WriteLine("SCSI floppy mode page present: {0}.", containsFloppyPage); - _dumpLog.WriteLine("Media identified as {0}.", dskType); - - uint longBlockSize = scsiReader.LongBlockSize; - - if(_dumpRaw) - if(blockSize == longBlockSize) - { - ErrorMessage?.Invoke(!scsiReader.CanReadRaw - ? "Device doesn't seem capable of reading raw data from media." - : "Device is capable of reading raw data but I've been unable to guess correct sector size."); - - if(!_force) + if(!sense && + !_dev.Error) + if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) { - StoppingErrorMessage?. - Invoke("Not continuing. If you want to continue reading cooked data when raw is not available use the force option."); - - // TODO: Exit more gracefully - return; + mediaTags.Add(MediaTagType.SCSI_MODESENSE_10, cmdBuf); + decMode = Modes.DecodeMode10(cmdBuf, _dev.ScsiType); } - ErrorMessage?.Invoke("Continuing dumping cooked data."); - } - else + _dumpLog.WriteLine("Requesting MODE SENSE (6)."); + UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); + + sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, + out _); + + if(sense || _dev.Error) + sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, + 5, out _); + + if(sense || _dev.Error) + sense = _dev.ModeSense(out cmdBuf, out _, 5, out _); + + if(!sense && + !_dev.Error) + if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) + { + mediaTags.Add(MediaTagType.SCSI_MODESENSE_6, cmdBuf); + decMode = Modes.DecodeMode6(cmdBuf, _dev.ScsiType); + } + + if(decMode.HasValue) { - // Only a block will be read, but it contains 16 sectors and command expect sector number not block number - blocksToRead = (uint)(longBlockSize == 37856 ? 16 : 1); + scsiMediumType = (byte)decMode.Value.Header.MediumType; - UpdateStatus?. - Invoke($"Reading {longBlockSize} raw bytes ({blockSize * blocksToRead} cooked bytes) per sector."); + if(decMode.Value.Header.BlockDescriptors?.Length > 0) + scsiDensityCode = (byte)decMode.Value.Header.BlockDescriptors[0].Density; - physicalBlockSize = longBlockSize; - blockSize = longBlockSize; + // TODO: Fix this + containsFloppyPage = decMode.Value.Pages?.Aggregate(containsFloppyPage, + (current, modePage) => + current | (modePage.Page == 0x05)) == + true; + } + } + } + + if(dskType == MediaType.Unknown) + dskType = MediaTypeFromDevice.GetFromScsi((byte)_dev.ScsiType, _dev.Manufacturer, _dev.Model, + scsiMediumType, scsiDensityCode, blocks + 1, blockSize, + _dev.IsUsb, opticalDisc); + + if(_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) + MMC.DetectDiscType(ref dskType, 1, null, _dev, out _, out _, 0, blocks + 1); + + switch(dskType) + { + // Hi-MD devices show the disks while in Hi-MD mode, but they cannot be read using any known command + // SonicStage changes the device mode, so it is no longer a mass storage device, and can only read + // tracks written by that same application ID (changes between computers). + case MediaType.MD: + _dumpLog.WriteLine("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); + + StoppingErrorMessage?. + Invoke("MiniDisc albums, NetMD discs or user-written audio MiniDisc cannot be dumped."); + + return; + case MediaType.Unknown when _dev.IsUsb && containsFloppyPage: + dskType = MediaType.FlashDrive; + + break; + } + + if(scsiReader.FindReadCommand()) + { + _dumpLog.WriteLine("ERROR: Cannot find correct read command: {0}.", scsiReader.ErrorMessage); + StoppingErrorMessage?.Invoke("Unable to read medium."); + + return; + } + + if(blocks != 0 && + blockSize != 0) + { + blocks++; + + ulong totalSize = blocks * blockSize; + + if(totalSize > 1099511627776) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); + else if(totalSize > 1073741824) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); + else if(totalSize > 1048576) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); + else if(totalSize > 1024) + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); + else + UpdateStatus?. + Invoke($"Media has {blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); + } + + // Check how many blocks to read, if error show and return + if(scsiReader.GetBlocksToRead(_maximumReadable)) + { + _dumpLog.WriteLine("ERROR: Cannot get blocks to read: {0}.", scsiReader.ErrorMessage); + StoppingErrorMessage?.Invoke(scsiReader.ErrorMessage); + + return; + } + + uint blocksToRead = scsiReader.BlocksToRead; + uint logicalBlockSize = blockSize; + uint physicalBlockSize = scsiReader.PhysicalBlockSize; + + if(blocks == 0) + { + _dumpLog.WriteLine("ERROR: Unable to read medium or empty medium present..."); + StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); + + return; + } + + UpdateStatus?.Invoke($"Device reports {blocks} blocks ({blocks * blockSize} bytes)."); + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); + UpdateStatus?.Invoke($"Device reports {blockSize} bytes per logical block."); + UpdateStatus?.Invoke($"Device reports {scsiReader.LongBlockSize} bytes per physical block."); + UpdateStatus?.Invoke($"SCSI device type: {_dev.ScsiType}."); + UpdateStatus?.Invoke($"SCSI medium type: {scsiMediumType}."); + UpdateStatus?.Invoke($"SCSI density type: {scsiDensityCode}."); + UpdateStatus?.Invoke($"SCSI floppy mode page present: {containsFloppyPage}."); + UpdateStatus?.Invoke($"Media identified as {dskType}"); + + _dumpLog.WriteLine("Device reports {0} blocks ({1} bytes).", blocks, blocks * blockSize); + _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); + _dumpLog.WriteLine("Device reports {0} bytes per logical block.", blockSize); + _dumpLog.WriteLine("Device reports {0} bytes per physical block.", scsiReader.LongBlockSize); + _dumpLog.WriteLine("SCSI device type: {0}.", _dev.ScsiType); + _dumpLog.WriteLine("SCSI medium type: {0}.", scsiMediumType); + _dumpLog.WriteLine("SCSI density type: {0}.", scsiDensityCode); + _dumpLog.WriteLine("SCSI floppy mode page present: {0}.", containsFloppyPage); + _dumpLog.WriteLine("Media identified as {0}.", dskType); + + uint longBlockSize = scsiReader.LongBlockSize; + + if(_dumpRaw) + if(blockSize == longBlockSize) + { + ErrorMessage?.Invoke(!scsiReader.CanReadRaw + ? "Device doesn't seem capable of reading raw data from media." + : "Device is capable of reading raw data but I've been unable to guess correct sector size."); + + if(!_force) + { + StoppingErrorMessage?. + Invoke("Not continuing. If you want to continue reading cooked data when raw is not available use the force option."); + + // TODO: Exit more gracefully + return; } - ret = true; - - foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputFormat.SupportedMediaTags.Contains(tag))) + ErrorMessage?.Invoke("Continuing dumping cooked data."); + } + else { - ret = false; - _dumpLog.WriteLine($"Output format does not support {tag}."); - ErrorMessage?.Invoke($"Output format does not support {tag}."); + // Only a block will be read, but it contains 16 sectors and command expect sector number not block number + blocksToRead = (uint)(longBlockSize == 37856 ? 16 : 1); + + UpdateStatus?. + Invoke($"Reading {longBlockSize} raw bytes ({blockSize * blocksToRead} cooked bytes) per sector."); + + physicalBlockSize = longBlockSize; + blockSize = longBlockSize; } + ret = true; + + foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputFormat.SupportedMediaTags.Contains(tag))) + { + ret = false; + _dumpLog.WriteLine($"Output format does not support {tag}."); + ErrorMessage?.Invoke($"Output format does not support {tag}."); + } + + if(!ret) + { + if(_force) + { + _dumpLog.WriteLine("Several media tags not supported, continuing..."); + ErrorMessage?.Invoke("Several media tags not supported, continuing..."); + } + else + { + _dumpLog.WriteLine("Several media tags not supported, not continuing..."); + StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + + return; + } + } + + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + _dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); + + var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); + var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile); + bool imageCreated = false; + + if(!opticalDisc) + { + ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); + + // Cannot create image if(!ret) { - if(_force) - { - _dumpLog.WriteLine("Several media tags not supported, continuing..."); - ErrorMessage?.Invoke("Several media tags not supported, continuing..."); - } - else - { - _dumpLog.WriteLine("Several media tags not supported, not continuing..."); - StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputFormat.ErrorMessage); - return; - } + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; } - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); - _dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); + imageCreated = true; + } - var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); - var ibgLog = new IbgLog(_outputPrefix + ".ibg", sbcProfile); - bool imageCreated = false; + start = DateTime.UtcNow; + double imageWriteDuration = 0; + bool writeSingleOpticalTrack = true; - if(!opticalDisc) + if(opticalDisc) + { + if(outputFormat is IWritableOpticalImage opticalPlugin) { - ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); + sense = _dev.ReadDiscInformation(out readBuffer, out _, MmcDiscInformationDataTypes.DiscInformation, + _dev.Timeout, out _); - // Cannot create image - if(!ret) + if(!sense) { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputFormat.ErrorMessage); + DiscInformation.StandardDiscInformation? discInformation = + DiscInformation.Decode000b(readBuffer); - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + - outputFormat.ErrorMessage); + // This means the output image can store sessions that are not on a CD, like on a DVD or Blu-ray + bool canStoreNotCdSessions = + opticalPlugin.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreNotCdSessions); - return; - } + // This means the output image can store tracks that are not on a CD, like on a DVD or Blu-ray + bool canStoreNotCdTracks = + opticalPlugin.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreNotCdTracks); - imageCreated = true; - } - - start = DateTime.UtcNow; - double imageWriteDuration = 0; - bool writeSingleOpticalTrack = true; - - if(opticalDisc) - { - if(outputFormat is IWritableOpticalImage opticalPlugin) - { - sense = _dev.ReadDiscInformation(out readBuffer, out _, MmcDiscInformationDataTypes.DiscInformation, - _dev.Timeout, out _); - - if(!sense) + if(discInformation.HasValue) { - DiscInformation.StandardDiscInformation? discInformation = - DiscInformation.Decode000b(readBuffer); + writeSingleOpticalTrack = false; - // This means the output image can store sessions that are not on a CD, like on a DVD or Blu-ray - bool canStoreNotCdSessions = - opticalPlugin.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreNotCdSessions); - - // This means the output image can store tracks that are not on a CD, like on a DVD or Blu-ray - bool canStoreNotCdTracks = - opticalPlugin.OpticalCapabilities.HasFlag(OpticalImageCapabilities.CanStoreNotCdTracks); - - if(discInformation.HasValue) + if(discInformation?.Sessions > 1 && + !canStoreNotCdSessions) { - writeSingleOpticalTrack = false; - - if(discInformation?.Sessions > 1 && - !canStoreNotCdSessions) + if(_force) { - if(_force) - { - _dumpLog. - WriteLine("Image does not support multiple sessions in non Compact Disc dumps, continuing..."); + _dumpLog. + WriteLine("Image does not support multiple sessions in non Compact Disc dumps, continuing..."); - ErrorMessage?. - Invoke("Image does not support multiple sessions in non Compact Disc dumps, continuing..."); - } - else - { - _dumpLog. - WriteLine("Image does not support multiple sessions in non Compact Disc dumps, not continuing..."); - - StoppingErrorMessage?. - Invoke("Image does not support multiple sessions in non Compact Disc dumps, not continuing..."); - - return; - } + ErrorMessage?. + Invoke("Image does not support multiple sessions in non Compact Disc dumps, continuing..."); } - - if((discInformation?.LastTrackLastSession - discInformation?.FirstTrackNumber > 0 || - discInformation?.FirstTrackNumber != 1) && - !canStoreNotCdTracks) + else { - if(_force) - { - _dumpLog. - WriteLine("Image does not support multiple tracks in non Compact Disc dumps, continuing..."); + _dumpLog. + WriteLine("Image does not support multiple sessions in non Compact Disc dumps, not continuing..."); - ErrorMessage?. - Invoke("Image does not support multiple tracks in non Compact Disc dumps, continuing..."); - } - else - { - _dumpLog. - WriteLine("Image does not support multiple tracks in non Compact Disc dumps, not continuing..."); + StoppingErrorMessage?. + Invoke("Image does not support multiple sessions in non Compact Disc dumps, not continuing..."); - StoppingErrorMessage?. - Invoke("Image does not support multiple tracks in non Compact Disc dumps, not continuing..."); - - return; - } + return; } + } - UpdateStatus?.Invoke("Building track map..."); - _dumpLog.WriteLine("Building track map..."); - - List tracks = new(); - - for(ushort tno = discInformation.Value.FirstTrackNumber; - tno <= discInformation?.LastTrackLastSession; tno++) + if((discInformation?.LastTrackLastSession - discInformation?.FirstTrackNumber > 0 || + discInformation?.FirstTrackNumber != 1) && + !canStoreNotCdTracks) + { + if(_force) { - sense = _dev.ReadTrackInformation(out readBuffer, out _, false, - TrackInformationType.LogicalTrackNumber, tno, - _dev.Timeout, out _); + _dumpLog. + WriteLine("Image does not support multiple tracks in non Compact Disc dumps, continuing..."); - if(sense) - continue; - - var trkInfo = TrackInformation.Decode(readBuffer); - - if(trkInfo is null) - continue; - - // Some drives return this invalid value with recordable discs - if(trkInfo.LogicalTrackNumber == 0) - continue; - - // Fixes a firmware bug in some DVD drives - if((int)trkInfo.LogicalTrackStartAddress < 0) - trkInfo.LogicalTrackStartAddress = 0; - - // Some drives return this invalid value with recordable discs - if(trkInfo.LogicalTrackSize == 0xFFFFFFFF) - trkInfo.LogicalTrackSize = (uint)(blocks - trkInfo.LogicalTrackStartAddress); - - var track = new Track - { - Sequence = trkInfo.LogicalTrackNumber, - Session = (ushort)(canStoreNotCdSessions ? trkInfo.SessionNumber : 1), - Type = TrackType.Data, - StartSector = trkInfo.LogicalTrackStartAddress, - EndSector = trkInfo.LogicalTrackSize + trkInfo.LogicalTrackStartAddress - 1, - RawBytesPerSector = (int)blockSize, - BytesPerSector = (int)blockSize, - SubchannelType = TrackSubchannelType.None - }; - - if(track.EndSector >= blocks) - blocks = track.EndSector + 1; - - tracks.Add(track); + ErrorMessage?. + Invoke("Image does not support multiple tracks in non Compact Disc dumps, continuing..."); } + else + { + _dumpLog. + WriteLine("Image does not support multiple tracks in non Compact Disc dumps, not continuing..."); - if(tracks.Count == 0) - tracks.Add(new Track + StoppingErrorMessage?. + Invoke("Image does not support multiple tracks in non Compact Disc dumps, not continuing..."); + + return; + } + } + + UpdateStatus?.Invoke("Building track map..."); + _dumpLog.WriteLine("Building track map..."); + + List tracks = new(); + + for(ushort tno = discInformation.Value.FirstTrackNumber; + tno <= discInformation?.LastTrackLastSession; tno++) + { + sense = _dev.ReadTrackInformation(out readBuffer, out _, false, + TrackInformationType.LogicalTrackNumber, tno, + _dev.Timeout, out _); + + if(sense) + continue; + + var trkInfo = TrackInformation.Decode(readBuffer); + + if(trkInfo is null) + continue; + + // Some drives return this invalid value with recordable discs + if(trkInfo.LogicalTrackNumber == 0) + continue; + + // Fixes a firmware bug in some DVD drives + if((int)trkInfo.LogicalTrackStartAddress < 0) + trkInfo.LogicalTrackStartAddress = 0; + + // Some drives return this invalid value with recordable discs + if(trkInfo.LogicalTrackSize == 0xFFFFFFFF) + trkInfo.LogicalTrackSize = (uint)(blocks - trkInfo.LogicalTrackStartAddress); + + var track = new Track + { + Sequence = trkInfo.LogicalTrackNumber, + Session = (ushort)(canStoreNotCdSessions ? trkInfo.SessionNumber : 1), + Type = TrackType.Data, + StartSector = trkInfo.LogicalTrackStartAddress, + EndSector = trkInfo.LogicalTrackSize + trkInfo.LogicalTrackStartAddress - 1, + RawBytesPerSector = (int)blockSize, + BytesPerSector = (int)blockSize, + SubchannelType = TrackSubchannelType.None + }; + + if(track.EndSector >= blocks) + blocks = track.EndSector + 1; + + tracks.Add(track); + } + + if(tracks.Count == 0) + tracks.Add(new Track + { + BytesPerSector = (int)blockSize, + EndSector = blocks - 1, + Sequence = 1, + RawBytesPerSector = (int)blockSize, + SubchannelType = TrackSubchannelType.None, + Session = 1, + Type = TrackType.Data + }); + else + tracks = tracks.OrderBy(t => t.Sequence).ToList(); + + ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); + + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputFormat.ErrorMessage); + + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + + Environment.NewLine + outputFormat.ErrorMessage); + + return; + } + + imageCreated = true; + + #if DEBUG + foreach(Track trk in tracks) + UpdateStatus?. + Invoke($"Track {trk.Sequence} starts at LBA {trk.StartSector} and ends at LBA {trk.EndSector}"); + #endif + + if(canStoreNotCdTracks) + { + ret = opticalPlugin.SetTracks(tracks); + + if(!ret) + { + _dumpLog.WriteLine("Error sending tracks to output image, not continuing."); + _dumpLog.WriteLine(opticalPlugin.ErrorMessage); + + StoppingErrorMessage?. + Invoke("Error sending tracks to output image, not continuing." + + Environment.NewLine + opticalPlugin.ErrorMessage); + + return; + } + } + else + opticalPlugin.SetTracks(new List + { + new() { BytesPerSector = (int)blockSize, EndSector = blocks - 1, @@ -502,752 +556,697 @@ namespace Aaru.Core.Devices.Dumping SubchannelType = TrackSubchannelType.None, Session = 1, Type = TrackType.Data - }); - else - tracks = tracks.OrderBy(t => t.Sequence).ToList(); - - ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); - - // Cannot create image - if(!ret) - { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputFormat.ErrorMessage); - - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + - Environment.NewLine + outputFormat.ErrorMessage); - - return; - } - - imageCreated = true; - - #if DEBUG - foreach(Track trk in tracks) - UpdateStatus?. - Invoke($"Track {trk.Sequence} starts at LBA {trk.StartSector} and ends at LBA {trk.EndSector}"); - #endif - - if(canStoreNotCdTracks) - { - ret = opticalPlugin.SetTracks(tracks); - - if(!ret) - { - _dumpLog.WriteLine("Error sending tracks to output image, not continuing."); - _dumpLog.WriteLine(opticalPlugin.ErrorMessage); - - StoppingErrorMessage?. - Invoke("Error sending tracks to output image, not continuing." + - Environment.NewLine + opticalPlugin.ErrorMessage); - - return; } - } - else - opticalPlugin.SetTracks(new List - { - new() - { - BytesPerSector = (int)blockSize, - EndSector = blocks - 1, - Sequence = 1, - RawBytesPerSector = (int)blockSize, - SubchannelType = TrackSubchannelType.None, - Session = 1, - Type = TrackType.Data - } - }); - } + }); } } - else - { - _dumpLog.WriteLine("The specified plugin does not support storing optical disc images.."); - StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images."); - - return; - } } - else if(decMode?.Pages != null) + else { - bool setGeometry = false; + _dumpLog.WriteLine("The specified plugin does not support storing optical disc images.."); + StoppingErrorMessage?.Invoke("The specified plugin does not support storing optical disc images."); - foreach(Modes.ModePage page in decMode.Value.Pages) - if(page.Page == 0x04 && - page.Subpage == 0x00) - { - Modes.ModePage_04? rigidPage = Modes.DecodeModePage_04(page.PageResponse); - - if(!rigidPage.HasValue || setGeometry) - continue; - - _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", - rigidPage.Value.Cylinders, rigidPage.Value.Heads, - (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); - - UpdateStatus?. - Invoke($"Setting geometry to {rigidPage.Value.Cylinders} cylinders, {rigidPage.Value.Heads} heads, {(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))} sectors per track"); - - outputFormat.SetGeometry(rigidPage.Value.Cylinders, rigidPage.Value.Heads, - (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); - - setGeometry = true; - } - else if(page.Page == 0x05 && - page.Subpage == 0x00) - { - Modes.ModePage_05? flexiblePage = Modes.DecodeModePage_05(page.PageResponse); - - if(!flexiblePage.HasValue) - continue; - - _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", - flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, - flexiblePage.Value.SectorsPerTrack); - - UpdateStatus?. - Invoke($"Setting geometry to {flexiblePage.Value.Cylinders} cylinders, {flexiblePage.Value.Heads} heads, {flexiblePage.Value.SectorsPerTrack} sectors per track"); - - outputFormat.SetGeometry(flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, - flexiblePage.Value.SectorsPerTrack); - - setGeometry = true; - } + return; } + } + else if(decMode?.Pages != null) + { + bool setGeometry = false; - if(!imageCreated) - { - ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); - - // Cannot create image - if(!ret) + foreach(Modes.ModePage page in decMode.Value.Pages) + if(page.Page == 0x04 && + page.Subpage == 0x00) { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputFormat.ErrorMessage); + Modes.ModePage_04? rigidPage = Modes.DecodeModePage_04(page.PageResponse); - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + - outputFormat.ErrorMessage); + if(!rigidPage.HasValue || setGeometry) + continue; - return; + _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", + rigidPage.Value.Cylinders, rigidPage.Value.Heads, + (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); + + UpdateStatus?. + Invoke($"Setting geometry to {rigidPage.Value.Cylinders} cylinders, {rigidPage.Value.Heads} heads, {(uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))} sectors per track"); + + outputFormat.SetGeometry(rigidPage.Value.Cylinders, rigidPage.Value.Heads, + (uint)(blocks / (rigidPage.Value.Cylinders * rigidPage.Value.Heads))); + + setGeometry = true; } - - if(writeSingleOpticalTrack) + else if(page.Page == 0x05 && + page.Subpage == 0x00) { - _dumpLog.WriteLine("Creating single track as could not retrieve track list from drive."); + Modes.ModePage_05? flexiblePage = Modes.DecodeModePage_05(page.PageResponse); - UpdateStatus?.Invoke("Creating single track as could not retrieve track list from drive."); + if(!flexiblePage.HasValue) + continue; - (outputFormat as IWritableOpticalImage)?.SetTracks(new List - { - new() - { - BytesPerSector = (int)blockSize, - EndSector = blocks - 1, - Sequence = 1, - RawBytesPerSector = (int)blockSize, - SubchannelType = TrackSubchannelType.None, - Session = 1, - Type = TrackType.Data - } - }); + _dumpLog.WriteLine("Setting geometry to {0} cylinders, {1} heads, {2} sectors per track", + flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, + flexiblePage.Value.SectorsPerTrack); + + UpdateStatus?. + Invoke($"Setting geometry to {flexiblePage.Value.Cylinders} cylinders, {flexiblePage.Value.Heads} heads, {flexiblePage.Value.SectorsPerTrack} sectors per track"); + + outputFormat.SetGeometry(flexiblePage.Value.Cylinders, flexiblePage.Value.Heads, + flexiblePage.Value.SectorsPerTrack); + + setGeometry = true; } - } + } - DumpHardwareType currentTry = null; - ExtentsULong extents = null; + if(!imageCreated) + { + ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); - ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, - _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, - _private, _force); - - if(currentTry == null || - extents == null) + // Cannot create image + if(!ret) { - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputFormat.ErrorMessage); + + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputFormat.ErrorMessage); return; } - if(_resume.NextBlock > 0) + if(writeSingleOpticalTrack) { - UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); - _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + _dumpLog.WriteLine("Creating single track as could not retrieve track list from drive."); + + UpdateStatus?.Invoke("Creating single track as could not retrieve track list from drive."); + + (outputFormat as IWritableOpticalImage)?.SetTracks(new List + { + new() + { + BytesPerSector = (int)blockSize, + EndSector = blocks - 1, + Sequence = 1, + RawBytesPerSector = (int)blockSize, + SubchannelType = TrackSubchannelType.None, + Session = 1, + Type = TrackType.Data + } + }); } + } - // Set speed - if(_speedMultiplier >= 0) - { - _dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX." : $"{_speed}x")}."); - UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX." : $"{_speed}x")}."); + DumpHardwareType currentTry = null; + ExtentsULong extents = null; - _speed *= _speedMultiplier; + ResumeSupport.Process(true, _dev.IsRemovable, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, + _dev.PlatformId, ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, + _private, _force); - if(_speed == 0 || - _speed > 0xFFFF) - _speed = 0xFFFF; + if(currentTry == null || + extents == null) + { + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); - _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _); - } + return; + } - if(_resume?.BlankExtents != null) - blankExtents = ExtentsConverter.FromMetadata(_resume.BlankExtents); + if(_resume.NextBlock > 0) + { + UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); + _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + } - bool newTrim = false; + // Set speed + if(_speedMultiplier >= 0) + { + _dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX." : $"{_speed}x")}."); + UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX." : $"{_speed}x")}."); - if(mediaTags.TryGetValue(MediaTagType.DVD_CMI, out byte[] cmi) && - Settings.Settings.Current.EnableDecryption && - _titleKeys && - dskType == MediaType.DVDROM && - (CopyrightType)cmi[0] == CopyrightType.CSS) - { - UpdateStatus?.Invoke("Title keys dumping is enabled. This will be very slow."); - _resume.MissingTitleKeys ??= new List(Enumerable.Range(0, (int)blocks).Select(n => (ulong)n)); - } + _speed *= _speedMultiplier; - if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) - ReadOpticalData(blocks, blocksToRead, blockSize, currentTry, extents, ref currentSpeed, ref minSpeed, - ref maxSpeed, ref totalDuration, scsiReader, mhddLog, ibgLog, ref imageWriteDuration, - ref newTrim, ref blankExtents); - else - ReadSbcData(blocks, blocksToRead, blockSize, currentTry, extents, ref currentSpeed, ref minSpeed, + if(_speed == 0 || + _speed > 0xFFFF) + _speed = 0xFFFF; + + _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _); + } + + if(_resume?.BlankExtents != null) + blankExtents = ExtentsConverter.FromMetadata(_resume.BlankExtents); + + bool newTrim = false; + + if(mediaTags.TryGetValue(MediaTagType.DVD_CMI, out byte[] cmi) && + Settings.Settings.Current.EnableDecryption && + _titleKeys && + dskType == MediaType.DVDROM && + (CopyrightType)cmi[0] == CopyrightType.CSS) + { + UpdateStatus?.Invoke("Title keys dumping is enabled. This will be very slow."); + _resume.MissingTitleKeys ??= new List(Enumerable.Range(0, (int)blocks).Select(n => (ulong)n)); + } + + if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) + ReadOpticalData(blocks, blocksToRead, blockSize, currentTry, extents, ref currentSpeed, ref minSpeed, ref maxSpeed, ref totalDuration, scsiReader, mhddLog, ibgLog, ref imageWriteDuration, - ref newTrim, ref dvdDecrypt, - mediaTags.ContainsKey(MediaTagType.DVD_DiscKey_Decrypted) - ? mediaTags[MediaTagType.DVD_DiscKey_Decrypted] : null); + ref newTrim, ref blankExtents); + else + ReadSbcData(blocks, blocksToRead, blockSize, currentTry, extents, ref currentSpeed, ref minSpeed, + ref maxSpeed, ref totalDuration, scsiReader, mhddLog, ibgLog, ref imageWriteDuration, + ref newTrim, ref dvdDecrypt, + mediaTags.ContainsKey(MediaTagType.DVD_DiscKey_Decrypted) + ? mediaTags[MediaTagType.DVD_DiscKey_Decrypted] : null); + end = DateTime.UtcNow; + mhddLog.Close(); + + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + + #region Trimming + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _trim && + newTrim) + { + start = DateTime.UtcNow; + UpdateStatus?.Invoke("Trimming skipped sectors"); + _dumpLog.WriteLine("Trimming skipped sectors"); + + InitProgress?.Invoke(); + + TrimSbcData(scsiReader, extents, currentTry, blankExtents); + + EndProgress?.Invoke(); end = DateTime.UtcNow; - mhddLog.Close(); + UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); + _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + } + #endregion Trimming - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + #region Error handling + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _retryPasses > 0) + RetrySbcData(scsiReader, currentTry, extents, ref totalDuration, blankExtents); - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + if(_resume.MissingTitleKeys?.Count > 0 && + !_aborted && + _retryPasses > 0 && + Settings.Settings.Current.EnableDecryption && + _titleKeys && + mediaTags.ContainsKey(MediaTagType.DVD_DiscKey_Decrypted)) + RetryTitleKeys(dvdDecrypt, mediaTags[MediaTagType.DVD_DiscKey_Decrypted], ref totalDuration); + #endregion Error handling - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - #region Trimming - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _trim && - newTrim) + if(opticalDisc) + { + foreach(KeyValuePair tag in mediaTags) { - start = DateTime.UtcNow; - UpdateStatus?.Invoke("Trimming skipped sectors"); - _dumpLog.WriteLine("Trimming skipped sectors"); - - InitProgress?.Invoke(); - - TrimSbcData(scsiReader, extents, currentTry, blankExtents); - - EndProgress?.Invoke(); - end = DateTime.UtcNow; - UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); - _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); - } - #endregion Trimming - - #region Error handling - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _retryPasses > 0) - RetrySbcData(scsiReader, currentTry, extents, ref totalDuration, blankExtents); - - if(_resume.MissingTitleKeys?.Count > 0 && - !_aborted && - _retryPasses > 0 && - Settings.Settings.Current.EnableDecryption && - _titleKeys && - mediaTags.ContainsKey(MediaTagType.DVD_DiscKey_Decrypted)) - RetryTitleKeys(dvdDecrypt, mediaTags[MediaTagType.DVD_DiscKey_Decrypted], ref totalDuration); - #endregion Error handling - - if(opticalDisc) - { - foreach(KeyValuePair tag in mediaTags) + if(tag.Value is null) { - if(tag.Value is null) - { - AaruConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key); + AaruConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key); - continue; - } - - ret = outputFormat.WriteMediaTag(tag.Value, tag.Key); - - if(ret || _force) - continue; - - // Cannot write tag to image - StoppingErrorMessage?.Invoke($"Cannot write tag {tag.Key}."); - - _dumpLog.WriteLine($"Cannot write tag {tag.Key}." + Environment.NewLine + - outputFormat.ErrorMessage); - - return; + continue; } + + ret = outputFormat.WriteMediaTag(tag.Value, tag.Key); + + if(ret || _force) + continue; + + // Cannot write tag to image + StoppingErrorMessage?.Invoke($"Cannot write tag {tag.Key}."); + + _dumpLog.WriteLine($"Cannot write tag {tag.Key}." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; } - else + } + else + { + if(!_dev.IsRemovable || + _dev.IsUsb) { - if(!_dev.IsRemovable || - _dev.IsUsb) + if(_dev.IsUsb && + _dev.UsbDescriptors != null) { - if(_dev.IsUsb && - _dev.UsbDescriptors != null) + UpdateStatus?.Invoke("Reading USB descriptors."); + _dumpLog.WriteLine("Reading USB descriptors."); + ret = outputFormat.WriteMediaTag(_dev.UsbDescriptors, MediaTagType.USB_Descriptors); + + if(!ret && + !_force) { - UpdateStatus?.Invoke("Reading USB descriptors."); - _dumpLog.WriteLine("Reading USB descriptors."); - ret = outputFormat.WriteMediaTag(_dev.UsbDescriptors, MediaTagType.USB_Descriptors); + _dumpLog.WriteLine("Cannot write USB descriptors."); - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write USB descriptors."); + StoppingErrorMessage?.Invoke("Cannot write USB descriptors." + Environment.NewLine + + outputFormat.ErrorMessage); - StoppingErrorMessage?.Invoke("Cannot write USB descriptors." + Environment.NewLine + - outputFormat.ErrorMessage); - - return; - } + return; } + } - byte[] cmdBuf; + byte[] cmdBuf; - if(_dev.Type == DeviceType.ATAPI) - { - UpdateStatus?.Invoke("Requesting ATAPI IDENTIFY PACKET DEVICE."); - _dumpLog.WriteLine("Requesting ATAPI IDENTIFY PACKET DEVICE."); - sense = _dev.AtapiIdentify(out cmdBuf, out _); - - if(!sense) - { - if(_private) - cmdBuf = DeviceReport.ClearIdentify(cmdBuf); - - ret = outputFormat.WriteMediaTag(cmdBuf, MediaTagType.ATAPI_IDENTIFY); - - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write ATAPI IDENTIFY PACKET DEVICE."); - - StoppingErrorMessage?.Invoke("Cannot write ATAPI IDENTIFY PACKET DEVICE." + - Environment.NewLine + outputFormat.ErrorMessage); - - return; - } - } - } - - sense = _dev.ScsiInquiry(out cmdBuf, out _); + if(_dev.Type == DeviceType.ATAPI) + { + UpdateStatus?.Invoke("Requesting ATAPI IDENTIFY PACKET DEVICE."); + _dumpLog.WriteLine("Requesting ATAPI IDENTIFY PACKET DEVICE."); + sense = _dev.AtapiIdentify(out cmdBuf, out _); if(!sense) { - UpdateStatus?.Invoke("Requesting SCSI INQUIRY."); - _dumpLog.WriteLine("Requesting SCSI INQUIRY."); - ret = outputFormat.WriteMediaTag(cmdBuf, MediaTagType.SCSI_INQUIRY); + if(_private) + cmdBuf = DeviceReport.ClearIdentify(cmdBuf); + + ret = outputFormat.WriteMediaTag(cmdBuf, MediaTagType.ATAPI_IDENTIFY); if(!ret && !_force) { - StoppingErrorMessage?.Invoke("Cannot write SCSI INQUIRY."); + _dumpLog.WriteLine("Cannot write ATAPI IDENTIFY PACKET DEVICE."); - _dumpLog.WriteLine("Cannot write SCSI INQUIRY." + Environment.NewLine + - outputFormat.ErrorMessage); + StoppingErrorMessage?.Invoke("Cannot write ATAPI IDENTIFY PACKET DEVICE." + + Environment.NewLine + outputFormat.ErrorMessage); return; } - - UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); - _dumpLog.WriteLine("Requesting MODE SENSE (10)."); - - sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, - 0xFF, 5, out _); - - if(!sense || - _dev.Error) - sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, - 0x3F, 0x00, 5, out _); - - if(!sense && - !_dev.Error) - if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) - { - ret = outputFormat.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_10); - - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write SCSI MODE SENSE (10)."); - - StoppingErrorMessage?.Invoke("Cannot write SCSI MODE SENSE (10)." + - Environment.NewLine + outputFormat.ErrorMessage); - - return; - } - } - - UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); - _dumpLog.WriteLine("Requesting MODE SENSE (6)."); - - sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, - 5, out _); - - if(sense || _dev.Error) - sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out _); - - if(sense || _dev.Error) - sense = _dev.ModeSense(out cmdBuf, out _, 5, out _); - - if(!sense && - !_dev.Error) - if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) - { - ret = outputFormat.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_6); - - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write SCSI MODE SENSE (6)."); - - StoppingErrorMessage?.Invoke("Cannot write SCSI MODE SENSE (6)." + - Environment.NewLine + outputFormat.ErrorMessage); - - return; - } - } } } - } - _resume.BadBlocks.Sort(); + sense = _dev.ScsiInquiry(out cmdBuf, out _); - foreach(ulong bad in _resume.BadBlocks) - _dumpLog.WriteLine("Sector {0} could not be read.", bad); - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - outputFormat.SetDumpHardware(_resume.Tries); - - // TODO: Media Serial Number - // TODO: Non-removable drive information - var metadata = new CommonTypes.Structs.ImageInfo - { - Application = "Aaru", - ApplicationVersion = Version.GetVersion() - }; - - if(!outputFormat.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputFormat.ErrorMessage); - - if(_preSidecar != null) - outputFormat.SetCicmMetadata(_preSidecar); - - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputFormat.Close(); - DateTime closeEnd = DateTime.Now; - UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); - - if(_aborted) - { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - return; - } - - double totalChkDuration = 0; - - if(_metadata) - { - // TODO: Layers - if(opticalDisc) - WriteOpticalSidecar(blockSize, blocks, dskType, null, mediaTags, 1, out totalChkDuration, null); - else + if(!sense) { - UpdateStatus?.Invoke("Creating sidecar."); - _dumpLog.WriteLine("Creating sidecar."); - var filters = new FiltersList(); - IFilter filter = filters.GetFilter(_outputPath); - IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; - ErrorNumber opened = inputPlugin.Open(filter); + UpdateStatus?.Invoke("Requesting SCSI INQUIRY."); + _dumpLog.WriteLine("Requesting SCSI INQUIRY."); + ret = outputFormat.WriteMediaTag(cmdBuf, MediaTagType.SCSI_INQUIRY); - if(opened != ErrorNumber.NoError) + if(!ret && + !_force) { - StoppingErrorMessage?.Invoke(string.Format("Error {0} opening created image.", opened)); + StoppingErrorMessage?.Invoke("Cannot write SCSI INQUIRY."); + + _dumpLog.WriteLine("Cannot write SCSI INQUIRY." + Environment.NewLine + + outputFormat.ErrorMessage); return; } - DateTime chkStart = DateTime.UtcNow; - _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); - _sidecarClass.InitProgressEvent += InitProgress; - _sidecarClass.UpdateProgressEvent += UpdateProgress; - _sidecarClass.EndProgressEvent += EndProgress; - _sidecarClass.InitProgressEvent2 += InitProgress2; - _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; - _sidecarClass.EndProgressEvent2 += EndProgress2; - _sidecarClass.UpdateStatusEvent += UpdateStatus; - CICMMetadataType sidecar = _sidecarClass.Create(); - end = DateTime.UtcNow; + UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); + _dumpLog.WriteLine("Requesting MODE SENSE (10)."); - if(!_aborted) - { - totalChkDuration = (end - chkStart).TotalMilliseconds; - UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); + sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, + 0xFF, 5, out _); - UpdateStatus?. - Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + if(!sense || + _dev.Error) + sense = _dev.ModeSense10(out cmdBuf, out _, false, true, ScsiModeSensePageControl.Current, + 0x3F, 0x00, 5, out _); - _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); - - _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); - - if(_preSidecar != null) + if(!sense && + !_dev.Error) + if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) { - _preSidecar.BlockMedia = sidecar.BlockMedia; - sidecar = _preSidecar; + ret = outputFormat.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_10); + + if(!ret && + !_force) + { + _dumpLog.WriteLine("Cannot write SCSI MODE SENSE (10)."); + + StoppingErrorMessage?.Invoke("Cannot write SCSI MODE SENSE (10)." + + Environment.NewLine + outputFormat.ErrorMessage); + + return; + } } - // All USB flash drives report as removable, even if the media is not removable - if(!_dev.IsRemovable || - _dev.IsUsb) + UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); + _dumpLog.WriteLine("Requesting MODE SENSE (6)."); + + sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, + 5, out _); + + if(sense || _dev.Error) + sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, + 0x00, 5, out _); + + if(sense || _dev.Error) + sense = _dev.ModeSense(out cmdBuf, out _, 5, out _); + + if(!sense && + !_dev.Error) + if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) { - if(_dev.IsUsb && - _dev.UsbDescriptors != null) - if(outputFormat.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) - sidecar.BlockMedia[0].USB = new USBType - { - ProductID = _dev.UsbProductId, - VendorID = _dev.UsbVendorId, - Descriptors = new DumpType - { - Image = _outputPath, - Size = (ulong)_dev.UsbDescriptors.Length, - Checksums = Checksum.GetChecksums(_dev.UsbDescriptors).ToArray() - } - }; + ret = outputFormat.WriteMediaTag(cmdBuf, MediaTagType.SCSI_MODESENSE_6); - byte[] cmdBuf; - - if(_dev.Type == DeviceType.ATAPI) + if(!ret && + !_force) { - sense = _dev.AtapiIdentify(out cmdBuf, out _); + _dumpLog.WriteLine("Cannot write SCSI MODE SENSE (6)."); - if(!sense) - if(outputFormat.SupportedMediaTags.Contains(MediaTagType.ATAPI_IDENTIFY)) - sidecar.BlockMedia[0].ATA = new ATAType - { - Identify = new DumpType - { - Image = _outputPath, - Size = (ulong)cmdBuf.Length, - Checksums = Checksum.GetChecksums(cmdBuf).ToArray() - } - }; + StoppingErrorMessage?.Invoke("Cannot write SCSI MODE SENSE (6)." + + Environment.NewLine + outputFormat.ErrorMessage); + + return; } + } + } + } + } - sense = _dev.ScsiInquiry(out cmdBuf, out _); + _resume.BadBlocks.Sort(); + + foreach(ulong bad in _resume.BadBlocks) + _dumpLog.WriteLine("Sector {0} could not be read.", bad); + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + outputFormat.SetDumpHardware(_resume.Tries); + + // TODO: Media Serial Number + // TODO: Non-removable drive information + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion() + }; + + if(!outputFormat.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputFormat.ErrorMessage); + + if(_preSidecar != null) + outputFormat.SetCicmMetadata(_preSidecar); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputFormat.Close(); + DateTime closeEnd = DateTime.Now; + UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); + _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + if(_metadata) + { + // TODO: Layers + if(opticalDisc) + WriteOpticalSidecar(blockSize, blocks, dskType, null, mediaTags, 1, out totalChkDuration, null); + else + { + UpdateStatus?.Invoke("Creating sidecar."); + _dumpLog.WriteLine("Creating sidecar."); + var filters = new FiltersList(); + IFilter filter = filters.GetFilter(_outputPath); + IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; + ErrorNumber opened = inputPlugin.Open(filter); + + if(opened != ErrorNumber.NoError) + { + StoppingErrorMessage?.Invoke(string.Format("Error {0} opening created image.", opened)); + + return; + } + + DateTime chkStart = DateTime.UtcNow; + _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); + _sidecarClass.InitProgressEvent += InitProgress; + _sidecarClass.UpdateProgressEvent += UpdateProgress; + _sidecarClass.EndProgressEvent += EndProgress; + _sidecarClass.InitProgressEvent2 += InitProgress2; + _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; + _sidecarClass.EndProgressEvent2 += EndProgress2; + _sidecarClass.UpdateStatusEvent += UpdateStatus; + CICMMetadataType sidecar = _sidecarClass.Create(); + end = DateTime.UtcNow; + + if(!_aborted) + { + totalChkDuration = (end - chkStart).TotalMilliseconds; + UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + + _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); + + _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + + if(_preSidecar != null) + { + _preSidecar.BlockMedia = sidecar.BlockMedia; + sidecar = _preSidecar; + } + + // All USB flash drives report as removable, even if the media is not removable + if(!_dev.IsRemovable || + _dev.IsUsb) + { + if(_dev.IsUsb && + _dev.UsbDescriptors != null) + if(outputFormat.SupportedMediaTags.Contains(MediaTagType.USB_Descriptors)) + sidecar.BlockMedia[0].USB = new USBType + { + ProductID = _dev.UsbProductId, + VendorID = _dev.UsbVendorId, + Descriptors = new DumpType + { + Image = _outputPath, + Size = (ulong)_dev.UsbDescriptors.Length, + Checksums = Checksum.GetChecksums(_dev.UsbDescriptors).ToArray() + } + }; + + byte[] cmdBuf; + + if(_dev.Type == DeviceType.ATAPI) + { + sense = _dev.AtapiIdentify(out cmdBuf, out _); if(!sense) - { - if(outputFormat.SupportedMediaTags.Contains(MediaTagType.SCSI_INQUIRY)) - sidecar.BlockMedia[0].SCSI = new SCSIType + if(outputFormat.SupportedMediaTags.Contains(MediaTagType.ATAPI_IDENTIFY)) + sidecar.BlockMedia[0].ATA = new ATAType { - Inquiry = new DumpType + Identify = new DumpType { Image = _outputPath, Size = (ulong)cmdBuf.Length, Checksums = Checksum.GetChecksums(cmdBuf).ToArray() } }; + } - // TODO: SCSI Extended Vendor Page descriptors - /* - UpdateStatus?.Invoke("Reading SCSI Extended Vendor Page Descriptors."); - dumpLog.WriteLine("Reading SCSI Extended Vendor Page Descriptors."); - sense = dev.ScsiInquiry(out cmdBuf, out _, 0x00); - if(!sense) + sense = _dev.ScsiInquiry(out cmdBuf, out _); + + if(!sense) + { + if(outputFormat.SupportedMediaTags.Contains(MediaTagType.SCSI_INQUIRY)) + sidecar.BlockMedia[0].SCSI = new SCSIType { - byte[] pages = EVPD.DecodePage00(cmdBuf); - - if(pages != null) + Inquiry = new DumpType { - List evpds = new List(); - foreach(byte page in pages) - { - dumpLog.WriteLine("Requesting page {0:X2}h.", page); - sense = dev.ScsiInquiry(out cmdBuf, out _, page); - if(sense) continue; - - EVPDType evpd = new EVPDType - { - Image = $"{outputPrefix}.evpd_{page:X2}h.bin", - Checksums = Checksum.GetChecksums(cmdBuf).ToArray(), - Size = cmdBuf.Length - }; - evpd.Checksums = Checksum.GetChecksums(cmdBuf).ToArray(); - DataFile.WriteTo("SCSI Dump", evpd.Image, cmdBuf); - evpds.Add(evpd); - } - - if(evpds.Count > 0) sidecar.BlockMedia[0].SCSI.EVPD = evpds.ToArray(); + Image = _outputPath, + Size = (ulong)cmdBuf.Length, + Checksums = Checksum.GetChecksums(cmdBuf).ToArray() } + }; + + // TODO: SCSI Extended Vendor Page descriptors + /* + UpdateStatus?.Invoke("Reading SCSI Extended Vendor Page Descriptors."); + dumpLog.WriteLine("Reading SCSI Extended Vendor Page Descriptors."); + sense = dev.ScsiInquiry(out cmdBuf, out _, 0x00); + if(!sense) + { + byte[] pages = EVPD.DecodePage00(cmdBuf); + + if(pages != null) + { + List evpds = new List(); + foreach(byte page in pages) + { + dumpLog.WriteLine("Requesting page {0:X2}h.", page); + sense = dev.ScsiInquiry(out cmdBuf, out _, page); + if(sense) continue; + + EVPDType evpd = new EVPDType + { + Image = $"{outputPrefix}.evpd_{page:X2}h.bin", + Checksums = Checksum.GetChecksums(cmdBuf).ToArray(), + Size = cmdBuf.Length + }; + evpd.Checksums = Checksum.GetChecksums(cmdBuf).ToArray(); + DataFile.WriteTo("SCSI Dump", evpd.Image, cmdBuf); + evpds.Add(evpd); + } + + if(evpds.Count > 0) sidecar.BlockMedia[0].SCSI.EVPD = evpds.ToArray(); } - */ + } + */ - UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); - _dumpLog.WriteLine("Requesting MODE SENSE (10)."); + UpdateStatus?.Invoke("Requesting MODE SENSE (10)."); + _dumpLog.WriteLine("Requesting MODE SENSE (10)."); + sense = _dev.ModeSense10(out cmdBuf, out _, false, true, + ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _); + + if(!sense || + _dev.Error) sense = _dev.ModeSense10(out cmdBuf, out _, false, true, - ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _); + ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); - if(!sense || - _dev.Error) - sense = _dev.ModeSense10(out cmdBuf, out _, false, true, - ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); + if(!sense && + !_dev.Error) + if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) + if(outputFormat.SupportedMediaTags.Contains(MediaTagType.SCSI_MODESENSE_10)) + sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType + { + Image = _outputPath, + Size = (ulong)cmdBuf.Length, + Checksums = Checksum.GetChecksums(cmdBuf).ToArray() + }; - if(!sense && - !_dev.Error) - if(Modes.DecodeMode10(cmdBuf, _dev.ScsiType).HasValue) - if(outputFormat.SupportedMediaTags.Contains(MediaTagType.SCSI_MODESENSE_10)) - sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType - { - Image = _outputPath, - Size = (ulong)cmdBuf.Length, - Checksums = Checksum.GetChecksums(cmdBuf).ToArray() - }; + UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); + _dumpLog.WriteLine("Requesting MODE SENSE (6)."); - UpdateStatus?.Invoke("Requesting MODE SENSE (6)."); - _dumpLog.WriteLine("Requesting MODE SENSE (6)."); + sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, + 0x3F, 0x00, 5, out _); + if(sense || _dev.Error) sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); - if(sense || _dev.Error) - sense = _dev.ModeSense6(out cmdBuf, out _, false, ScsiModeSensePageControl.Current, - 0x3F, 0x00, 5, out _); + if(sense || _dev.Error) + sense = _dev.ModeSense(out cmdBuf, out _, 5, out _); - if(sense || _dev.Error) - sense = _dev.ModeSense(out cmdBuf, out _, 5, out _); + if(!sense && + !_dev.Error) + if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) + if(outputFormat.SupportedMediaTags.Contains(MediaTagType.SCSI_MODESENSE_6)) + sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType + { + Image = _outputPath, + Size = (ulong)cmdBuf.Length, + Checksums = Checksum.GetChecksums(cmdBuf).ToArray() + }; + } + } - if(!sense && - !_dev.Error) - if(Modes.DecodeMode6(cmdBuf, _dev.ScsiType).HasValue) - if(outputFormat.SupportedMediaTags.Contains(MediaTagType.SCSI_MODESENSE_6)) - sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType - { - Image = _outputPath, - Size = (ulong)cmdBuf.Length, - Checksums = Checksum.GetChecksums(cmdBuf).ToArray() - }; - } + List<(ulong start, string type)> filesystems = new(); + + if(sidecar.BlockMedia[0].FileSystemInformation != null) + filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation + where partition.FileSystems != null + from fileSystem in partition.FileSystems + select (partition.StartSector, fileSystem.Type)); + + if(filesystems.Count > 0) + foreach(var filesystem in filesystems.Select(o => new + { + o.start, + o.type + }).Distinct()) + { + UpdateStatus?. + Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); + + _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, + filesystem.start); } - List<(ulong start, string type)> filesystems = new(); + sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); - if(sidecar.BlockMedia[0].FileSystemInformation != null) - filesystems.AddRange(from partition in sidecar.BlockMedia[0].FileSystemInformation - where partition.FileSystems != null - from fileSystem in partition.FileSystems - select (partition.StartSector, fileSystem.Type)); + (string type, string subType) xmlType = + CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); - if(filesystems.Count > 0) - foreach(var filesystem in filesystems.Select(o => new - { - o.start, - o.type - }).Distinct()) - { - UpdateStatus?. - Invoke($"Found filesystem {filesystem.type} at sector {filesystem.start}"); + sidecar.BlockMedia[0].DiskType = xmlType.type; + sidecar.BlockMedia[0].DiskSubType = xmlType.subType; - _dumpLog.WriteLine("Found filesystem {0} at sector {1}", filesystem.type, - filesystem.start); - } + // TODO: Implement device firmware revision + if(!_dev.IsRemovable || + _dev.IsUsb) + if(_dev.Type == DeviceType.ATAPI) + sidecar.BlockMedia[0].Interface = "ATAPI"; + else if(_dev.IsUsb) + sidecar.BlockMedia[0].Interface = "USB"; + else if(_dev.IsFireWire) + sidecar.BlockMedia[0].Interface = "FireWire"; + else + sidecar.BlockMedia[0].Interface = "SCSI"; - sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(dskType); + sidecar.BlockMedia[0].LogicalBlocks = blocks; + sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize; + sidecar.BlockMedia[0].LogicalBlockSize = logicalBlockSize; + sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; + sidecar.BlockMedia[0].Model = _dev.Model; - (string type, string subType) xmlType = - CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + if(!_private) + sidecar.BlockMedia[0].Serial = _dev.Serial; - sidecar.BlockMedia[0].DiskType = xmlType.type; - sidecar.BlockMedia[0].DiskSubType = xmlType.subType; + sidecar.BlockMedia[0].Size = blocks * blockSize; - // TODO: Implement device firmware revision - if(!_dev.IsRemovable || - _dev.IsUsb) - if(_dev.Type == DeviceType.ATAPI) - sidecar.BlockMedia[0].Interface = "ATAPI"; - else if(_dev.IsUsb) - sidecar.BlockMedia[0].Interface = "USB"; - else if(_dev.IsFireWire) - sidecar.BlockMedia[0].Interface = "FireWire"; - else - sidecar.BlockMedia[0].Interface = "SCSI"; + if(_dev.IsRemovable) + sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); - sidecar.BlockMedia[0].LogicalBlocks = blocks; - sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize; - sidecar.BlockMedia[0].LogicalBlockSize = logicalBlockSize; - sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; - sidecar.BlockMedia[0].Model = _dev.Model; + UpdateStatus?.Invoke("Writing metadata sidecar"); - if(!_private) - sidecar.BlockMedia[0].Serial = _dev.Serial; + var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - sidecar.BlockMedia[0].Size = blocks * blockSize; - - if(_dev.IsRemovable) - sidecar.BlockMedia[0].DumpHardwareArray = _resume.Tries.ToArray(); - - UpdateStatus?.Invoke("Writing metadata sidecar"); - - var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - - var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); - xmlSer.Serialize(xmlFs, sidecar); - xmlFs.Close(); - } + var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); } } - - UpdateStatus?.Invoke(""); - - UpdateStatus?. - Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); - - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); - - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); - - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); - - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - UpdateStatus?.Invoke(""); - - Statistics.AddMedia(dskType, true); } + + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + UpdateStatus?.Invoke(""); + + Statistics.AddMedia(dskType, true); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/Sbc/Error.cs b/Aaru.Core/Devices/Dumping/Sbc/Error.cs index 0cebdd5e4..cef558f67 100644 --- a/Aaru.Core/Devices/Dumping/Sbc/Error.cs +++ b/Aaru.Core/Devices/Dumping/Sbc/Error.cs @@ -43,377 +43,376 @@ using DVDDecryption = Aaru.Decryption.DVD.Dump; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Retries errored data when dumping from a SCSI Block Commands compliant device + /// Resume information + /// Correctly dump extents + /// Total time spent in commands + /// SCSI reader + /// Blank extents + void RetrySbcData(Reader scsiReader, DumpHardwareType currentTry, ExtentsULong extents, + ref double totalDuration, ExtentsULong blankExtents) { - /// Retries errored data when dumping from a SCSI Block Commands compliant device - /// Resume information - /// Correctly dump extents - /// Total time spent in commands - /// SCSI reader - /// Blank extents - void RetrySbcData(Reader scsiReader, DumpHardwareType currentTry, ExtentsULong extents, - ref double totalDuration, ExtentsULong blankExtents) + int pass = 1; + bool forward = true; + bool runningPersistent = false; + bool sense; + byte[] buffer; + bool recoveredError; + Modes.ModePage? currentModePage = null; + byte[] md6; + byte[] md10; + bool blankCheck; + bool newBlank = false; + var outputFormat = _outputPlugin as IWritableImage; + + if(_persistent) { - int pass = 1; - bool forward = true; - bool runningPersistent = false; - bool sense; - byte[] buffer; - bool recoveredError; - Modes.ModePage? currentModePage = null; - byte[] md6; - byte[] md10; - bool blankCheck; - bool newBlank = false; - var outputFormat = _outputPlugin as IWritableImage; + Modes.ModePage_01_MMC pgMmc; + Modes.ModePage_01 pg; - if(_persistent) + sense = _dev.ModeSense6(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, + out _); + + if(sense) { - Modes.ModePage_01_MMC pgMmc; - Modes.ModePage_01 pg; + sense = _dev.ModeSense10(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, + _dev.Timeout, out _); - sense = _dev.ModeSense6(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, _dev.Timeout, - out _); - - if(sense) + if(!sense) { - sense = _dev.ModeSense10(out buffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - _dev.Timeout, out _); + Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(buffer, _dev.ScsiType); - if(!sense) - { - Modes.DecodedMode? dcMode10 = Modes.DecodeMode10(buffer, _dev.ScsiType); - - if(dcMode10?.Pages != null) - foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) - currentModePage = modePage; - } - } - else - { - Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(buffer, _dev.ScsiType); - - if(dcMode6?.Pages != null) - foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) + if(dcMode10?.Pages != null) + foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) currentModePage = modePage; } + } + else + { + Modes.DecodedMode? dcMode6 = Modes.DecodeMode6(buffer, _dev.ScsiType); - if(currentModePage == null) - { - if(_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) - { - pgMmc = new Modes.ModePage_01_MMC - { - PS = false, - ReadRetryCount = 32, - Parameter = 0x00 - }; - - currentModePage = new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) - }; - } - else - { - pg = new Modes.ModePage_01 - { - PS = false, - AWRE = true, - ARRE = true, - TB = false, - RC = false, - EER = true, - PER = false, - DTE = true, - DCR = false, - ReadRetryCount = 32 - }; - - currentModePage = new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01(pg) - }; - } - } + if(dcMode6?.Pages != null) + foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) + currentModePage = modePage; + } + if(currentModePage == null) + { if(_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { pgMmc = new Modes.ModePage_01_MMC { PS = false, - ReadRetryCount = 255, - Parameter = 0x20 + ReadRetryCount = 32, + Parameter = 0x00 }; - var md = new Modes.DecodedMode + currentModePage = new Modes.ModePage { - Header = new Modes.ModeHeader(), - Pages = new[] - { - new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) - } - } + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - md10 = Modes.EncodeMode10(md, _dev.ScsiType); } else { pg = new Modes.ModePage_01 { PS = false, - AWRE = false, - ARRE = false, - TB = true, + AWRE = true, + ARRE = true, + TB = false, RC = false, EER = true, PER = false, - DTE = false, + DTE = true, DCR = false, - ReadRetryCount = 255 + ReadRetryCount = 32 }; - var md = new Modes.DecodedMode + currentModePage = new Modes.ModePage { - Header = new Modes.ModeHeader(), - Pages = new[] - { - new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01(pg) - } - } + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01(pg) }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - md10 = Modes.EncodeMode10(md, _dev.ScsiType); - } - - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); - sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - { - UpdateStatus?. - Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - - AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - - _dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - } - else - { - runningPersistent = true; } } - InitProgress?.Invoke(); - repeatRetry: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - - foreach(ulong badSector in tmpArray) + if(_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) { - if(_aborted) + pgMmc = new Modes.ModePage_01_MMC { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + PS = false, + ReadRetryCount = 255, + Parameter = 0x20 + }; - break; - } - - PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, - forward ? "forward" : "reverse", - runningPersistent ? "recovering partial data, " : "")); - - sense = scsiReader.ReadBlock(out buffer, badSector, out double cmdDuration, out recoveredError, - out blankCheck); - - totalDuration += cmdDuration; - - if(blankCheck) - { - _resume.BadBlocks.Remove(badSector); - blankExtents.Add(badSector, badSector); - newBlank = true; - - UpdateStatus?.Invoke($"Found blank block {badSector} in pass {pass}."); - _dumpLog.WriteLine("Found blank block {0} in pass {1}.", badSector, pass); - - continue; - } - - if((!sense && !_dev.Error) || recoveredError) - { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(buffer, badSector); - UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); - } - else if(runningPersistent) - { - outputFormat.WriteSector(buffer, badSector); - } - } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto repeatRetry; - } - - if(runningPersistent && currentModePage.HasValue) - { var md = new Modes.DecodedMode { Header = new Modes.ModeHeader(), Pages = new[] { - currentModePage.Value + new Modes.ModePage + { + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + } } }; md6 = Modes.EncodeMode6(md, _dev.ScsiType); md10 = Modes.EncodeMode10(md, _dev.ScsiType); + } + else + { + pg = new Modes.ModePage_01 + { + PS = false, + AWRE = false, + ARRE = false, + TB = true, + RC = false, + EER = true, + PER = false, + DTE = false, + DCR = false, + ReadRetryCount = 255 + }; - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); - sense = _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + new Modes.ModePage + { + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01(pg) + } + } + }; - if(sense) - _dev.ModeSelect10(md10, out _, true, false, _dev.Timeout, out _); + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + md10 = Modes.EncodeMode10(md, _dev.ScsiType); } - if(newBlank) - _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents); + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); + sense = _dev.ModeSelect(md6, out byte[] senseBuf, true, false, _dev.Timeout, out _); - EndProgress?.Invoke(); + if(sense) + sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) + { + UpdateStatus?. + Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + + AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); + + _dumpLog.WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); + } + else + { + runningPersistent = true; + } } - void RetryTitleKeys(DVDDecryption dvdDecrypt, byte[] discKey, ref double totalDuration) + InitProgress?.Invoke(); + repeatRetry: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); + + foreach(ulong badSector in tmpArray) { - int pass = 1; - bool forward = true; - bool sense; - byte[] buffer; - var outputFormat = _outputPlugin as IWritableImage; - - InitProgress?.Invoke(); - - repeatRetry: - ulong[] tmpArray = _resume.MissingTitleKeys.ToArray(); - - foreach(ulong missingKey in tmpArray) + if(_aborted) { - if(_aborted) - { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - break; + break; + } + + PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); + + sense = scsiReader.ReadBlock(out buffer, badSector, out double cmdDuration, out recoveredError, + out blankCheck); + + totalDuration += cmdDuration; + + if(blankCheck) + { + _resume.BadBlocks.Remove(badSector); + blankExtents.Add(badSector, badSector); + newBlank = true; + + UpdateStatus?.Invoke($"Found blank block {badSector} in pass {pass}."); + _dumpLog.WriteLine("Found blank block {0} in pass {1}.", badSector, pass); + + continue; + } + + if((!sense && !_dev.Error) || recoveredError) + { + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(buffer, badSector); + UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); + } + else if(runningPersistent) + { + outputFormat.WriteSector(buffer, badSector); + } + } + + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) + { + pass++; + forward = !forward; + _resume.BadBlocks.Sort(); + + if(!forward) + _resume.BadBlocks.Reverse(); + + goto repeatRetry; + } + + if(runningPersistent && currentModePage.HasValue) + { + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + currentModePage.Value } + }; - PulseProgress?.Invoke(string.Format("Retrying title key {0}, pass {1}, {2}", missingKey, pass, - forward ? "forward" : "reverse")); + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + md10 = Modes.EncodeMode10(md, _dev.ScsiType); - sense = dvdDecrypt.ReadTitleKey(out buffer, out _, DvdCssKeyClass.DvdCssCppmOrCprm, missingKey, - _dev.Timeout, out double cmdDuration); + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); + sense = _dev.ModeSelect(md6, out _, true, false, _dev.Timeout, out _); - totalDuration += cmdDuration; + if(sense) + _dev.ModeSelect10(md10, out _, true, false, _dev.Timeout, out _); + } - if(!sense && - !_dev.Error) + if(newBlank) + _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents); + + EndProgress?.Invoke(); + } + + void RetryTitleKeys(DVDDecryption dvdDecrypt, byte[] discKey, ref double totalDuration) + { + int pass = 1; + bool forward = true; + bool sense; + byte[] buffer; + var outputFormat = _outputPlugin as IWritableImage; + + InitProgress?.Invoke(); + + repeatRetry: + ulong[] tmpArray = _resume.MissingTitleKeys.ToArray(); + + foreach(ulong missingKey in tmpArray) + { + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + PulseProgress?.Invoke(string.Format("Retrying title key {0}, pass {1}, {2}", missingKey, pass, + forward ? "forward" : "reverse")); + + sense = dvdDecrypt.ReadTitleKey(out buffer, out _, DvdCssKeyClass.DvdCssCppmOrCprm, missingKey, + _dev.Timeout, out double cmdDuration); + + totalDuration += cmdDuration; + + if(!sense && + !_dev.Error) + { + CSS_CPRM.TitleKey? titleKey = CSS.DecodeTitleKey(buffer, dvdDecrypt.BusKey); + + if(titleKey.HasValue) { - CSS_CPRM.TitleKey? titleKey = CSS.DecodeTitleKey(buffer, dvdDecrypt.BusKey); - - if(titleKey.HasValue) + outputFormat.WriteSectorTag(new[] { - outputFormat.WriteSectorTag(new[] + titleKey.Value.CMI + }, missingKey, SectorTagType.DvdCmi); + + // If the CMI bit is 1, the sector is using copy protection, else it is not + // If the decoded title key is zeroed, there should be no copy protection + if((titleKey.Value.CMI & 0x80) >> 7 == 0 || + titleKey.Value.Key.All(k => k == 0)) + { + outputFormat.WriteSectorTag(new byte[] { - titleKey.Value.CMI - }, missingKey, SectorTagType.DvdCmi); + 0, 0, 0, 0, 0 + }, missingKey, SectorTagType.DvdTitleKey); - // If the CMI bit is 1, the sector is using copy protection, else it is not - // If the decoded title key is zeroed, there should be no copy protection - if((titleKey.Value.CMI & 0x80) >> 7 == 0 || - titleKey.Value.Key.All(k => k == 0)) + outputFormat.WriteSectorTag(new byte[] { - outputFormat.WriteSectorTag(new byte[] - { - 0, 0, 0, 0, 0 - }, missingKey, SectorTagType.DvdTitleKey); + 0, 0, 0, 0, 0 + }, missingKey, SectorTagType.DvdTitleKeyDecrypted); - outputFormat.WriteSectorTag(new byte[] - { - 0, 0, 0, 0, 0 - }, missingKey, SectorTagType.DvdTitleKeyDecrypted); + _resume.MissingTitleKeys.Remove(missingKey); + UpdateStatus?.Invoke($"Correctly retried title key {missingKey} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried title key {0} in pass {1}.", missingKey, pass); + } + else + { + outputFormat.WriteSectorTag(titleKey.Value.Key, missingKey, SectorTagType.DvdTitleKey); + _resume.MissingTitleKeys.Remove(missingKey); - _resume.MissingTitleKeys.Remove(missingKey); - UpdateStatus?.Invoke($"Correctly retried title key {missingKey} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried title key {0} in pass {1}.", missingKey, pass); + if(discKey != null) + { + CSS.DecryptTitleKey(0, discKey, titleKey.Value.Key, out buffer); + outputFormat.WriteSectorTag(buffer, missingKey, SectorTagType.DvdTitleKeyDecrypted); } - else - { - outputFormat.WriteSectorTag(titleKey.Value.Key, missingKey, SectorTagType.DvdTitleKey); - _resume.MissingTitleKeys.Remove(missingKey); - if(discKey != null) - { - CSS.DecryptTitleKey(0, discKey, titleKey.Value.Key, out buffer); - outputFormat.WriteSectorTag(buffer, missingKey, SectorTagType.DvdTitleKeyDecrypted); - } - - UpdateStatus?.Invoke($"Correctly retried title key {missingKey} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried title key {0} in pass {1}.", missingKey, pass); - } + UpdateStatus?.Invoke($"Correctly retried title key {missingKey} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried title key {0} in pass {1}.", missingKey, pass); } } } - - if(pass < _retryPasses && - !_aborted && - _resume.MissingTitleKeys.Count > 0) - { - pass++; - forward = !forward; - _resume.MissingTitleKeys.Sort(); - - if(!forward) - _resume.MissingTitleKeys.Reverse(); - - goto repeatRetry; - } - - EndProgress?.Invoke(); } + + if(pass < _retryPasses && + !_aborted && + _resume.MissingTitleKeys.Count > 0) + { + pass++; + forward = !forward; + _resume.MissingTitleKeys.Sort(); + + if(!forward) + _resume.MissingTitleKeys.Reverse(); + + goto repeatRetry; + } + + EndProgress?.Invoke(); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/Sbc/Optical.cs b/Aaru.Core/Devices/Dumping/Sbc/Optical.cs index 9c8347165..45b2c98f0 100644 --- a/Aaru.Core/Devices/Dumping/Sbc/Optical.cs +++ b/Aaru.Core/Devices/Dumping/Sbc/Optical.cs @@ -12,259 +12,258 @@ using Schemas; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// + /// Dumps data when dumping from a SCSI Block Commands compliant device, optical variant (magneto-optical and + /// successors) + /// + /// Media blocks + /// Maximum number of blocks to read in a single command + /// Block size in bytes + /// Resume information + /// Correctly dump extents + /// Current speed + /// Minimum speed + /// Maximum speed + /// Total time spent in commands + /// SCSI reader + /// MHDD log + /// ImgBurn log + /// Total time spent writing to image + /// Set if we need to start a trim + /// Blank extents + void ReadOpticalData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardwareType currentTry, + ExtentsULong extents, ref double currentSpeed, ref double minSpeed, ref double maxSpeed, + ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog, + ref double imageWriteDuration, ref bool newTrim, ref ExtentsULong blankExtents) { - /// - /// Dumps data when dumping from a SCSI Block Commands compliant device, optical variant (magneto-optical and - /// successors) - /// - /// Media blocks - /// Maximum number of blocks to read in a single command - /// Block size in bytes - /// Resume information - /// Correctly dump extents - /// Current speed - /// Minimum speed - /// Maximum speed - /// Total time spent in commands - /// SCSI reader - /// MHDD log - /// ImgBurn log - /// Total time spent writing to image - /// Set if we need to start a trim - /// Blank extents - void ReadOpticalData(in ulong blocks, in uint maxBlocksToRead, in uint blockSize, DumpHardwareType currentTry, - ExtentsULong extents, ref double currentSpeed, ref double minSpeed, ref double maxSpeed, - ref double totalDuration, Reader scsiReader, MhddLog mhddLog, IbgLog ibgLog, - ref double imageWriteDuration, ref bool newTrim, ref ExtentsULong blankExtents) + const uint maxBlocks = 256; + var writtenExtents = new ExtentsULong(); + bool written; + uint c = maxBlocks; + bool conditionMet; + bool changingCounter; + bool changingWritten; + uint blocksToRead = maxBlocksToRead; + bool sense; + byte[] buffer; + ulong sectorSpeedStart = 0; + DateTime timeSpeedStart = DateTime.UtcNow; + bool canMediumScan = true; + var outputFormat = _outputPlugin as IWritableImage; + + InitProgress?.Invoke(); + + if(blankExtents is null) { - const uint maxBlocks = 256; - var writtenExtents = new ExtentsULong(); - bool written; - uint c = maxBlocks; - bool conditionMet; - bool changingCounter; - bool changingWritten; - uint blocksToRead = maxBlocksToRead; - bool sense; - byte[] buffer; - ulong sectorSpeedStart = 0; - DateTime timeSpeedStart = DateTime.UtcNow; - bool canMediumScan = true; - var outputFormat = _outputPlugin as IWritableImage; + blankExtents = new ExtentsULong(); - InitProgress?.Invoke(); + written = _dev.MediumScan(out buffer, true, false, false, false, false, 0, 1, 1, out _, out _, + uint.MaxValue, out _); - if(blankExtents is null) + DecodedSense? decodedSense = Sense.Decode(buffer); + + if(_dev.LastError != 0 || + decodedSense?.SenseKey == SenseKeys.IllegalRequest) { - blankExtents = new ExtentsULong(); + UpdateStatus?. + Invoke("The current environment doesn't support the medium scan command, dump will take much longer than normal."); - written = _dev.MediumScan(out buffer, true, false, false, false, false, 0, 1, 1, out _, out _, - uint.MaxValue, out _); - - DecodedSense? decodedSense = Sense.Decode(buffer); - - if(_dev.LastError != 0 || - decodedSense?.SenseKey == SenseKeys.IllegalRequest) - { - UpdateStatus?. - Invoke("The current environment doesn't support the medium scan command, dump will take much longer than normal."); - - canMediumScan = false; - writtenExtents.Add(0, blocks - 1); - } - - // TODO: Find a place where MEDIUM SCAN works properly - else if(buffer?.Length > 0 && - !ArrayHelpers.ArrayIsNullOrEmpty(buffer)) - AaruConsole. - WriteLine("Please open a bug report in github with the manufacturer and model of this device, as well as your operating system name and version and this message: This environment correctly supports MEDIUM SCAN command."); - - changingCounter = false; - changingWritten = false; - - for(uint b = 0; b < blocks; b += c) - { - if(!canMediumScan) - break; - - if(_aborted) - { - _resume.BlankExtents = null; - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - if(changingWritten) - { - changingWritten = false; - written = !written; - c = maxBlocks; - } - - if(changingCounter) - { - b -= c; - changingCounter = false; - } - - if(b + c >= blocks) - c = (uint)(blocks - b); - - UpdateProgress?. - Invoke($"Scanning for {c} {(written ? "written" : "blank")} blocks starting in block {b}", b, - (long)blocks); - - conditionMet = _dev.MediumScan(out _, written, false, false, false, false, b, c, c, out _, out _, - uint.MaxValue, out _); - - if(conditionMet) - { - if(written) - writtenExtents.Add(b, c, true); - else - blankExtents.Add(b, c, true); - - if(c < maxBlocks) - changingWritten = true; - } - else - { - if(c > 64) - c /= 2; - else - c--; - - changingCounter = true; - - if(c != 0) - continue; - - written = !written; - c = maxBlocks; - } - } - - if(_resume != null && canMediumScan) - _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents); - - EndProgress?.Invoke(); - } - else - { + canMediumScan = false; writtenExtents.Add(0, blocks - 1); - - foreach(Tuple blank in blankExtents.ToArray()) - for(ulong b = blank.Item1; b <= blank.Item2; b++) - writtenExtents.Remove(b); } - if(writtenExtents.Count == 0) + // TODO: Find a place where MEDIUM SCAN works properly + else if(buffer?.Length > 0 && + !ArrayHelpers.ArrayIsNullOrEmpty(buffer)) + AaruConsole. + WriteLine("Please open a bug report in github with the manufacturer and model of this device, as well as your operating system name and version and this message: This environment correctly supports MEDIUM SCAN command."); + + changingCounter = false; + changingWritten = false; + + for(uint b = 0; b < blocks; b += c) { - UpdateStatus?.Invoke("Cannot dump empty media!"); - _dumpLog.WriteLine("Cannot dump empty media!"); + if(!canMediumScan) + break; - return; - } - - InitProgress?.Invoke(); - - Tuple[] extentsToDump = writtenExtents.ToArray(); - - foreach(Tuple extent in extentsToDump) - { - if(extent.Item2 < _resume.NextBlock) - continue; // Skip this extent - - ulong nextBlock = extent.Item1; - - if(extent.Item1 < _resume.NextBlock) - nextBlock = (uint)_resume.NextBlock; - - for(ulong i = nextBlock; i <= extent.Item2; i += blocksToRead) + if(_aborted) { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + _resume.BlankExtents = null; + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - break; - } + break; + } - if(extent.Item2 + 1 - i < blocksToRead) - blocksToRead = (uint)(extent.Item2 + 1 - i); + if(changingWritten) + { + changingWritten = false; + written = !written; + c = maxBlocks; + } - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; + if(changingCounter) + { + b -= c; + changingCounter = false; + } - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; + if(b + c >= blocks) + c = (uint)(blocks - b); - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)blocks); + UpdateProgress?. + Invoke($"Scanning for {c} {(written ? "written" : "blank")} blocks starting in block {b}", b, + (long)blocks); - sense = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _); - totalDuration += cmdDuration; + conditionMet = _dev.MediumScan(out _, written, false, false, false, false, b, c, c, out _, out _, + uint.MaxValue, out _); - if(!sense && - !_dev.Error) - { - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(buffer, i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(i, blocksToRead, true); - } + if(conditionMet) + { + if(written) + writtenExtents.Add(b, c, true); else - { - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly + blankExtents.Add(b, c, true); - if(i + _skip > extent.Item2 + 1) - _skip = (uint)(extent.Item2 + 1 - i); + if(c < maxBlocks) + changingWritten = true; + } + else + { + if(c > 64) + c /= 2; + else + c--; - // Write empty data - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + changingCounter = true; - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; - } - - sectorSpeedStart += blocksToRead; - _resume.NextBlock = i + blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) + if(c != 0) continue; - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; + written = !written; + c = maxBlocks; } } - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + if(_resume != null && canMediumScan) + _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents); EndProgress?.Invoke(); } + else + { + writtenExtents.Add(0, blocks - 1); + + foreach(Tuple blank in blankExtents.ToArray()) + for(ulong b = blank.Item1; b <= blank.Item2; b++) + writtenExtents.Remove(b); + } + + if(writtenExtents.Count == 0) + { + UpdateStatus?.Invoke("Cannot dump empty media!"); + _dumpLog.WriteLine("Cannot dump empty media!"); + + return; + } + + InitProgress?.Invoke(); + + Tuple[] extentsToDump = writtenExtents.ToArray(); + + foreach(Tuple extent in extentsToDump) + { + if(extent.Item2 < _resume.NextBlock) + continue; // Skip this extent + + ulong nextBlock = extent.Item1; + + if(extent.Item1 < _resume.NextBlock) + nextBlock = (uint)_resume.NextBlock; + + for(ulong i = nextBlock; i <= extent.Item2; i += blocksToRead) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + if(extent.Item2 + 1 - i < blocksToRead) + blocksToRead = (uint)(extent.Item2 + 1 - i); + + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)blocks); + + sense = scsiReader.ReadBlocks(out buffer, i, blocksToRead, out double cmdDuration, out _, out _); + totalDuration += cmdDuration; + + if(!sense && + !_dev.Error) + { + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(buffer, i, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(i, blocksToRead, true); + } + else + { + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + if(i + _skip > extent.Item2 + 1) + _skip = (uint)(extent.Item2 + 1 - i); + + // Write empty data + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); + + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; + } + + sectorSpeedStart += blocksToRead; + _resume.NextBlock = i + blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + } + + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + + EndProgress?.Invoke(); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/Sbc/Trim.cs b/Aaru.Core/Devices/Dumping/Sbc/Trim.cs index 2ff2f9191..c7437ab40 100644 --- a/Aaru.Core/Devices/Dumping/Sbc/Trim.cs +++ b/Aaru.Core/Devices/Dumping/Sbc/Trim.cs @@ -32,64 +32,63 @@ using Schemas; // ReSharper disable InlineOutVariableDeclaration // ReSharper disable TooWideLocalVariableScope -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +partial class Dump { - partial class Dump + /// Trims data when dumping from a SCSI Block Commands compliant device + /// SCSI reader + /// Correctly dump extents + /// Resume information + /// Blank extents + void TrimSbcData(Reader scsiReader, ExtentsULong extents, DumpHardwareType currentTry, + ExtentsULong blankExtents) { - /// Trims data when dumping from a SCSI Block Commands compliant device - /// SCSI reader - /// Correctly dump extents - /// Resume information - /// Blank extents - void TrimSbcData(Reader scsiReader, ExtentsULong extents, DumpHardwareType currentTry, - ExtentsULong blankExtents) + ulong[] tmpArray = _resume.BadBlocks.ToArray(); + bool sense; + bool recoveredError; + bool blankCheck; + byte[] buffer; + bool newBlank = false; + var outputFormat = _outputPlugin as IWritableImage; + + foreach(ulong badSector in tmpArray) { - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - bool sense; - bool recoveredError; - bool blankCheck; - byte[] buffer; - bool newBlank = false; - var outputFormat = _outputPlugin as IWritableImage; - - foreach(ulong badSector in tmpArray) + if(_aborted) { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - break; - } - - PulseProgress?.Invoke($"Trimming sector {badSector}"); - - sense = scsiReader.ReadBlock(out buffer, badSector, out double _, out recoveredError, out blankCheck); - - if(blankCheck) - { - blankExtents.Add(badSector, badSector); - newBlank = true; - _resume.BadBlocks.Remove(badSector); - - UpdateStatus?.Invoke($"Found blank block {badSector}."); - _dumpLog.WriteLine("Found blank block {0}.", badSector); - - continue; - } - - if((sense || _dev.Error) && - !recoveredError) - continue; - - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(buffer, badSector); + break; } - if(newBlank) - _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents); + PulseProgress?.Invoke($"Trimming sector {badSector}"); + + sense = scsiReader.ReadBlock(out buffer, badSector, out double _, out recoveredError, out blankCheck); + + if(blankCheck) + { + blankExtents.Add(badSector, badSector); + newBlank = true; + _resume.BadBlocks.Remove(badSector); + + UpdateStatus?.Invoke($"Found blank block {badSector}."); + _dumpLog.WriteLine("Found blank block {0}.", badSector); + + continue; + } + + if((sense || _dev.Error) && + !recoveredError) + continue; + + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(buffer, badSector); } + + if(newBlank) + _resume.BlankExtents = ExtentsConverter.ToMetadata(blankExtents); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/SecureDigital.cs b/Aaru.Core/Devices/Dumping/SecureDigital.cs index f65e1e125..163d55450 100644 --- a/Aaru.Core/Devices/Dumping/SecureDigital.cs +++ b/Aaru.Core/Devices/Dumping/SecureDigital.cs @@ -51,549 +51,668 @@ using Version = Aaru.CommonTypes.Interop.Version; // ReSharper disable JoinDeclarationAndInitializer -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Implements dumping a MultiMediaCard or SecureDigital flash card +public partial class Dump { - /// Implements dumping a MultiMediaCard or SecureDigital flash card - public partial class Dump + /// Dumps a MultiMediaCard or SecureDigital flash card + void SecureDigital() { - /// Dumps a MultiMediaCard or SecureDigital flash card - void SecureDigital() + if(_dumpRaw) { - if(_dumpRaw) - { - if(_force) - ErrorMessage?. - Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Continuing..."); - else - { - StoppingErrorMessage?. - Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Aborting..."); - - return; - } - } - - bool sense; - const ushort sdProfile = 0x0001; - const uint timeout = 5; - double duration; - ushort blocksToRead = 128; - uint blockSize = 512; - ulong blocks = 0; - byte[] csd = null; - byte[] ocr = null; - byte[] ecsd = null; - byte[] scr = null; - uint physicalBlockSize = 0; - bool byteAddressed = true; - uint[] response; - bool supportsCmd23 = false; - var outputFormat = _outputPlugin as IWritableImage; - - Dictionary mediaTags = new(); - - switch(_dev.Type) - { - case DeviceType.MMC: - { - UpdateStatus?.Invoke("Reading CSD"); - _dumpLog.WriteLine("Reading CSD"); - sense = _dev.ReadCsd(out csd, out response, timeout, out duration); - - if(!sense) - { - CSD csdDecoded = Decoders.MMC.Decoders.DecodeCSD(csd); - blocks = (ulong)((csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2)); - blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength); - - mediaTags.Add(MediaTagType.MMC_CSD, null); - - // Found at least since MMC System Specification 3.31 - supportsCmd23 = csdDecoded.Version >= 3; - - if(csdDecoded.Size == 0xFFF) - { - UpdateStatus?.Invoke("Reading Extended CSD"); - _dumpLog.WriteLine("Reading Extended CSD"); - sense = _dev.ReadExtendedCsd(out ecsd, out response, timeout, out duration); - - if(!sense) - { - ExtendedCSD ecsdDecoded = Decoders.MMC.Decoders.DecodeExtendedCSD(ecsd); - blocks = ecsdDecoded.SectorCount; - blockSize = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512); - - if(ecsdDecoded.NativeSectorSize == 0) - physicalBlockSize = 512; - else if(ecsdDecoded.NativeSectorSize == 1) - physicalBlockSize = 4096; - - blocksToRead = (ushort)(ecsdDecoded.OptimalReadSize * 4096 / blockSize); - - if(blocksToRead == 0) - blocksToRead = 128; - - // Supposing it's high-capacity MMC if it has Extended CSD... - byteAddressed = false; - mediaTags.Add(MediaTagType.MMC_ExtendedCSD, null); - } - else - { - _errorLog?.WriteLine("Read eCSD", _dev.Error, _dev.LastError, response); - ecsd = null; - } - } - } - else - { - _errorLog?.WriteLine("Read CSD", _dev.Error, _dev.LastError, response); - csd = null; - } - - UpdateStatus?.Invoke("Reading OCR"); - _dumpLog.WriteLine("Reading OCR"); - sense = _dev.ReadOcr(out ocr, out response, timeout, out duration); - - if(sense) - { - _errorLog?.WriteLine("Read OCR", _dev.Error, _dev.LastError, response); - ocr = null; - } - else - mediaTags.Add(MediaTagType.MMC_OCR, null); - - break; - } - - case DeviceType.SecureDigital: - { - UpdateStatus?.Invoke("Reading CSD"); - _dumpLog.WriteLine("Reading CSD"); - sense = _dev.ReadCsd(out csd, out response, timeout, out duration); - - if(!sense) - { - Decoders.SecureDigital.CSD csdDecoded = Decoders.SecureDigital.Decoders.DecodeCSD(csd); - - blocks = (ulong)(csdDecoded.Structure == 0 - ? (csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2) - : (csdDecoded.Size + 1) * 1024); - - blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength); - - // Structure >=1 for SDHC/SDXC, so that's block addressed - byteAddressed = csdDecoded.Structure == 0; - mediaTags.Add(MediaTagType.SD_CSD, null); - - physicalBlockSize = blockSize; - - if(blockSize != 512) - { - uint ratio = blockSize / 512; - blocks *= ratio; - blockSize = 512; - } - } - else - { - _errorLog?.WriteLine("Read CSD", _dev.Error, _dev.LastError, response); - csd = null; - } - - UpdateStatus?.Invoke("Reading OCR"); - _dumpLog.WriteLine("Reading OCR"); - sense = _dev.ReadSdocr(out ocr, out response, timeout, out duration); - - if(sense) - { - _errorLog?.WriteLine("Read OCR", _dev.Error, _dev.LastError, response); - ocr = null; - } - else - mediaTags.Add(MediaTagType.SD_OCR, null); - - UpdateStatus?.Invoke("Reading SCR"); - _dumpLog.WriteLine("Reading SCR"); - sense = _dev.ReadScr(out scr, out response, timeout, out duration); - - if(sense) - { - _errorLog?.WriteLine("Read SCR", _dev.Error, _dev.LastError, response); - scr = null; - } - else - { - supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(scr)?.CommandSupport. - HasFlag(CommandSupport.SetBlockCount) ?? false; - - mediaTags.Add(MediaTagType.SD_SCR, null); - } - - break; - } - } - - UpdateStatus?.Invoke("Reading CID"); - _dumpLog.WriteLine("Reading CID"); - sense = _dev.ReadCid(out byte[] cid, out response, timeout, out duration); - - if(sense) - { - _errorLog?.WriteLine("Read CID", _dev.Error, _dev.LastError, response); - cid = null; - } + if(_force) + ErrorMessage?. + Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Continuing..."); else - mediaTags.Add(_dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null); - - DateTime start; - DateTime end; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; - - if(blocks == 0) { - _dumpLog.WriteLine("Unable to get device size."); - StoppingErrorMessage?.Invoke("Unable to get device size."); + StoppingErrorMessage?. + Invoke("Raw dumping is not supported in MultiMediaCard or SecureDigital devices. Aborting..."); return; } + } - UpdateStatus?.Invoke($"Device reports {blocks} blocks."); - _dumpLog.WriteLine("Device reports {0} blocks.", blocks); + bool sense; + const ushort sdProfile = 0x0001; + const uint timeout = 5; + double duration; + ushort blocksToRead = 128; + uint blockSize = 512; + ulong blocks = 0; + byte[] csd = null; + byte[] ocr = null; + byte[] ecsd = null; + byte[] scr = null; + uint physicalBlockSize = 0; + bool byteAddressed = true; + uint[] response; + bool supportsCmd23 = false; + var outputFormat = _outputPlugin as IWritableImage; - byte[] cmdBuf; - bool error; + Dictionary mediaTags = new(); - if(blocksToRead > _maximumReadable) - blocksToRead = (ushort)_maximumReadable; - - if(supportsCmd23 && blocksToRead > 1) + switch(_dev.Type) + { + case DeviceType.MMC: { - sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, 1, byteAddressed, timeout, - out duration); + UpdateStatus?.Invoke("Reading CSD"); + _dumpLog.WriteLine("Reading CSD"); + sense = _dev.ReadCsd(out csd, out response, timeout, out duration); - if(sense || _dev.Error) - supportsCmd23 = false; + if(!sense) + { + CSD csdDecoded = Decoders.MMC.Decoders.DecodeCSD(csd); + blocks = (ulong)((csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2)); + blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength); - // Need to restart device, otherwise is it just busy streaming data with no one listening - sense = _dev.ReOpen(); + mediaTags.Add(MediaTagType.MMC_CSD, null); + + // Found at least since MMC System Specification 3.31 + supportsCmd23 = csdDecoded.Version >= 3; + + if(csdDecoded.Size == 0xFFF) + { + UpdateStatus?.Invoke("Reading Extended CSD"); + _dumpLog.WriteLine("Reading Extended CSD"); + sense = _dev.ReadExtendedCsd(out ecsd, out response, timeout, out duration); + + if(!sense) + { + ExtendedCSD ecsdDecoded = Decoders.MMC.Decoders.DecodeExtendedCSD(ecsd); + blocks = ecsdDecoded.SectorCount; + blockSize = (uint)(ecsdDecoded.SectorSize == 1 ? 4096 : 512); + + if(ecsdDecoded.NativeSectorSize == 0) + physicalBlockSize = 512; + else if(ecsdDecoded.NativeSectorSize == 1) + physicalBlockSize = 4096; + + blocksToRead = (ushort)(ecsdDecoded.OptimalReadSize * 4096 / blockSize); + + if(blocksToRead == 0) + blocksToRead = 128; + + // Supposing it's high-capacity MMC if it has Extended CSD... + byteAddressed = false; + mediaTags.Add(MediaTagType.MMC_ExtendedCSD, null); + } + else + { + _errorLog?.WriteLine("Read eCSD", _dev.Error, _dev.LastError, response); + ecsd = null; + } + } + } + else + { + _errorLog?.WriteLine("Read CSD", _dev.Error, _dev.LastError, response); + csd = null; + } + + UpdateStatus?.Invoke("Reading OCR"); + _dumpLog.WriteLine("Reading OCR"); + sense = _dev.ReadOcr(out ocr, out response, timeout, out duration); if(sense) { - StoppingErrorMessage?.Invoke($"Error {_dev.LastError} reopening device."); - - return; + _errorLog?.WriteLine("Read OCR", _dev.Error, _dev.LastError, response); + ocr = null; } + else + mediaTags.Add(MediaTagType.MMC_OCR, null); + + break; } - if(supportsCmd23 && blocksToRead > 1) + case DeviceType.SecureDigital: { - while(true) + UpdateStatus?.Invoke("Reading CSD"); + _dumpLog.WriteLine("Reading CSD"); + sense = _dev.ReadCsd(out csd, out response, timeout, out duration); + + if(!sense) { - error = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, - timeout, out duration); + Decoders.SecureDigital.CSD csdDecoded = Decoders.SecureDigital.Decoders.DecodeCSD(csd); - if(error) - blocksToRead /= 2; + blocks = (ulong)(csdDecoded.Structure == 0 + ? (csdDecoded.Size + 1) * Math.Pow(2, csdDecoded.SizeMultiplier + 2) + : (csdDecoded.Size + 1) * 1024); - if(!error || - blocksToRead == 1) - break; - } + blockSize = (uint)Math.Pow(2, csdDecoded.ReadBlockLength); - if(error) - { - _dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError); + // Structure >=1 for SDHC/SDXC, so that's block addressed + byteAddressed = csdDecoded.Structure == 0; + mediaTags.Add(MediaTagType.SD_CSD, null); - StoppingErrorMessage?. - Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + physicalBlockSize = blockSize; - return; - } - } - - if(_useBufferedReads && blocksToRead > 1 && !supportsCmd23) - { - while(true) - { - error = _dev.BufferedOsRead(out cmdBuf, 0, blockSize * blocksToRead, - out duration); - - if(error) - blocksToRead /= 2; - - if(!error || - blocksToRead == 1) - break; - - // Device is in timeout, reopen to reset - if(_dev.LastError == 110) - sense = _dev.ReOpen(); - } - - if(error) - { - UpdateStatus?.Invoke("Buffered OS reads are not working, trying direct commands."); - _dumpLog.WriteLine("Buffered OS reads are not working, trying direct commands."); - blocksToRead = 1; - _useBufferedReads = false; - } - } - - if(!_useBufferedReads && blocksToRead > 1 && !supportsCmd23) - { - while(true) - { - error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, 0, blockSize, blocksToRead, - byteAddressed, timeout, out duration); - - if(error) - blocksToRead /= 2; - - // Device is in timeout, reopen to reset - if(_dev.LastError == 110) - sense = _dev.ReOpen(); - - if(!error || - blocksToRead == 1) - break; - } - - if(error) - { - _dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError); - - StoppingErrorMessage?. - Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); - - return; - } - } - - if(blocksToRead == 1) - { - error = _dev.ReadSingleBlock(out cmdBuf, out _, 0, blockSize, byteAddressed, timeout, - out duration); - - if(error) - { - _dumpLog.WriteLine("ERROR: Could not read from device, device error {0}.", _dev.LastError); - - StoppingErrorMessage?. - Invoke($"Device error {_dev.LastError} trying to read from device."); - - return; - } - } - - if(supportsCmd23 || blocksToRead == 1) - { - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); - _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); - } - else if(_useBufferedReads) - { - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time using OS buffered reads."); - _dumpLog.WriteLine("Device can read {0} blocks at a time using OS buffered reads.", blocksToRead); - } - else - { - UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks using sequential commands."); - _dumpLog.WriteLine("Device can read {0} blocks using sequential commands.", blocksToRead); - } - - if(_skip < blocksToRead) - _skip = blocksToRead; - - DumpHardwareType currentTry = null; - ExtentsULong extents = null; - - ResumeSupport.Process(true, false, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, - ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private, _force); - - if(currentTry == null || - extents == null) - { - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); - - return; - } - - bool ret = true; - - foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputFormat.SupportedMediaTags.Contains(tag))) - { - ret = false; - _dumpLog.WriteLine($"Output format does not support {tag}."); - ErrorMessage?.Invoke($"Output format does not support {tag}."); - } - - if(!ret) - { - if(_force) - { - _dumpLog.WriteLine("Several media tags not supported, continuing..."); - ErrorMessage?.Invoke("Several media tags not supported, continuing..."); + if(blockSize != 512) + { + uint ratio = blockSize / 512; + blocks *= ratio; + blockSize = 512; + } } else { - _dumpLog.WriteLine("Several media tags not supported, not continuing..."); - StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); - - return; + _errorLog?.WriteLine("Read CSD", _dev.Error, _dev.LastError, response); + csd = null; } + + UpdateStatus?.Invoke("Reading OCR"); + _dumpLog.WriteLine("Reading OCR"); + sense = _dev.ReadSdocr(out ocr, out response, timeout, out duration); + + if(sense) + { + _errorLog?.WriteLine("Read OCR", _dev.Error, _dev.LastError, response); + ocr = null; + } + else + mediaTags.Add(MediaTagType.SD_OCR, null); + + UpdateStatus?.Invoke("Reading SCR"); + _dumpLog.WriteLine("Reading SCR"); + sense = _dev.ReadScr(out scr, out response, timeout, out duration); + + if(sense) + { + _errorLog?.WriteLine("Read SCR", _dev.Error, _dev.LastError, response); + scr = null; + } + else + { + supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(scr)?.CommandSupport. + HasFlag(CommandSupport.SetBlockCount) ?? false; + + mediaTags.Add(MediaTagType.SD_SCR, null); + } + + break; + } + } + + UpdateStatus?.Invoke("Reading CID"); + _dumpLog.WriteLine("Reading CID"); + sense = _dev.ReadCid(out byte[] cid, out response, timeout, out duration); + + if(sense) + { + _errorLog?.WriteLine("Read CID", _dev.Error, _dev.LastError, response); + cid = null; + } + else + mediaTags.Add(_dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID : MediaTagType.MMC_CID, null); + + DateTime start; + DateTime end; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + + if(blocks == 0) + { + _dumpLog.WriteLine("Unable to get device size."); + StoppingErrorMessage?.Invoke("Unable to get device size."); + + return; + } + + UpdateStatus?.Invoke($"Device reports {blocks} blocks."); + _dumpLog.WriteLine("Device reports {0} blocks.", blocks); + + byte[] cmdBuf; + bool error; + + if(blocksToRead > _maximumReadable) + blocksToRead = (ushort)_maximumReadable; + + if(supportsCmd23 && blocksToRead > 1) + { + sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, 1, byteAddressed, timeout, + out duration); + + if(sense || _dev.Error) + supportsCmd23 = false; + + // Need to restart device, otherwise is it just busy streaming data with no one listening + sense = _dev.ReOpen(); + + if(sense) + { + StoppingErrorMessage?.Invoke($"Error {_dev.LastError} reopening device."); + + return; + } + } + + if(supportsCmd23 && blocksToRead > 1) + { + while(true) + { + error = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, + timeout, out duration); + + if(error) + blocksToRead /= 2; + + if(!error || + blocksToRead == 1) + break; } - var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); - var ibgLog = new IbgLog(_outputPrefix + ".ibg", sdProfile); - - ret = outputFormat.Create(_outputPath, - _dev.Type == DeviceType.SecureDigital ? MediaType.SecureDigital : MediaType.MMC, - _formatOptions, blocks, blockSize); - - // Cannot create image - if(!ret) + if(error) { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputFormat.ErrorMessage); + _dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError); - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + StoppingErrorMessage?. + Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + + return; + } + } + + if(_useBufferedReads && blocksToRead > 1 && !supportsCmd23) + { + while(true) + { + error = _dev.BufferedOsRead(out cmdBuf, 0, blockSize * blocksToRead, + out duration); + + if(error) + blocksToRead /= 2; + + if(!error || + blocksToRead == 1) + break; + + // Device is in timeout, reopen to reset + if(_dev.LastError == 110) + sense = _dev.ReOpen(); + } + + if(error) + { + UpdateStatus?.Invoke("Buffered OS reads are not working, trying direct commands."); + _dumpLog.WriteLine("Buffered OS reads are not working, trying direct commands."); + blocksToRead = 1; + _useBufferedReads = false; + } + } + + if(!_useBufferedReads && blocksToRead > 1 && !supportsCmd23) + { + while(true) + { + error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, 0, blockSize, blocksToRead, + byteAddressed, timeout, out duration); + + if(error) + blocksToRead /= 2; + + // Device is in timeout, reopen to reset + if(_dev.LastError == 110) + sense = _dev.ReOpen(); + + if(!error || + blocksToRead == 1) + break; + } + + if(error) + { + _dumpLog.WriteLine("ERROR: Cannot get blocks to read, device error {0}.", _dev.LastError); + + StoppingErrorMessage?. + Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + + return; + } + } + + if(blocksToRead == 1) + { + error = _dev.ReadSingleBlock(out cmdBuf, out _, 0, blockSize, byteAddressed, timeout, + out duration); + + if(error) + { + _dumpLog.WriteLine("ERROR: Could not read from device, device error {0}.", _dev.LastError); + + StoppingErrorMessage?. + Invoke($"Device error {_dev.LastError} trying to read from device."); + + return; + } + } + + if(supportsCmd23 || blocksToRead == 1) + { + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time."); + _dumpLog.WriteLine("Device can read {0} blocks at a time.", blocksToRead); + } + else if(_useBufferedReads) + { + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks at a time using OS buffered reads."); + _dumpLog.WriteLine("Device can read {0} blocks at a time using OS buffered reads.", blocksToRead); + } + else + { + UpdateStatus?.Invoke($"Device can read {blocksToRead} blocks using sequential commands."); + _dumpLog.WriteLine("Device can read {0} blocks using sequential commands.", blocksToRead); + } + + if(_skip < blocksToRead) + _skip = blocksToRead; + + DumpHardwareType currentTry = null; + ExtentsULong extents = null; + + ResumeSupport.Process(true, false, blocks, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, + ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private, _force); + + if(currentTry == null || + extents == null) + { + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + + return; + } + + bool ret = true; + + foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputFormat.SupportedMediaTags.Contains(tag))) + { + ret = false; + _dumpLog.WriteLine($"Output format does not support {tag}."); + ErrorMessage?.Invoke($"Output format does not support {tag}."); + } + + if(!ret) + { + if(_force) + { + _dumpLog.WriteLine("Several media tags not supported, continuing..."); + ErrorMessage?.Invoke("Several media tags not supported, continuing..."); + } + else + { + _dumpLog.WriteLine("Several media tags not supported, not continuing..."); + StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + + return; + } + } + + var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); + var ibgLog = new IbgLog(_outputPrefix + ".ibg", sdProfile); + + ret = outputFormat.Create(_outputPath, + _dev.Type == DeviceType.SecureDigital ? MediaType.SecureDigital : MediaType.MMC, + _formatOptions, blocks, blockSize); + + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputFormat.ErrorMessage); + + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; + } + + if(cid != null) + { + if(_dev.Type == DeviceType.SecureDigital && _private) + { + // Clear serial number and manufacturing date + cid[9] = 0; + cid[10] = 0; + cid[11] = 0; + cid[12] = 0; + cid[13] = 0; + cid[14] = 0; + } + else if(_dev.Type == DeviceType.MMC && _private) + { + // Clear serial number and manufacturing date + cid[10] = 0; + cid[11] = 0; + cid[12] = 0; + cid[13] = 0; + cid[14] = 0; + } + + ret = + outputFormat.WriteMediaTag(cid, + _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID + : MediaTagType.MMC_CID); + + // Cannot write CID to image + if(!ret && + !_force) + { + _dumpLog.WriteLine("Cannot write CID to output image."); + + StoppingErrorMessage?.Invoke("Cannot write CID to output image." + Environment.NewLine + outputFormat.ErrorMessage); return; } + } - if(cid != null) + if(csd != null) + { + ret = + outputFormat.WriteMediaTag(csd, + _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CSD + : MediaTagType.MMC_CSD); + + // Cannot write CSD to image + if(!ret && + !_force) { - if(_dev.Type == DeviceType.SecureDigital && _private) - { - // Clear serial number and manufacturing date - cid[9] = 0; - cid[10] = 0; - cid[11] = 0; - cid[12] = 0; - cid[13] = 0; - cid[14] = 0; - } - else if(_dev.Type == DeviceType.MMC && _private) - { - // Clear serial number and manufacturing date - cid[10] = 0; - cid[11] = 0; - cid[12] = 0; - cid[13] = 0; - cid[14] = 0; - } + _dumpLog.WriteLine("Cannot write CSD to output image."); - ret = - outputFormat.WriteMediaTag(cid, - _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CID - : MediaTagType.MMC_CID); + StoppingErrorMessage?.Invoke("Cannot write CSD to output image." + Environment.NewLine + + outputFormat.ErrorMessage); - // Cannot write CID to image - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write CID to output image."); + return; + } + } - StoppingErrorMessage?.Invoke("Cannot write CID to output image." + Environment.NewLine + - outputFormat.ErrorMessage); + if(ecsd != null) + { + ret = outputFormat.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD); - return; - } + // Cannot write Extended CSD to image + if(!ret && + !_force) + { + _dumpLog.WriteLine("Cannot write Extended CSD to output image."); + + StoppingErrorMessage?.Invoke("Cannot write Extended CSD to output image." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; + } + } + + if(ocr != null) + { + ret = + outputFormat.WriteMediaTag(ocr, + _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_OCR + : MediaTagType.MMC_OCR); + + // Cannot write OCR to image + if(!ret && + !_force) + { + _dumpLog.WriteLine("Cannot write OCR to output image."); + + StoppingErrorMessage?.Invoke("Cannot write OCR to output image." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; + } + } + + if(scr != null) + { + ret = outputFormat.WriteMediaTag(scr, MediaTagType.SD_SCR); + + // Cannot write SCR to image + if(!ret && + !_force) + { + _dumpLog.WriteLine("Cannot write SCR to output image."); + + StoppingErrorMessage?.Invoke("Cannot write SCR to output image." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; + } + } + + if(_resume.NextBlock > 0) + { + UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); + _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + } + + start = DateTime.UtcNow; + double imageWriteDuration = 0; + bool newTrim = false; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + + InitProgress?.Invoke(); + + for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; } - if(csd != null) + if(blocks - i < blocksToRead) + blocksToRead = (byte)(blocks - i); + + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)blocks); + + if(blocksToRead == 1) + error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout, + out duration); + else if(supportsCmd23) + error = _dev.ReadWithBlockCount(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, + timeout, out duration); + else if(_useBufferedReads) + error = _dev.BufferedOsRead(out cmdBuf, (long)(i * blockSize), blockSize * blocksToRead, + out duration); + else + error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, + byteAddressed, timeout, out duration); + + if(!error) { - ret = - outputFormat.WriteMediaTag(csd, - _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_CSD - : MediaTagType.MMC_CSD); + mhddLog.Write(i, duration); + ibgLog.Write(i, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(cmdBuf, i, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(i, blocksToRead, true); + } + else + { + _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, byteAddressed, response); - // Cannot write CSD to image - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write CSD to output image."); + if(i + _skip > blocks) + _skip = (uint)(blocks - i); - StoppingErrorMessage?.Invoke("Cannot write CSD to output image." + Environment.NewLine + - outputFormat.ErrorMessage); + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); - return; - } + mhddLog.Write(i, duration < 500 ? 65535 : duration); + + ibgLog.Write(i, 0); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; + newTrim = true; } - if(ecsd != null) - { - ret = outputFormat.WriteMediaTag(ecsd, MediaTagType.MMC_ExtendedCSD); + sectorSpeedStart += blocksToRead; + _resume.NextBlock = i + blocksToRead; - // Cannot write Extended CSD to image - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write Extended CSD to output image."); + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - StoppingErrorMessage?.Invoke("Cannot write Extended CSD to output image." + Environment.NewLine + - outputFormat.ErrorMessage); + if(elapsed <= 0) + continue; - return; - } - } + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } - if(ocr != null) - { - ret = - outputFormat.WriteMediaTag(ocr, - _dev.Type == DeviceType.SecureDigital ? MediaTagType.SD_OCR - : MediaTagType.MMC_OCR); + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - // Cannot write OCR to image - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write OCR to output image."); + end = DateTime.Now; + EndProgress?.Invoke(); + mhddLog.Close(); - StoppingErrorMessage?.Invoke("Cannot write OCR to output image." + Environment.NewLine + - outputFormat.ErrorMessage); + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - return; - } - } + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - if(scr != null) - { - ret = outputFormat.WriteMediaTag(scr, MediaTagType.SD_SCR); + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - // Cannot write SCR to image - if(!ret && - !_force) - { - _dumpLog.WriteLine("Cannot write SCR to output image."); + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - StoppingErrorMessage?.Invoke("Cannot write SCR to output image." + Environment.NewLine + - outputFormat.ErrorMessage); + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - return; - } - } + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - if(_resume.NextBlock > 0) - { - UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); - _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); - } + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + #region Trimming + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _trim && + newTrim) + { start = DateTime.UtcNow; - double imageWriteDuration = 0; - bool newTrim = false; - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; + UpdateStatus?.Invoke("Trimming skipped sectors"); + _dumpLog.WriteLine("Trimming skipped sectors"); + ulong[] tmpArray = _resume.BadBlocks.ToArray(); InitProgress?.Invoke(); - for(ulong i = _resume.NextBlock; i < blocks; i += blocksToRead) + foreach(ulong badSector in tmpArray) { if(_aborted) { @@ -604,373 +723,253 @@ namespace Aaru.Core.Devices.Dumping break; } - if(blocks - i < blocksToRead) - blocksToRead = (byte)(blocks - i); + PulseProgress?.Invoke($"Trimming sector {badSector}"); - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; + error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed, + timeout, out duration); - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; + totalDuration += duration; - UpdateProgress?.Invoke($"Reading sector {i} of {blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)blocks); + if(error) + { + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, byteAddressed, response); - if(blocksToRead == 1) - error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout, - out duration); - else if(supportsCmd23) - error = _dev.ReadWithBlockCount(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, - timeout, out duration); - else if(_useBufferedReads) - error = _dev.BufferedOsRead(out cmdBuf, (long)(i * blockSize), blockSize * blocksToRead, - out duration); - else - error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, - byteAddressed, timeout, out duration); + continue; + } + + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(cmdBuf, badSector); + } + + EndProgress?.Invoke(); + end = DateTime.UtcNow; + UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); + _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + } + #endregion Trimming + + #region Error handling + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _retryPasses > 0) + { + int pass = 1; + bool forward = true; + bool runningPersistent = false; + + InitProgress?.Invoke(); + repeatRetryLba: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); + + foreach(ulong badSector in tmpArray) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); + + error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed, + timeout, out duration); + + totalDuration += duration; + + if(error) + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, byteAddressed, response); if(!error) { - mhddLog.Write(i, duration); - ibgLog.Write(i, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(cmdBuf, i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(i, blocksToRead, true); - } - else - { - _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, byteAddressed, response); - - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - mhddLog.Write(i, duration < 500 ? 65535 : duration); - - ibgLog.Write(i, 0); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - newTrim = true; - } - - sectorSpeedStart += blocksToRead; - _resume.NextBlock = i + blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - - end = DateTime.Now; - EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - #region Trimming - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _trim && - newTrim) - { - start = DateTime.UtcNow; - UpdateStatus?.Invoke("Trimming skipped sectors"); - _dumpLog.WriteLine("Trimming skipped sectors"); - - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - InitProgress?.Invoke(); - - foreach(ulong badSector in tmpArray) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke($"Trimming sector {badSector}"); - - error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed, - timeout, out duration); - - totalDuration += duration; - - if(error) - { - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, byteAddressed, response); - - continue; - } - _resume.BadBlocks.Remove(badSector); extents.Add(badSector); outputFormat.WriteSector(cmdBuf, badSector); + UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); } - - EndProgress?.Invoke(); - end = DateTime.UtcNow; - UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); - _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); - } - #endregion Trimming - - #region Error handling - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _retryPasses > 0) - { - int pass = 1; - bool forward = true; - bool runningPersistent = false; - - InitProgress?.Invoke(); - repeatRetryLba: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - - foreach(ulong badSector in tmpArray) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, - forward ? "forward" : "reverse", - runningPersistent ? "recovering partial data, " : "")); - - error = _dev.ReadSingleBlock(out cmdBuf, out response, (uint)badSector, blockSize, byteAddressed, - timeout, out duration); - - totalDuration += duration; - - if(error) - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, byteAddressed, response); - - if(!error) - { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(cmdBuf, badSector); - UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); - } - else if(runningPersistent) - outputFormat.WriteSector(cmdBuf, badSector); - } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto repeatRetryLba; - } - - EndProgress?.Invoke(); - } - #endregion Error handling - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - outputFormat.SetDumpHardware(_resume.Tries); - - // TODO: Drive info - var metadata = new CommonTypes.Structs.ImageInfo - { - Application = "Aaru", - ApplicationVersion = Version.GetVersion() - }; - - if(!outputFormat.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputFormat.ErrorMessage); - - if(_preSidecar != null) - outputFormat.SetCicmMetadata(_preSidecar); - - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputFormat.Close(); - DateTime closeEnd = DateTime.Now; - UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); - - if(_aborted) - { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - return; + else if(runningPersistent) + outputFormat.WriteSector(cmdBuf, badSector); } - double totalChkDuration = 0; - - if(_metadata) + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) { - UpdateStatus?.Invoke("Creating sidecar."); - _dumpLog.WriteLine("Creating sidecar."); - var filters = new FiltersList(); - IFilter filter = filters.GetFilter(_outputPath); - IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; - ErrorNumber opened = inputPlugin.Open(filter); - - if(opened != ErrorNumber.NoError) - StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); - - DateTime chkStart = DateTime.UtcNow; - _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); - _sidecarClass.InitProgressEvent += InitProgress; - _sidecarClass.UpdateProgressEvent += UpdateProgress; - _sidecarClass.EndProgressEvent += EndProgress; - _sidecarClass.InitProgressEvent2 += InitProgress2; - _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; - _sidecarClass.EndProgressEvent2 += EndProgress2; - _sidecarClass.UpdateStatusEvent += UpdateStatus; - CICMMetadataType sidecar = _sidecarClass.Create(); - - if(!_aborted) - { - if(_preSidecar != null) - { - _preSidecar.BlockMedia = sidecar.BlockMedia; - sidecar = _preSidecar; - } - - end = DateTime.UtcNow; - - totalChkDuration = (end - chkStart).TotalMilliseconds; - UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); - - _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); - - _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); - - (string type, string subType) xmlType = (null, null); - - switch(_dev.Type) - { - case DeviceType.MMC: - xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC); - - sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC); - - break; - case DeviceType.SecureDigital: - CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital); - - sidecar.BlockMedia[0].Dimensions = - Dimensions.DimensionsFromMediaType(MediaType.SecureDigital); - - break; - } - - sidecar.BlockMedia[0].DiskType = xmlType.type; - sidecar.BlockMedia[0].DiskSubType = xmlType.subType; - - // TODO: Implement device firmware revision - sidecar.BlockMedia[0].LogicalBlocks = blocks; - sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : blockSize; - sidecar.BlockMedia[0].LogicalBlockSize = blockSize; - sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; - sidecar.BlockMedia[0].Model = _dev.Model; - - if(!_private) - sidecar.BlockMedia[0].Serial = _dev.Serial; - - sidecar.BlockMedia[0].Size = blocks * blockSize; - - UpdateStatus?.Invoke("Writing metadata sidecar"); - - var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); - - var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); - xmlSer.Serialize(xmlFs, sidecar); - xmlFs.Close(); - } - } - - UpdateStatus?.Invoke(""); - - UpdateStatus?. - Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); - - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); - - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); - - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); - - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - UpdateStatus?.Invoke(""); - - if(_resume.BadBlocks.Count > 0) + pass++; + forward = !forward; _resume.BadBlocks.Sort(); - switch(_dev.Type) - { - case DeviceType.MMC: - Statistics.AddMedia(MediaType.MMC, true); + if(!forward) + _resume.BadBlocks.Reverse(); - break; - case DeviceType.SecureDigital: - Statistics.AddMedia(MediaType.SecureDigital, true); - - break; + goto repeatRetryLba; } + + EndProgress?.Invoke(); + } + #endregion Error handling + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + outputFormat.SetDumpHardware(_resume.Tries); + + // TODO: Drive info + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion() + }; + + if(!outputFormat.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputFormat.ErrorMessage); + + if(_preSidecar != null) + outputFormat.SetCicmMetadata(_preSidecar); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputFormat.Close(); + DateTime closeEnd = DateTime.Now; + UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); + _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + if(_metadata) + { + UpdateStatus?.Invoke("Creating sidecar."); + _dumpLog.WriteLine("Creating sidecar."); + var filters = new FiltersList(); + IFilter filter = filters.GetFilter(_outputPath); + IMediaImage inputPlugin = ImageFormat.Detect(filter) as IMediaImage; + ErrorNumber opened = inputPlugin.Open(filter); + + if(opened != ErrorNumber.NoError) + StoppingErrorMessage?.Invoke($"Error {opened} opening created image."); + + DateTime chkStart = DateTime.UtcNow; + _sidecarClass = new Sidecar(inputPlugin, _outputPath, filter.Id, _encoding); + _sidecarClass.InitProgressEvent += InitProgress; + _sidecarClass.UpdateProgressEvent += UpdateProgress; + _sidecarClass.EndProgressEvent += EndProgress; + _sidecarClass.InitProgressEvent2 += InitProgress2; + _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; + _sidecarClass.EndProgressEvent2 += EndProgress2; + _sidecarClass.UpdateStatusEvent += UpdateStatus; + CICMMetadataType sidecar = _sidecarClass.Create(); + + if(!_aborted) + { + if(_preSidecar != null) + { + _preSidecar.BlockMedia = sidecar.BlockMedia; + sidecar = _preSidecar; + } + + end = DateTime.UtcNow; + + totalChkDuration = (end - chkStart).TotalMilliseconds; + UpdateStatus?.Invoke($"Sidecar created in {(end - chkStart).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average checksum speed {blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000):F3} KiB/sec."); + + _dumpLog.WriteLine("Sidecar created in {0} seconds.", (end - chkStart).TotalSeconds); + + _dumpLog.WriteLine("Average checksum speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalChkDuration / 1000)); + + (string type, string subType) xmlType = (null, null); + + switch(_dev.Type) + { + case DeviceType.MMC: + xmlType = CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.MMC); + + sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(MediaType.MMC); + + break; + case DeviceType.SecureDigital: + CommonTypes.Metadata.MediaType.MediaTypeToString(MediaType.SecureDigital); + + sidecar.BlockMedia[0].Dimensions = + Dimensions.DimensionsFromMediaType(MediaType.SecureDigital); + + break; + } + + sidecar.BlockMedia[0].DiskType = xmlType.type; + sidecar.BlockMedia[0].DiskSubType = xmlType.subType; + + // TODO: Implement device firmware revision + sidecar.BlockMedia[0].LogicalBlocks = blocks; + sidecar.BlockMedia[0].PhysicalBlockSize = physicalBlockSize > 0 ? physicalBlockSize : blockSize; + sidecar.BlockMedia[0].LogicalBlockSize = blockSize; + sidecar.BlockMedia[0].Manufacturer = _dev.Manufacturer; + sidecar.BlockMedia[0].Model = _dev.Model; + + if(!_private) + sidecar.BlockMedia[0].Serial = _dev.Serial; + + sidecar.BlockMedia[0].Size = blocks * blockSize; + + UpdateStatus?.Invoke("Writing metadata sidecar"); + + var xmlFs = new FileStream(_outputPrefix + ".cicm.xml", FileMode.Create); + + var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); + } + } + + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + UpdateStatus?.Invoke(""); + + if(_resume.BadBlocks.Count > 0) + _resume.BadBlocks.Sort(); + + switch(_dev.Type) + { + case DeviceType.MMC: + Statistics.AddMedia(MediaType.MMC, true); + + break; + case DeviceType.SecureDigital: + Statistics.AddMedia(MediaType.SecureDigital, true); + + break; } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Dumping/XGD.cs b/Aaru.Core/Devices/Dumping/XGD.cs index 6db095c68..4d41794ce 100644 --- a/Aaru.Core/Devices/Dumping/XGD.cs +++ b/Aaru.Core/Devices/Dumping/XGD.cs @@ -53,666 +53,531 @@ using Version = Aaru.CommonTypes.Interop.Version; // ReSharper disable JoinDeclarationAndInitializer -namespace Aaru.Core.Devices.Dumping +namespace Aaru.Core.Devices.Dumping; + +/// Implements dumping an Xbox Game Disc using a Kreon drive +partial class Dump { - /// Implements dumping an Xbox Game Disc using a Kreon drive - partial class Dump + /// Dumps an Xbox Game Disc using a Kreon drive + /// Media tags as retrieved in MMC layer + /// Disc type as detected in MMC layer + void Xgd(Dictionary mediaTags, MediaType dskType) { - /// Dumps an Xbox Game Disc using a Kreon drive - /// Media tags as retrieved in MMC layer - /// Disc type as detected in MMC layer - void Xgd(Dictionary mediaTags, MediaType dskType) + bool sense; + const uint blockSize = 2048; + uint blocksToRead = 64; + DateTime start; + DateTime end; + double totalDuration = 0; + double currentSpeed = 0; + double maxSpeed = double.MinValue; + double minSpeed = double.MaxValue; + var outputFormat = _outputPlugin as IWritableImage; + + if(DetectOS.GetRealPlatformID() != PlatformID.Win32NT) { - bool sense; - const uint blockSize = 2048; - uint blocksToRead = 64; - DateTime start; - DateTime end; - double totalDuration = 0; - double currentSpeed = 0; - double maxSpeed = double.MinValue; - double minSpeed = double.MaxValue; - var outputFormat = _outputPlugin as IWritableImage; + bool isAdmin = _dev.IsRemote ? _dev.IsRemoteAdmin : DetectOS.IsAdmin; - if(DetectOS.GetRealPlatformID() != PlatformID.Win32NT) + if(!isAdmin) { - bool isAdmin = _dev.IsRemote ? _dev.IsRemoteAdmin : DetectOS.IsAdmin; + AaruConsole. + ErrorWriteLine("Because of the commands sent to a device, dumping XGD must be done with administrative privileges. Cannot continue."); - if(!isAdmin) - { - AaruConsole. - ErrorWriteLine("Because of the commands sent to a device, dumping XGD must be done with administrative privileges. Cannot continue."); - - _dumpLog.WriteLine("Cannot dump XGD without administrative privileges."); - - return; - } - } - - if(mediaTags.ContainsKey(MediaTagType.DVD_PFI)) - mediaTags.Remove(MediaTagType.DVD_PFI); - - if(mediaTags.ContainsKey(MediaTagType.DVD_DMI)) - mediaTags.Remove(MediaTagType.DVD_DMI); - - // Drive shall move to lock state when a new disc is inserted. Old kreon versions do not lock correctly so save this - sense = _dev.ReadCapacity(out byte[] coldReadCapacity, out byte[] senseBuf, _dev.Timeout, out _); - - if(sense) - { - _dumpLog.WriteLine("Cannot get disc capacity."); - StoppingErrorMessage?.Invoke("Cannot get disc capacity."); + _dumpLog.WriteLine("Cannot dump XGD without administrative privileges."); return; } + } - // Drive shall move to lock state when a new disc is inserted. Old kreon versions do not lock correctly so save this - sense = _dev.ReadDiscStructure(out byte[] coldPfi, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); + if(mediaTags.ContainsKey(MediaTagType.DVD_PFI)) + mediaTags.Remove(MediaTagType.DVD_PFI); - if(sense) - { - _dumpLog.WriteLine("Cannot get PFI."); - StoppingErrorMessage?.Invoke("Cannot get PFI."); + if(mediaTags.ContainsKey(MediaTagType.DVD_DMI)) + mediaTags.Remove(MediaTagType.DVD_DMI); - return; - } + // Drive shall move to lock state when a new disc is inserted. Old kreon versions do not lock correctly so save this + sense = _dev.ReadCapacity(out byte[] coldReadCapacity, out byte[] senseBuf, _dev.Timeout, out _); - UpdateStatus?.Invoke("Reading Xbox Security Sector."); - _dumpLog.WriteLine("Reading Xbox Security Sector."); - sense = _dev.KreonExtractSs(out byte[] ssBuf, out senseBuf, _dev.Timeout, out _); + if(sense) + { + _dumpLog.WriteLine("Cannot get disc capacity."); + StoppingErrorMessage?.Invoke("Cannot get disc capacity."); - if(sense) - { - _dumpLog.WriteLine("Cannot get Xbox Security Sector, not continuing."); - StoppingErrorMessage?.Invoke("Cannot get Xbox Security Sector, not continuing."); + return; + } - return; - } + // Drive shall move to lock state when a new disc is inserted. Old kreon versions do not lock correctly so save this + sense = _dev.ReadDiscStructure(out byte[] coldPfi, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); - _dumpLog.WriteLine("Decoding Xbox Security Sector."); - UpdateStatus?.Invoke("Decoding Xbox Security Sector."); - SS.SecuritySector? xboxSs = SS.Decode(ssBuf); + if(sense) + { + _dumpLog.WriteLine("Cannot get PFI."); + StoppingErrorMessage?.Invoke("Cannot get PFI."); - if(!xboxSs.HasValue) - { - _dumpLog.WriteLine("Cannot decode Xbox Security Sector, not continuing."); - StoppingErrorMessage?.Invoke("Cannot decode Xbox Security Sector, not continuing."); + return; + } - return; - } + UpdateStatus?.Invoke("Reading Xbox Security Sector."); + _dumpLog.WriteLine("Reading Xbox Security Sector."); + sense = _dev.KreonExtractSs(out byte[] ssBuf, out senseBuf, _dev.Timeout, out _); - byte[] tmpBuf = new byte[ssBuf.Length - 4]; - Array.Copy(ssBuf, 4, tmpBuf, 0, ssBuf.Length - 4); - mediaTags.Add(MediaTagType.Xbox_SecuritySector, tmpBuf); + if(sense) + { + _dumpLog.WriteLine("Cannot get Xbox Security Sector, not continuing."); + StoppingErrorMessage?.Invoke("Cannot get Xbox Security Sector, not continuing."); - // Get video partition size - AaruConsole.DebugWriteLine("Dump-media command", "Getting video partition size"); - UpdateStatus?.Invoke("Locking drive."); - _dumpLog.WriteLine("Locking drive."); - sense = _dev.KreonLock(out senseBuf, _dev.Timeout, out _); + return; + } - if(sense) - { - _errorLog?.WriteLine("Kreon lock", _dev.Error, _dev.LastError, senseBuf); + _dumpLog.WriteLine("Decoding Xbox Security Sector."); + UpdateStatus?.Invoke("Decoding Xbox Security Sector."); + SS.SecuritySector? xboxSs = SS.Decode(ssBuf); - _dumpLog.WriteLine("Cannot lock drive, not continuing."); - StoppingErrorMessage?.Invoke("Cannot lock drive, not continuing."); + if(!xboxSs.HasValue) + { + _dumpLog.WriteLine("Cannot decode Xbox Security Sector, not continuing."); + StoppingErrorMessage?.Invoke("Cannot decode Xbox Security Sector, not continuing."); - return; - } + return; + } - UpdateStatus?.Invoke("Getting video partition size."); - _dumpLog.WriteLine("Getting video partition size."); - sense = _dev.ReadCapacity(out byte[] readBuffer, out senseBuf, _dev.Timeout, out _); + byte[] tmpBuf = new byte[ssBuf.Length - 4]; + Array.Copy(ssBuf, 4, tmpBuf, 0, ssBuf.Length - 4); + mediaTags.Add(MediaTagType.Xbox_SecuritySector, tmpBuf); - if(sense) - { - _dumpLog.WriteLine("Cannot get disc capacity."); - StoppingErrorMessage?.Invoke("Cannot get disc capacity."); + // Get video partition size + AaruConsole.DebugWriteLine("Dump-media command", "Getting video partition size"); + UpdateStatus?.Invoke("Locking drive."); + _dumpLog.WriteLine("Locking drive."); + sense = _dev.KreonLock(out senseBuf, _dev.Timeout, out _); - return; - } + if(sense) + { + _errorLog?.WriteLine("Kreon lock", _dev.Error, _dev.LastError, senseBuf); - ulong totalSize = - (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) & - 0xFFFFFFFF; + _dumpLog.WriteLine("Cannot lock drive, not continuing."); + StoppingErrorMessage?.Invoke("Cannot lock drive, not continuing."); - UpdateStatus?.Invoke("Reading Physical Format Information."); - _dumpLog.WriteLine("Reading Physical Format Information."); + return; + } - sense = _dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); + UpdateStatus?.Invoke("Getting video partition size."); + _dumpLog.WriteLine("Getting video partition size."); + sense = _dev.ReadCapacity(out byte[] readBuffer, out senseBuf, _dev.Timeout, out _); - if(sense) - { - _dumpLog.WriteLine("Cannot get PFI."); - StoppingErrorMessage?.Invoke("Cannot get PFI."); + if(sense) + { + _dumpLog.WriteLine("Cannot get disc capacity."); + StoppingErrorMessage?.Invoke("Cannot get disc capacity."); - return; - } + return; + } - tmpBuf = new byte[readBuffer.Length - 4]; - Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); + ulong totalSize = + (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) & + 0xFFFFFFFF; + + UpdateStatus?.Invoke("Reading Physical Format Information."); + _dumpLog.WriteLine("Reading Physical Format Information."); + + sense = _dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); + + if(sense) + { + _dumpLog.WriteLine("Cannot get PFI."); + StoppingErrorMessage?.Invoke("Cannot get PFI."); + + return; + } + + tmpBuf = new byte[readBuffer.Length - 4]; + Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); + mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf); + AaruConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", totalSize); + + ulong l0Video = (PFI.Decode(readBuffer, MediaType.DVDROM)?.Layer0EndPSN ?? 0) - + (PFI.Decode(readBuffer, MediaType.DVDROM)?.DataAreaStartPSN ?? 0) + 1; + + ulong l1Video = totalSize - l0Video + 1; + UpdateStatus?.Invoke("Reading Disc Manufacturing Information."); + _dumpLog.WriteLine("Reading Disc Manufacturing Information."); + + sense = _dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _); + + if(sense) + { + _dumpLog.WriteLine("Cannot get DMI."); + StoppingErrorMessage?.Invoke("Cannot get DMI."); + + return; + } + + tmpBuf = new byte[readBuffer.Length - 4]; + Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); + mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf); + + // Should be a safe value to detect the lock command was ignored, and we're indeed getting the whole size and not the locked one + if(totalSize > 300000) + { + UpdateStatus?.Invoke("Video partition is too big, did lock work? Trying cold values."); + _dumpLog.WriteLine("Video partition is too big, did lock work? Trying cold values."); + + totalSize = (ulong)((coldReadCapacity[0] << 24) + (coldReadCapacity[1] << 16) + + (coldReadCapacity[2] << 8) + coldReadCapacity[3]) & 0xFFFFFFFF; + + tmpBuf = new byte[coldPfi.Length - 4]; + Array.Copy(coldPfi, 4, tmpBuf, 0, coldPfi.Length - 4); + mediaTags.Remove(MediaTagType.DVD_PFI); mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf); AaruConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", totalSize); - ulong l0Video = (PFI.Decode(readBuffer, MediaType.DVDROM)?.Layer0EndPSN ?? 0) - - (PFI.Decode(readBuffer, MediaType.DVDROM)?.DataAreaStartPSN ?? 0) + 1; + l0Video = (PFI.Decode(coldPfi, MediaType.DVDROM)?.Layer0EndPSN ?? 0) - + (PFI.Decode(coldPfi, MediaType.DVDROM)?.DataAreaStartPSN ?? 0) + 1; - ulong l1Video = totalSize - l0Video + 1; - UpdateStatus?.Invoke("Reading Disc Manufacturing Information."); - _dumpLog.WriteLine("Reading Disc Manufacturing Information."); + l1Video = totalSize - l0Video + 1; - sense = _dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _); - - if(sense) - { - _dumpLog.WriteLine("Cannot get DMI."); - StoppingErrorMessage?.Invoke("Cannot get DMI."); - - return; - } - - tmpBuf = new byte[readBuffer.Length - 4]; - Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); - mediaTags.Add(MediaTagType.DVD_DMI, tmpBuf); - - // Should be a safe value to detect the lock command was ignored, and we're indeed getting the whole size and not the locked one if(totalSize > 300000) { - UpdateStatus?.Invoke("Video partition is too big, did lock work? Trying cold values."); - _dumpLog.WriteLine("Video partition is too big, did lock work? Trying cold values."); + _dumpLog.WriteLine("Cannot get video partition size, not continuing. Try to eject and reinsert the drive, if it keeps happening, contact support."); - totalSize = (ulong)((coldReadCapacity[0] << 24) + (coldReadCapacity[1] << 16) + - (coldReadCapacity[2] << 8) + coldReadCapacity[3]) & 0xFFFFFFFF; - - tmpBuf = new byte[coldPfi.Length - 4]; - Array.Copy(coldPfi, 4, tmpBuf, 0, coldPfi.Length - 4); - mediaTags.Remove(MediaTagType.DVD_PFI); - mediaTags.Add(MediaTagType.DVD_PFI, tmpBuf); - AaruConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", totalSize); - - l0Video = (PFI.Decode(coldPfi, MediaType.DVDROM)?.Layer0EndPSN ?? 0) - - (PFI.Decode(coldPfi, MediaType.DVDROM)?.DataAreaStartPSN ?? 0) + 1; - - l1Video = totalSize - l0Video + 1; - - if(totalSize > 300000) - { - _dumpLog.WriteLine("Cannot get video partition size, not continuing. Try to eject and reinsert the drive, if it keeps happening, contact support."); - - StoppingErrorMessage?. - Invoke("Cannot get video partition size, not continuing. Try to eject and reinsert the drive, if it keeps happening, contact support."); - - return; - } - } - - // Get game partition size - AaruConsole.DebugWriteLine("Dump-media command", "Getting game partition size"); - UpdateStatus?.Invoke("Unlocking drive (Xtreme)."); - _dumpLog.WriteLine("Unlocking drive (Xtreme)."); - sense = _dev.KreonUnlockXtreme(out senseBuf, _dev.Timeout, out _); - - if(sense) - { - _errorLog?.WriteLine("Kreon Xtreme unlock", _dev.Error, _dev.LastError, senseBuf); - _dumpLog.WriteLine("Cannot unlock drive, not continuing."); - StoppingErrorMessage?.Invoke("Cannot unlock drive, not continuing."); + StoppingErrorMessage?. + Invoke("Cannot get video partition size, not continuing. Try to eject and reinsert the drive, if it keeps happening, contact support."); return; } + } - UpdateStatus?.Invoke("Getting game partition size."); - _dumpLog.WriteLine("Getting game partition size."); - sense = _dev.ReadCapacity(out readBuffer, out senseBuf, _dev.Timeout, out _); + // Get game partition size + AaruConsole.DebugWriteLine("Dump-media command", "Getting game partition size"); + UpdateStatus?.Invoke("Unlocking drive (Xtreme)."); + _dumpLog.WriteLine("Unlocking drive (Xtreme)."); + sense = _dev.KreonUnlockXtreme(out senseBuf, _dev.Timeout, out _); - if(sense) + if(sense) + { + _errorLog?.WriteLine("Kreon Xtreme unlock", _dev.Error, _dev.LastError, senseBuf); + _dumpLog.WriteLine("Cannot unlock drive, not continuing."); + StoppingErrorMessage?.Invoke("Cannot unlock drive, not continuing."); + + return; + } + + UpdateStatus?.Invoke("Getting game partition size."); + _dumpLog.WriteLine("Getting game partition size."); + sense = _dev.ReadCapacity(out readBuffer, out senseBuf, _dev.Timeout, out _); + + if(sense) + { + _dumpLog.WriteLine("Cannot get disc capacity."); + StoppingErrorMessage?.Invoke("Cannot get disc capacity."); + + return; + } + + ulong gameSize = + ((ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) & + 0xFFFFFFFF) + 1; + + AaruConsole.DebugWriteLine("Dump-media command", "Game partition total size: {0} sectors", gameSize); + + // Get middle zone size + AaruConsole.DebugWriteLine("Dump-media command", "Getting middle zone size"); + UpdateStatus?.Invoke("Unlocking drive (Wxripper)."); + _dumpLog.WriteLine("Unlocking drive (Wxripper)."); + sense = _dev.KreonUnlockWxripper(out senseBuf, _dev.Timeout, out _); + + if(sense) + { + _errorLog?.WriteLine("Kreon Wxripper unlock", _dev.Error, _dev.LastError, senseBuf); + _dumpLog.WriteLine("Cannot unlock drive, not continuing."); + StoppingErrorMessage?.Invoke("Cannot unlock drive, not continuing."); + + return; + } + + UpdateStatus?.Invoke("Getting disc size."); + _dumpLog.WriteLine("Getting disc size."); + sense = _dev.ReadCapacity(out readBuffer, out senseBuf, _dev.Timeout, out _); + + if(sense) + { + _dumpLog.WriteLine("Cannot get disc capacity."); + StoppingErrorMessage?.Invoke("Cannot get disc capacity."); + + return; + } + + totalSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) & + 0xFFFFFFFF; + + UpdateStatus?.Invoke("Reading Physical Format Information."); + _dumpLog.WriteLine("Reading Physical Format Information."); + + sense = _dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); + + if(sense) + { + _dumpLog.WriteLine("Cannot get PFI."); + StoppingErrorMessage?.Invoke("Cannot get PFI."); + + return; + } + + AaruConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", totalSize); + ulong blocks = totalSize + 1; + + PFI.PhysicalFormatInformation? wxRipperPfiNullable = PFI.Decode(readBuffer, MediaType.DVDROM); + + if(wxRipperPfiNullable == null) + { + _dumpLog.WriteLine("Cannot decode PFI."); + StoppingErrorMessage?.Invoke("Cannot decode PFI."); + + return; + } + + PFI.PhysicalFormatInformation wxRipperPfi = wxRipperPfiNullable.Value; + + UpdateStatus?.Invoke($"WxRipper PFI's Data Area Start PSN: {wxRipperPfi.DataAreaStartPSN} sectors"); + UpdateStatus?.Invoke($"WxRipper PFI's Layer 0 End PSN: {wxRipperPfi.Layer0EndPSN} sectors"); + _dumpLog.WriteLine($"WxRipper PFI's Data Area Start PSN: {wxRipperPfi.DataAreaStartPSN} sectors"); + _dumpLog.WriteLine($"WxRipper PFI's Layer 0 End PSN: {wxRipperPfi.Layer0EndPSN} sectors"); + + ulong middleZone = totalSize - (wxRipperPfi.Layer0EndPSN - wxRipperPfi.DataAreaStartPSN + 1) - gameSize + 1; + + tmpBuf = new byte[readBuffer.Length - 4]; + Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); + mediaTags.Add(MediaTagType.Xbox_PFI, tmpBuf); + + UpdateStatus?.Invoke("Reading Disc Manufacturing Information."); + _dumpLog.WriteLine("Reading Disc Manufacturing Information."); + + sense = _dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _); + + if(sense) + { + _dumpLog.WriteLine("Cannot get DMI."); + StoppingErrorMessage?.Invoke("Cannot get DMI."); + + return; + } + + tmpBuf = new byte[readBuffer.Length - 4]; + Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); + mediaTags.Add(MediaTagType.Xbox_DMI, tmpBuf); + + totalSize = l0Video + l1Video + (middleZone * 2) + gameSize; + ulong layerBreak = l0Video + middleZone + (gameSize / 2); + + UpdateStatus?.Invoke($"Video layer 0 size: {l0Video} sectors"); + UpdateStatus?.Invoke($"Video layer 1 size: {l1Video} sectors"); + UpdateStatus?.Invoke($"Middle zone size: {middleZone} sectors"); + UpdateStatus?.Invoke($"Game data size: {gameSize} sectors"); + UpdateStatus?.Invoke($"Total size: {totalSize} sectors"); + UpdateStatus?.Invoke($"Real layer break: {layerBreak}"); + UpdateStatus?.Invoke(""); + + _dumpLog.WriteLine("Video layer 0 size: {0} sectors", l0Video); + _dumpLog.WriteLine("Video layer 1 size: {0} sectors", l1Video); + _dumpLog.WriteLine("Middle zone 0 size: {0} sectors", middleZone); + _dumpLog.WriteLine("Game data 0 size: {0} sectors", gameSize); + _dumpLog.WriteLine("Total 0 size: {0} sectors", totalSize); + _dumpLog.WriteLine("Real layer break: {0}", layerBreak); + + bool read12 = !_dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, 0, blockSize, 0, 1, + false, _dev.Timeout, out _); + + if(!read12) + { + _dumpLog.WriteLine("Cannot read medium, aborting scan..."); + StoppingErrorMessage?.Invoke("Cannot read medium, aborting scan..."); + + return; + } + + _dumpLog.WriteLine("Using SCSI READ (12) command."); + UpdateStatus?.Invoke("Using SCSI READ (12) command."); + + // Set speed + if(_speedMultiplier >= 0) + { + _dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX." : $"{_speed}x")}."); + UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX." : $"{_speed}x")}."); + + _speed *= _speedMultiplier; + + if(_speed == 0 || + _speed > 0xFFFF) + _speed = 0xFFFF; + + _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _); + } + + while(true) + { + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, 0, blockSize, 0, + blocksToRead, false, _dev.Timeout, out _); + + if(sense || _dev.Error) + blocksToRead /= 2; + + if(!_dev.Error || + blocksToRead == 1) + break; + } + + if(_dev.Error) + { + _dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", _dev.LastError); + StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + + return; + } + + if(_skip < blocksToRead) + _skip = blocksToRead; + + bool ret = true; + + foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputFormat.SupportedMediaTags.Contains(tag))) + { + ret = false; + _dumpLog.WriteLine($"Output format does not support {tag}."); + ErrorMessage?.Invoke($"Output format does not support {tag}."); + } + + if(!ret) + { + if(_force) { - _dumpLog.WriteLine("Cannot get disc capacity."); - StoppingErrorMessage?.Invoke("Cannot get disc capacity."); + _dumpLog.WriteLine("Several media tags not supported, continuing..."); + ErrorMessage?.Invoke("Several media tags not supported, continuing..."); + } + else + { + _dumpLog.WriteLine("Several media tags not supported, not continuing..."); + StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); return; } + } - ulong gameSize = - ((ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) & - 0xFFFFFFFF) + 1; + _dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); - AaruConsole.DebugWriteLine("Dump-media command", "Game partition total size: {0} sectors", gameSize); + var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); + var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0010); + ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); - // Get middle zone size - AaruConsole.DebugWriteLine("Dump-media command", "Getting middle zone size"); - UpdateStatus?.Invoke("Unlocking drive (Wxripper)."); - _dumpLog.WriteLine("Unlocking drive (Wxripper)."); - sense = _dev.KreonUnlockWxripper(out senseBuf, _dev.Timeout, out _); + // Cannot create image + if(!ret) + { + _dumpLog.WriteLine("Error creating output image, not continuing."); + _dumpLog.WriteLine(outputFormat.ErrorMessage); - if(sense) + StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; + } + + start = DateTime.UtcNow; + double imageWriteDuration = 0; + + double cmdDuration = 0; + uint saveBlocksToRead = blocksToRead; + DumpHardwareType currentTry = null; + ExtentsULong extents = null; + + ResumeSupport.Process(true, true, totalSize, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, + ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private, _force); + + if(currentTry == null || + extents == null) + StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); + + (outputFormat as IWritableOpticalImage).SetTracks(new List + { + new Track { - _errorLog?.WriteLine("Kreon Wxripper unlock", _dev.Error, _dev.LastError, senseBuf); - _dumpLog.WriteLine("Cannot unlock drive, not continuing."); - StoppingErrorMessage?.Invoke("Cannot unlock drive, not continuing."); + BytesPerSector = (int)blockSize, + EndSector = blocks - 1, + Sequence = 1, + RawBytesPerSector = (int)blockSize, + SubchannelType = TrackSubchannelType.None, + Session = 1, + Type = TrackType.Data + } + }); - return; + ulong currentSector = _resume.NextBlock; + + if(_resume.NextBlock > 0) + { + UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); + _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); + } + + bool newTrim = false; + + _dumpLog.WriteLine("Reading game partition."); + UpdateStatus?.Invoke("Reading game partition."); + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(int e = 0; e <= 16; e++) + { + if(_aborted) + { + _resume.NextBlock = currentSector; + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; } - UpdateStatus?.Invoke("Getting disc size."); - _dumpLog.WriteLine("Getting disc size."); - sense = _dev.ReadCapacity(out readBuffer, out senseBuf, _dev.Timeout, out _); + if(currentSector >= blocks) + break; - if(sense) + ulong extentStart, extentEnd; + + // Extents + if(e < 16) { - _dumpLog.WriteLine("Cannot get disc capacity."); - StoppingErrorMessage?.Invoke("Cannot get disc capacity."); - - return; - } - - totalSize = (ulong)((readBuffer[0] << 24) + (readBuffer[1] << 16) + (readBuffer[2] << 8) + readBuffer[3]) & - 0xFFFFFFFF; - - UpdateStatus?.Invoke("Reading Physical Format Information."); - _dumpLog.WriteLine("Reading Physical Format Information."); - - sense = _dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); - - if(sense) - { - _dumpLog.WriteLine("Cannot get PFI."); - StoppingErrorMessage?.Invoke("Cannot get PFI."); - - return; - } - - AaruConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", totalSize); - ulong blocks = totalSize + 1; - - PFI.PhysicalFormatInformation? wxRipperPfiNullable = PFI.Decode(readBuffer, MediaType.DVDROM); - - if(wxRipperPfiNullable == null) - { - _dumpLog.WriteLine("Cannot decode PFI."); - StoppingErrorMessage?.Invoke("Cannot decode PFI."); - - return; - } - - PFI.PhysicalFormatInformation wxRipperPfi = wxRipperPfiNullable.Value; - - UpdateStatus?.Invoke($"WxRipper PFI's Data Area Start PSN: {wxRipperPfi.DataAreaStartPSN} sectors"); - UpdateStatus?.Invoke($"WxRipper PFI's Layer 0 End PSN: {wxRipperPfi.Layer0EndPSN} sectors"); - _dumpLog.WriteLine($"WxRipper PFI's Data Area Start PSN: {wxRipperPfi.DataAreaStartPSN} sectors"); - _dumpLog.WriteLine($"WxRipper PFI's Layer 0 End PSN: {wxRipperPfi.Layer0EndPSN} sectors"); - - ulong middleZone = totalSize - (wxRipperPfi.Layer0EndPSN - wxRipperPfi.DataAreaStartPSN + 1) - gameSize + 1; - - tmpBuf = new byte[readBuffer.Length - 4]; - Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); - mediaTags.Add(MediaTagType.Xbox_PFI, tmpBuf); - - UpdateStatus?.Invoke("Reading Disc Manufacturing Information."); - _dumpLog.WriteLine("Reading Disc Manufacturing Information."); - - sense = _dev.ReadDiscStructure(out readBuffer, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DiscManufacturingInformation, 0, 0, out _); - - if(sense) - { - _dumpLog.WriteLine("Cannot get DMI."); - StoppingErrorMessage?.Invoke("Cannot get DMI."); - - return; - } - - tmpBuf = new byte[readBuffer.Length - 4]; - Array.Copy(readBuffer, 4, tmpBuf, 0, readBuffer.Length - 4); - mediaTags.Add(MediaTagType.Xbox_DMI, tmpBuf); - - totalSize = l0Video + l1Video + (middleZone * 2) + gameSize; - ulong layerBreak = l0Video + middleZone + (gameSize / 2); - - UpdateStatus?.Invoke($"Video layer 0 size: {l0Video} sectors"); - UpdateStatus?.Invoke($"Video layer 1 size: {l1Video} sectors"); - UpdateStatus?.Invoke($"Middle zone size: {middleZone} sectors"); - UpdateStatus?.Invoke($"Game data size: {gameSize} sectors"); - UpdateStatus?.Invoke($"Total size: {totalSize} sectors"); - UpdateStatus?.Invoke($"Real layer break: {layerBreak}"); - UpdateStatus?.Invoke(""); - - _dumpLog.WriteLine("Video layer 0 size: {0} sectors", l0Video); - _dumpLog.WriteLine("Video layer 1 size: {0} sectors", l1Video); - _dumpLog.WriteLine("Middle zone 0 size: {0} sectors", middleZone); - _dumpLog.WriteLine("Game data 0 size: {0} sectors", gameSize); - _dumpLog.WriteLine("Total 0 size: {0} sectors", totalSize); - _dumpLog.WriteLine("Real layer break: {0}", layerBreak); - - bool read12 = !_dev.Read12(out readBuffer, out senseBuf, 0, false, true, false, false, 0, blockSize, 0, 1, - false, _dev.Timeout, out _); - - if(!read12) - { - _dumpLog.WriteLine("Cannot read medium, aborting scan..."); - StoppingErrorMessage?.Invoke("Cannot read medium, aborting scan..."); - - return; - } - - _dumpLog.WriteLine("Using SCSI READ (12) command."); - UpdateStatus?.Invoke("Using SCSI READ (12) command."); - - // Set speed - if(_speedMultiplier >= 0) - { - _dumpLog.WriteLine($"Setting speed to {(_speed == 0 ? "MAX." : $"{_speed}x")}."); - UpdateStatus?.Invoke($"Setting speed to {(_speed == 0 ? "MAX." : $"{_speed}x")}."); - - _speed *= _speedMultiplier; - - if(_speed == 0 || - _speed > 0xFFFF) - _speed = 0xFFFF; - - _dev.SetCdSpeed(out _, RotationalControl.ClvAndImpureCav, (ushort)_speed, 0, _dev.Timeout, out _); - } - - while(true) - { - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, 0, blockSize, 0, - blocksToRead, false, _dev.Timeout, out _); - - if(sense || _dev.Error) - blocksToRead /= 2; - - if(!_dev.Error || - blocksToRead == 1) - break; - } - - if(_dev.Error) - { - _dumpLog.WriteLine("Device error {0} trying to guess ideal transfer length.", _dev.LastError); - StoppingErrorMessage?.Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); - - return; - } - - if(_skip < blocksToRead) - _skip = blocksToRead; - - bool ret = true; - - foreach(MediaTagType tag in mediaTags.Keys.Where(tag => !outputFormat.SupportedMediaTags.Contains(tag))) - { - ret = false; - _dumpLog.WriteLine($"Output format does not support {tag}."); - ErrorMessage?.Invoke($"Output format does not support {tag}."); - } - - if(!ret) - { - if(_force) - { - _dumpLog.WriteLine("Several media tags not supported, continuing..."); - ErrorMessage?.Invoke("Several media tags not supported, continuing..."); - } + if(xboxSs.Value.Extents[e].StartPSN <= xboxSs.Value.Layer0EndPSN) + extentStart = xboxSs.Value.Extents[e].StartPSN - 0x30000; else - { - _dumpLog.WriteLine("Several media tags not supported, not continuing..."); - StoppingErrorMessage?.Invoke("Several media tags not supported, not continuing..."); + extentStart = ((xboxSs.Value.Layer0EndPSN + 1) * 2) - + ((xboxSs.Value.Extents[e].StartPSN ^ 0xFFFFFF) + 1) - 0x30000; - return; - } - } - - _dumpLog.WriteLine("Reading {0} sectors at a time.", blocksToRead); - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); - - var mhddLog = new MhddLog(_outputPrefix + ".mhddlog.bin", _dev, blocks, blockSize, blocksToRead, _private); - var ibgLog = new IbgLog(_outputPrefix + ".ibg", 0x0010); - ret = outputFormat.Create(_outputPath, dskType, _formatOptions, blocks, blockSize); - - // Cannot create image - if(!ret) - { - _dumpLog.WriteLine("Error creating output image, not continuing."); - _dumpLog.WriteLine(outputFormat.ErrorMessage); - - StoppingErrorMessage?.Invoke("Error creating output image, not continuing." + Environment.NewLine + - outputFormat.ErrorMessage); - - return; - } - - start = DateTime.UtcNow; - double imageWriteDuration = 0; - - double cmdDuration = 0; - uint saveBlocksToRead = blocksToRead; - DumpHardwareType currentTry = null; - ExtentsULong extents = null; - - ResumeSupport.Process(true, true, totalSize, _dev.Manufacturer, _dev.Model, _dev.Serial, _dev.PlatformId, - ref _resume, ref currentTry, ref extents, _dev.FirmwareRevision, _private, _force); - - if(currentTry == null || - extents == null) - StoppingErrorMessage?.Invoke("Could not process resume file, not continuing..."); - - (outputFormat as IWritableOpticalImage).SetTracks(new List - { - new Track - { - BytesPerSector = (int)blockSize, - EndSector = blocks - 1, - Sequence = 1, - RawBytesPerSector = (int)blockSize, - SubchannelType = TrackSubchannelType.None, - Session = 1, - Type = TrackType.Data - } - }); - - ulong currentSector = _resume.NextBlock; - - if(_resume.NextBlock > 0) - { - UpdateStatus?.Invoke($"Resuming from block {_resume.NextBlock}."); - _dumpLog.WriteLine("Resuming from block {0}.", _resume.NextBlock); - } - - bool newTrim = false; - - _dumpLog.WriteLine("Reading game partition."); - UpdateStatus?.Invoke("Reading game partition."); - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; - InitProgress?.Invoke(); - - for(int e = 0; e <= 16; e++) - { - if(_aborted) - { - _resume.NextBlock = currentSector; - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - if(currentSector >= blocks) - break; - - ulong extentStart, extentEnd; - - // Extents - if(e < 16) - { - if(xboxSs.Value.Extents[e].StartPSN <= xboxSs.Value.Layer0EndPSN) - extentStart = xboxSs.Value.Extents[e].StartPSN - 0x30000; - else - extentStart = ((xboxSs.Value.Layer0EndPSN + 1) * 2) - - ((xboxSs.Value.Extents[e].StartPSN ^ 0xFFFFFF) + 1) - 0x30000; - - if(xboxSs.Value.Extents[e].EndPSN <= xboxSs.Value.Layer0EndPSN) - extentEnd = xboxSs.Value.Extents[e].EndPSN - 0x30000; - else - extentEnd = ((xboxSs.Value.Layer0EndPSN + 1) * 2) - - ((xboxSs.Value.Extents[e].EndPSN ^ 0xFFFFFF) + 1) - 0x30000; - } - - // After last extent + if(xboxSs.Value.Extents[e].EndPSN <= xboxSs.Value.Layer0EndPSN) + extentEnd = xboxSs.Value.Extents[e].EndPSN - 0x30000; else - { - extentStart = blocks; - extentEnd = blocks; - } - - if(currentSector > extentEnd) - continue; - - for(ulong i = currentSector; i < extentStart; i += blocksToRead) - { - saveBlocksToRead = blocksToRead; - - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - if(extentStart - i < blocksToRead) - blocksToRead = (uint)(extentStart - i); - - if(currentSpeed > maxSpeed && - currentSpeed > 0) - maxSpeed = currentSpeed; - - if(currentSpeed < minSpeed && - currentSpeed > 0) - minSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {totalSize} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)totalSize); - - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)i, blockSize, - 0, blocksToRead, false, _dev.Timeout, out cmdDuration); - - totalDuration += cmdDuration; - - if(!sense && - !_dev.Error) - { - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(readBuffer, i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(i, blocksToRead, true); - } - else - { - _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); - - // TODO: Reset device after X errors - if(_stopOnError) - return; // TODO: Return more cleanly - - if(i + _skip > blocks) - _skip = (uint)(blocks - i); - - // Write empty data - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - - for(ulong b = i; b < i + _skip; b++) - _resume.BadBlocks.Add(b); - - AaruConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); - i += _skip - blocksToRead; - - string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] - { - Environment.NewLine - }, StringSplitOptions.RemoveEmptyEntries); - - foreach(string senseLine in senseLines) - _dumpLog.WriteLine(senseLine); - - newTrim = true; - } - - blocksToRead = saveBlocksToRead; - currentSector = i + 1; - _resume.NextBlock = currentSector; - sectorSpeedStart += blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - for(ulong i = extentStart; i <= extentEnd; i += blocksToRead) - { - saveBlocksToRead = blocksToRead; - - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - if(extentEnd - i < blocksToRead) - blocksToRead = (uint)(extentEnd - i) + 1; - - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - - // Write empty data - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * blocksToRead], i, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - blocksToRead = saveBlocksToRead; - extents.Add(i, blocksToRead, true); - currentSector = i + 1; - _resume.NextBlock = currentSector; - } - - if(!_aborted) - currentSector = extentEnd + 1; + extentEnd = ((xboxSs.Value.Layer0EndPSN + 1) * 2) - + ((xboxSs.Value.Extents[e].EndPSN ^ 0xFFFFFF) + 1) - 0x30000; } - _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); - - EndProgress?.Invoke(); - - // Middle Zone D - UpdateStatus?.Invoke("Writing Middle Zone D (empty)."); - _dumpLog.WriteLine("Writing Middle Zone D (empty)."); - InitProgress?.Invoke(); - - for(ulong middle = currentSector - blocks - 1; middle < middleZone - 1; middle += blocksToRead) + // After last extent + else { + extentStart = blocks; + extentEnd = blocks; + } + + if(currentSector > extentEnd) + continue; + + for(ulong i = currentSector; i < extentStart; i += blocksToRead) + { + saveBlocksToRead = blocksToRead; + if(_aborted) { currentTry.Extents = ExtentsConverter.ToMetadata(extents); @@ -722,69 +587,8 @@ namespace Aaru.Core.Devices.Dumping break; } - if(middleZone - 1 - middle < blocksToRead) - blocksToRead = (uint)(middleZone - 1 - middle); - - UpdateProgress?. - Invoke($"Reading sector {middle + currentSector} of {totalSize} ({currentSpeed:F3} MiB/sec.)", - (long)(middle + currentSector), (long)totalSize); - - mhddLog.Write(middle + currentSector, cmdDuration); - ibgLog.Write(middle + currentSector, currentSpeed * 1024); - - // Write empty data - DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * blocksToRead], middle + currentSector, blocksToRead); - imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(currentSector, blocksToRead, true); - - currentSector += blocksToRead; - _resume.NextBlock = currentSector; - } - - EndProgress?.Invoke(); - - blocksToRead = saveBlocksToRead; - - UpdateStatus?.Invoke("Locking drive."); - _dumpLog.WriteLine("Locking drive."); - sense = _dev.KreonLock(out senseBuf, _dev.Timeout, out _); - - if(sense) - { - _dumpLog.WriteLine("Cannot lock drive, not continuing."); - StoppingErrorMessage?.Invoke("Cannot lock drive, not continuing."); - - return; - } - - sense = _dev.ReadCapacity(out readBuffer, out senseBuf, _dev.Timeout, out _); - - if(sense) - { - StoppingErrorMessage?.Invoke("Cannot get disc capacity."); - - return; - } - - // Video Layer 1 - _dumpLog.WriteLine("Reading Video Layer 1."); - UpdateStatus?.Invoke("Reading Video Layer 1."); - InitProgress?.Invoke(); - - for(ulong l1 = currentSector - blocks - middleZone + l0Video; l1 < l0Video + l1Video; l1 += blocksToRead) - { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); - - break; - } - - if(l0Video + l1Video - l1 < blocksToRead) - blocksToRead = (uint)(l0Video + l1Video - l1); + if(extentStart - i < blocksToRead) + blocksToRead = (uint)(extentStart - i); if(currentSpeed > maxSpeed && currentSpeed > 0) @@ -794,46 +598,50 @@ namespace Aaru.Core.Devices.Dumping currentSpeed > 0) minSpeed = currentSpeed; - UpdateProgress?.Invoke($"Reading sector {currentSector} of {totalSize} ({currentSpeed:F3} MiB/sec.)", - (long)currentSector, (long)totalSize); + UpdateProgress?.Invoke($"Reading sector {i} of {totalSize} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)totalSize); - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)l1, blockSize, 0, - blocksToRead, false, _dev.Timeout, out cmdDuration); + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)i, blockSize, + 0, blocksToRead, false, _dev.Timeout, out cmdDuration); totalDuration += cmdDuration; if(!sense && !_dev.Error) { - mhddLog.Write(currentSector, cmdDuration); - ibgLog.Write(currentSector, currentSpeed * 1024); + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(readBuffer, currentSector, blocksToRead); + outputFormat.WriteSectors(readBuffer, i, blocksToRead); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - extents.Add(currentSector, blocksToRead, true); + extents.Add(i, blocksToRead, true); } else { - _errorLog?.WriteLine(currentSector, _dev.Error, _dev.LastError, senseBuf); + _errorLog?.WriteLine(i, _dev.Error, _dev.LastError, senseBuf); // TODO: Reset device after X errors if(_stopOnError) return; // TODO: Return more cleanly + if(i + _skip > blocks) + _skip = (uint)(blocks - i); + // Write empty data DateTime writeStart = DateTime.Now; - outputFormat.WriteSectors(new byte[blockSize * _skip], currentSector, _skip); + outputFormat.WriteSectors(new byte[blockSize * _skip], i, _skip); imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; - // TODO: Handle errors in video partition - //errored += blocksToRead; - //resume.BadBlocks.Add(l1); - AaruConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); - mhddLog.Write(l1, cmdDuration < 500 ? 65535 : cmdDuration); + for(ulong b = i; b < i + _skip; b++) + _resume.BadBlocks.Add(b); - ibgLog.Write(l1, 0); - _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, l1); - l1 += _skip - blocksToRead; + AaruConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(i, 0); + + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, i); + i += _skip - blocksToRead; string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] { @@ -842,9 +650,12 @@ namespace Aaru.Core.Devices.Dumping foreach(string senseLine in senseLines) _dumpLog.WriteLine(senseLine); + + newTrim = true; } - currentSector += blocksToRead; + blocksToRead = saveBlocksToRead; + currentSector = i + 1; _resume.NextBlock = currentSector; sectorSpeedStart += blocksToRead; @@ -858,402 +669,590 @@ namespace Aaru.Core.Devices.Dumping timeSpeedStart = DateTime.UtcNow; } - EndProgress?.Invoke(); - - UpdateStatus?.Invoke("Unlocking drive (Wxripper)."); - _dumpLog.WriteLine("Unlocking drive (Wxripper)."); - sense = _dev.KreonUnlockWxripper(out senseBuf, _dev.Timeout, out _); - - if(sense) + for(ulong i = extentStart; i <= extentEnd; i += blocksToRead) { - _errorLog?.WriteLine("Kreon Wxripper unlock", _dev.Error, _dev.LastError, senseBuf); - _dumpLog.WriteLine("Cannot unlock drive, not continuing."); - StoppingErrorMessage?.Invoke("Cannot unlock drive, not continuing."); + saveBlocksToRead = blocksToRead; - return; - } - - sense = _dev.ReadCapacity(out readBuffer, out senseBuf, _dev.Timeout, out _); - - if(sense) - { - StoppingErrorMessage?.Invoke("Cannot get disc capacity."); - - return; - } - - end = DateTime.UtcNow; - AaruConsole.WriteLine(); - mhddLog.Close(); - - ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); - - UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); - - UpdateStatus?. - Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); - - UpdateStatus?. - Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); - - _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); - - _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); - - _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", - blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); - - #region Trimming - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _trim && - newTrim) - { - start = DateTime.UtcNow; - UpdateStatus?.Invoke("Trimming skipped sectors"); - _dumpLog.WriteLine("Trimming skipped sectors"); - - ulong[] tmpArray = _resume.BadBlocks.ToArray(); - InitProgress?.Invoke(); - - foreach(ulong badSector in tmpArray) + if(_aborted) { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - _dumpLog.WriteLine("Aborted!"); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - break; - } - - PulseProgress?.Invoke($"Trimming sector {badSector}"); - - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector, - blockSize, 0, 1, false, _dev.Timeout, out cmdDuration); - - totalDuration += cmdDuration; - - if(sense || _dev.Error) - { - _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); - - continue; - } - - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(readBuffer, badSector); + break; } - EndProgress?.Invoke(); - end = DateTime.UtcNow; - UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); - _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + if(extentEnd - i < blocksToRead) + blocksToRead = (uint)(extentEnd - i) + 1; + + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + + // Write empty data + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * blocksToRead], i, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + blocksToRead = saveBlocksToRead; + extents.Add(i, blocksToRead, true); + currentSector = i + 1; + _resume.NextBlock = currentSector; } - #endregion Trimming - #region Error handling - if(_resume.BadBlocks.Count > 0 && - !_aborted && - _retryPasses > 0) + if(!_aborted) + currentSector = extentEnd + 1; + } + + _resume.BadBlocks = _resume.BadBlocks.Distinct().ToList(); + + EndProgress?.Invoke(); + + // Middle Zone D + UpdateStatus?.Invoke("Writing Middle Zone D (empty)."); + _dumpLog.WriteLine("Writing Middle Zone D (empty)."); + InitProgress?.Invoke(); + + for(ulong middle = currentSector - blocks - 1; middle < middleZone - 1; middle += blocksToRead) + { + if(_aborted) { - List tmpList = new List(); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); - foreach(ulong ur in _resume.BadBlocks) - for(ulong i = ur; i < ur + blocksToRead; i++) - tmpList.Add(i); + break; + } - tmpList.Sort(); + if(middleZone - 1 - middle < blocksToRead) + blocksToRead = (uint)(middleZone - 1 - middle); - int pass = 1; - bool forward = true; - bool runningPersistent = false; + UpdateProgress?. + Invoke($"Reading sector {middle + currentSector} of {totalSize} ({currentSpeed:F3} MiB/sec.)", + (long)(middle + currentSector), (long)totalSize); - _resume.BadBlocks = tmpList; - Modes.ModePage? currentModePage = null; - byte[] md6; - byte[] md10; + mhddLog.Write(middle + currentSector, cmdDuration); + ibgLog.Write(middle + currentSector, currentSpeed * 1024); - if(_persistent) + // Write empty data + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * blocksToRead], middle + currentSector, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(currentSector, blocksToRead, true); + + currentSector += blocksToRead; + _resume.NextBlock = currentSector; + } + + EndProgress?.Invoke(); + + blocksToRead = saveBlocksToRead; + + UpdateStatus?.Invoke("Locking drive."); + _dumpLog.WriteLine("Locking drive."); + sense = _dev.KreonLock(out senseBuf, _dev.Timeout, out _); + + if(sense) + { + _dumpLog.WriteLine("Cannot lock drive, not continuing."); + StoppingErrorMessage?.Invoke("Cannot lock drive, not continuing."); + + return; + } + + sense = _dev.ReadCapacity(out readBuffer, out senseBuf, _dev.Timeout, out _); + + if(sense) + { + StoppingErrorMessage?.Invoke("Cannot get disc capacity."); + + return; + } + + // Video Layer 1 + _dumpLog.WriteLine("Reading Video Layer 1."); + UpdateStatus?.Invoke("Reading Video Layer 1."); + InitProgress?.Invoke(); + + for(ulong l1 = currentSector - blocks - middleZone + l0Video; l1 < l0Video + l1Video; l1 += blocksToRead) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + if(l0Video + l1Video - l1 < blocksToRead) + blocksToRead = (uint)(l0Video + l1Video - l1); + + if(currentSpeed > maxSpeed && + currentSpeed > 0) + maxSpeed = currentSpeed; + + if(currentSpeed < minSpeed && + currentSpeed > 0) + minSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {currentSector} of {totalSize} ({currentSpeed:F3} MiB/sec.)", + (long)currentSector, (long)totalSize); + + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)l1, blockSize, 0, + blocksToRead, false, _dev.Timeout, out cmdDuration); + + totalDuration += cmdDuration; + + if(!sense && + !_dev.Error) + { + mhddLog.Write(currentSector, cmdDuration); + ibgLog.Write(currentSector, currentSpeed * 1024); + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(readBuffer, currentSector, blocksToRead); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + extents.Add(currentSector, blocksToRead, true); + } + else + { + _errorLog?.WriteLine(currentSector, _dev.Error, _dev.LastError, senseBuf); + + // TODO: Reset device after X errors + if(_stopOnError) + return; // TODO: Return more cleanly + + // Write empty data + DateTime writeStart = DateTime.Now; + outputFormat.WriteSectors(new byte[blockSize * _skip], currentSector, _skip); + imageWriteDuration += (DateTime.Now - writeStart).TotalSeconds; + + // TODO: Handle errors in video partition + //errored += blocksToRead; + //resume.BadBlocks.Add(l1); + AaruConsole.DebugWriteLine("Dump-Media", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); + mhddLog.Write(l1, cmdDuration < 500 ? 65535 : cmdDuration); + + ibgLog.Write(l1, 0); + _dumpLog.WriteLine("Skipping {0} blocks from errored block {1}.", _skip, l1); + l1 += _skip - blocksToRead; + + string[] senseLines = Sense.PrettifySense(senseBuf).Split(new[] { - Modes.ModePage_01_MMC pgMmc; + Environment.NewLine + }, StringSplitOptions.RemoveEmptyEntries); - sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - _dev.Timeout, out _); + foreach(string senseLine in senseLines) + _dumpLog.WriteLine(senseLine); + } - if(sense) + currentSector += blocksToRead; + _resume.NextBlock = currentSector; + sectorSpeedStart += blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + + EndProgress?.Invoke(); + + UpdateStatus?.Invoke("Unlocking drive (Wxripper)."); + _dumpLog.WriteLine("Unlocking drive (Wxripper)."); + sense = _dev.KreonUnlockWxripper(out senseBuf, _dev.Timeout, out _); + + if(sense) + { + _errorLog?.WriteLine("Kreon Wxripper unlock", _dev.Error, _dev.LastError, senseBuf); + _dumpLog.WriteLine("Cannot unlock drive, not continuing."); + StoppingErrorMessage?.Invoke("Cannot unlock drive, not continuing."); + + return; + } + + sense = _dev.ReadCapacity(out readBuffer, out senseBuf, _dev.Timeout, out _); + + if(sense) + { + StoppingErrorMessage?.Invoke("Cannot get disc capacity."); + + return; + } + + end = DateTime.UtcNow; + AaruConsole.WriteLine(); + mhddLog.Close(); + + ibgLog.Close(_dev, blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000), _devicePath); + + UpdateStatus?.Invoke($"Dump finished in {(end - start).TotalSeconds} seconds."); + + UpdateStatus?. + Invoke($"Average dump speed {blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000):F3} KiB/sec."); + + UpdateStatus?. + Invoke($"Average write speed {blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration:F3} KiB/sec."); + + _dumpLog.WriteLine("Dump finished in {0} seconds.", (end - start).TotalSeconds); + + _dumpLog.WriteLine("Average dump speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / (totalDuration / 1000)); + + _dumpLog.WriteLine("Average write speed {0:F3} KiB/sec.", + blockSize * (double)(blocks + 1) / 1024 / imageWriteDuration); + + #region Trimming + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _trim && + newTrim) + { + start = DateTime.UtcNow; + UpdateStatus?.Invoke("Trimming skipped sectors"); + _dumpLog.WriteLine("Trimming skipped sectors"); + + ulong[] tmpArray = _resume.BadBlocks.ToArray(); + InitProgress?.Invoke(); + + foreach(ulong badSector in tmpArray) + { + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + PulseProgress?.Invoke($"Trimming sector {badSector}"); + + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector, + blockSize, 0, 1, false, _dev.Timeout, out cmdDuration); + + totalDuration += cmdDuration; + + if(sense || _dev.Error) + { + _errorLog?.WriteLine(badSector, _dev.Error, _dev.LastError, senseBuf); + + continue; + } + + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(readBuffer, badSector); + } + + EndProgress?.Invoke(); + end = DateTime.UtcNow; + UpdateStatus?.Invoke($"Trimming finished in {(end - start).TotalSeconds} seconds."); + _dumpLog.WriteLine("Trimming finished in {0} seconds.", (end - start).TotalSeconds); + } + #endregion Trimming + + #region Error handling + if(_resume.BadBlocks.Count > 0 && + !_aborted && + _retryPasses > 0) + { + List tmpList = new List(); + + foreach(ulong ur in _resume.BadBlocks) + for(ulong i = ur; i < ur + blocksToRead; i++) + tmpList.Add(i); + + tmpList.Sort(); + + int pass = 1; + bool forward = true; + bool runningPersistent = false; + + _resume.BadBlocks = tmpList; + Modes.ModePage? currentModePage = null; + byte[] md6; + byte[] md10; + + if(_persistent) + { + Modes.ModePage_01_MMC pgMmc; + + sense = _dev.ModeSense6(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, + _dev.Timeout, out _); + + if(sense) + { + sense = _dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, + _dev.Timeout, out _); + + if(!sense) { - sense = _dev.ModeSense10(out readBuffer, out _, false, ScsiModeSensePageControl.Current, 0x01, - _dev.Timeout, out _); + Modes.DecodedMode? dcMode10 = + Modes.DecodeMode10(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); - if(!sense) - { - Modes.DecodedMode? dcMode10 = - Modes.DecodeMode10(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); - - if(dcMode10.HasValue) - foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) - currentModePage = modePage; - } - } - else - { - Modes.DecodedMode? dcMode6 = - Modes.DecodeMode6(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); - - if(dcMode6.HasValue) - foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => - modePage.Page == 0x01 && modePage.Subpage == 0x00)) + if(dcMode10.HasValue) + foreach(Modes.ModePage modePage in dcMode10.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) currentModePage = modePage; } + } + else + { + Modes.DecodedMode? dcMode6 = + Modes.DecodeMode6(readBuffer, PeripheralDeviceTypes.MultiMediaDevice); - if(currentModePage == null) + if(dcMode6.HasValue) + foreach(Modes.ModePage modePage in dcMode6.Value.Pages.Where(modePage => + modePage.Page == 0x01 && modePage.Subpage == 0x00)) + currentModePage = modePage; + } + + if(currentModePage == null) + { + pgMmc = new Modes.ModePage_01_MMC { - pgMmc = new Modes.ModePage_01_MMC - { - PS = false, - ReadRetryCount = 0x20, - Parameter = 0x00 - }; + PS = false, + ReadRetryCount = 0x20, + Parameter = 0x00 + }; - currentModePage = new Modes.ModePage + currentModePage = new Modes.ModePage + { + Page = 0x01, + Subpage = 0x00, + PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) + }; + } + + pgMmc = new Modes.ModePage_01_MMC + { + PS = false, + ReadRetryCount = 255, + Parameter = 0x20 + }; + + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + new Modes.ModePage { Page = 0x01, Subpage = 0x00, PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) - }; - } - - pgMmc = new Modes.ModePage_01_MMC - { - PS = false, - ReadRetryCount = 255, - Parameter = 0x20 - }; - - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - new Modes.ModePage - { - Page = 0x01, - Subpage = 0x00, - PageResponse = Modes.EncodeModePage_01_MMC(pgMmc) - } } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - md10 = Modes.EncodeMode10(md, _dev.ScsiType); - - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); - sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - { - UpdateStatus?. - Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - - AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - - _dumpLog. - WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } - else - runningPersistent = true; - } + }; - InitProgress?.Invoke(); - repeatRetry: - ulong[] tmpArray = _resume.BadBlocks.ToArray(); + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + md10 = Modes.EncodeMode10(md, _dev.ScsiType); - foreach(ulong badSector in tmpArray) + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return damaged blocks)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return damaged blocks)."); + sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) + sense = _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) { - if(_aborted) - { - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + UpdateStatus?. + Invoke("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); - break; - } + AaruConsole.DebugWriteLine("Error: {0}", Sense.PrettifySense(senseBuf)); - PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, - forward ? "forward" : "reverse", - runningPersistent ? "recovering partial data, " : "")); - - sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector, - blockSize, 0, 1, false, _dev.Timeout, out cmdDuration); - - totalDuration += cmdDuration; - - if(sense || _dev.Error) - _errorLog?.WriteLine(currentSector, _dev.Error, _dev.LastError, senseBuf); - - if(!sense && - !_dev.Error) - { - _resume.BadBlocks.Remove(badSector); - extents.Add(badSector); - outputFormat.WriteSector(readBuffer, badSector); - UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); - _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); - } - else if(runningPersistent) - outputFormat.WriteSector(readBuffer, badSector); + _dumpLog. + WriteLine("Drive did not accept MODE SELECT command for persistent error reading, try another drive."); } - - if(pass < _retryPasses && - !_aborted && - _resume.BadBlocks.Count > 0) - { - pass++; - forward = !forward; - _resume.BadBlocks.Sort(); - - if(!forward) - _resume.BadBlocks.Reverse(); - - goto repeatRetry; - } - - if(runningPersistent && currentModePage.HasValue) - { - var md = new Modes.DecodedMode - { - Header = new Modes.ModeHeader(), - Pages = new[] - { - currentModePage.Value - } - }; - - md6 = Modes.EncodeMode6(md, _dev.ScsiType); - md10 = Modes.EncodeMode10(md, _dev.ScsiType); - - UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); - _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); - sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); - - if(sense) - _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); - } - - EndProgress?.Invoke(); - } - #endregion Error handling - - _resume.BadBlocks.Sort(); - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - foreach(KeyValuePair tag in mediaTags) - { - if(tag.Value is null) - { - AaruConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key); - - continue; - } - - ret = outputFormat.WriteMediaTag(tag.Value, tag.Key); - - if(ret || _force) - continue; - - // Cannot write tag to image - _dumpLog.WriteLine($"Cannot write tag {tag.Key}."); - - StoppingErrorMessage?.Invoke($"Cannot write tag {tag.Key}." + Environment.NewLine + - outputFormat.ErrorMessage); - - return; + else + runningPersistent = true; } - _resume.BadBlocks.Sort(); + InitProgress?.Invoke(); + repeatRetry: + ulong[] tmpArray = _resume.BadBlocks.ToArray(); - foreach(ulong bad in _resume.BadBlocks) - _dumpLog.WriteLine("Sector {0} could not be read.", bad); - - currentTry.Extents = ExtentsConverter.ToMetadata(extents); - - outputFormat.SetDumpHardware(_resume.Tries); - - var metadata = new CommonTypes.Structs.ImageInfo + foreach(ulong badSector in tmpArray) { - Application = "Aaru", - ApplicationVersion = Version.GetVersion() + if(_aborted) + { + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + break; + } + + PulseProgress?.Invoke(string.Format("Retrying sector {0}, pass {1}, {3}{2}", badSector, pass, + forward ? "forward" : "reverse", + runningPersistent ? "recovering partial data, " : "")); + + sense = _dev.Read12(out readBuffer, out senseBuf, 0, false, false, false, false, (uint)badSector, + blockSize, 0, 1, false, _dev.Timeout, out cmdDuration); + + totalDuration += cmdDuration; + + if(sense || _dev.Error) + _errorLog?.WriteLine(currentSector, _dev.Error, _dev.LastError, senseBuf); + + if(!sense && + !_dev.Error) + { + _resume.BadBlocks.Remove(badSector); + extents.Add(badSector); + outputFormat.WriteSector(readBuffer, badSector); + UpdateStatus?.Invoke($"Correctly retried block {badSector} in pass {pass}."); + _dumpLog.WriteLine("Correctly retried block {0} in pass {1}.", badSector, pass); + } + else if(runningPersistent) + outputFormat.WriteSector(readBuffer, badSector); + } + + if(pass < _retryPasses && + !_aborted && + _resume.BadBlocks.Count > 0) + { + pass++; + forward = !forward; + _resume.BadBlocks.Sort(); + + if(!forward) + _resume.BadBlocks.Reverse(); + + goto repeatRetry; + } + + if(runningPersistent && currentModePage.HasValue) + { + var md = new Modes.DecodedMode + { + Header = new Modes.ModeHeader(), + Pages = new[] + { + currentModePage.Value + } + }; + + md6 = Modes.EncodeMode6(md, _dev.ScsiType); + md10 = Modes.EncodeMode10(md, _dev.ScsiType); + + UpdateStatus?.Invoke("Sending MODE SELECT to drive (return device to previous status)."); + _dumpLog.WriteLine("Sending MODE SELECT to drive (return device to previous status)."); + sense = _dev.ModeSelect(md6, out senseBuf, true, false, _dev.Timeout, out _); + + if(sense) + _dev.ModeSelect10(md10, out senseBuf, true, false, _dev.Timeout, out _); + } + + EndProgress?.Invoke(); + } + #endregion Error handling + + _resume.BadBlocks.Sort(); + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + foreach(KeyValuePair tag in mediaTags) + { + if(tag.Value is null) + { + AaruConsole.ErrorWriteLine("Error: Tag type {0} is null, skipping...", tag.Key); + + continue; + } + + ret = outputFormat.WriteMediaTag(tag.Value, tag.Key); + + if(ret || _force) + continue; + + // Cannot write tag to image + _dumpLog.WriteLine($"Cannot write tag {tag.Key}."); + + StoppingErrorMessage?.Invoke($"Cannot write tag {tag.Key}." + Environment.NewLine + + outputFormat.ErrorMessage); + + return; + } + + _resume.BadBlocks.Sort(); + + foreach(ulong bad in _resume.BadBlocks) + _dumpLog.WriteLine("Sector {0} could not be read.", bad); + + currentTry.Extents = ExtentsConverter.ToMetadata(extents); + + outputFormat.SetDumpHardware(_resume.Tries); + + var metadata = new CommonTypes.Structs.ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion() + }; + + if(!outputFormat.SetMetadata(metadata)) + ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + + outputFormat.ErrorMessage); + + if(_preSidecar != null) + outputFormat.SetCicmMetadata(_preSidecar); + + _dumpLog.WriteLine("Closing output file."); + UpdateStatus?.Invoke("Closing output file."); + DateTime closeStart = DateTime.Now; + outputFormat.Close(); + DateTime closeEnd = DateTime.Now; + UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); + _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); + + if(_aborted) + { + UpdateStatus?.Invoke("Aborted!"); + _dumpLog.WriteLine("Aborted!"); + + return; + } + + double totalChkDuration = 0; + + if(_metadata) + { + var layers = new LayersType + { + type = LayersTypeType.OTP, + typeSpecified = true, + Sectors = new SectorsType[1] }; - if(!outputFormat.SetMetadata(metadata)) - ErrorMessage?.Invoke("Error {0} setting metadata, continuing..." + Environment.NewLine + - outputFormat.ErrorMessage); - - if(_preSidecar != null) - outputFormat.SetCicmMetadata(_preSidecar); - - _dumpLog.WriteLine("Closing output file."); - UpdateStatus?.Invoke("Closing output file."); - DateTime closeStart = DateTime.Now; - outputFormat.Close(); - DateTime closeEnd = DateTime.Now; - UpdateStatus?.Invoke($"Closed in {(closeEnd - closeStart).TotalSeconds} seconds."); - _dumpLog.WriteLine("Closed in {0} seconds.", (closeEnd - closeStart).TotalSeconds); - - if(_aborted) + layers.Sectors[0] = new SectorsType { - UpdateStatus?.Invoke("Aborted!"); - _dumpLog.WriteLine("Aborted!"); + Value = layerBreak + }; - return; - } - - double totalChkDuration = 0; - - if(_metadata) - { - var layers = new LayersType - { - type = LayersTypeType.OTP, - typeSpecified = true, - Sectors = new SectorsType[1] - }; - - layers.Sectors[0] = new SectorsType - { - Value = layerBreak - }; - - WriteOpticalSidecar(blockSize, blocks, dskType, layers, mediaTags, 1, out totalChkDuration, null); - } - - UpdateStatus?.Invoke(""); - - UpdateStatus?. - Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); - - UpdateStatus?. - Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); - - if(maxSpeed > 0) - UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); - - if(minSpeed > 0 && - minSpeed < double.MaxValue) - UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); - - UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); - UpdateStatus?.Invoke(""); - - Statistics.AddMedia(dskType, true); + WriteOpticalSidecar(blockSize, blocks, dskType, layers, mediaTags, 1, out totalChkDuration, null); } + + UpdateStatus?.Invoke(""); + + UpdateStatus?. + Invoke($"Took a total of {(end - start).TotalSeconds:F3} seconds ({totalDuration / 1000:F3} processing commands, {totalChkDuration / 1000:F3} checksumming, {imageWriteDuration:F3} writing, {(closeEnd - closeStart).TotalSeconds:F3} closing)."); + + UpdateStatus?. + Invoke($"Average speed: {blockSize * (double)(blocks + 1) / 1048576 / (totalDuration / 1000):F3} MiB/sec."); + + if(maxSpeed > 0) + UpdateStatus?.Invoke($"Fastest speed burst: {maxSpeed:F3} MiB/sec."); + + if(minSpeed > 0 && + minSpeed < double.MaxValue) + UpdateStatus?.Invoke($"Slowest speed burst: {minSpeed:F3} MiB/sec."); + + UpdateStatus?.Invoke($"{_resume.BadBlocks.Count} sectors could not be read."); + UpdateStatus?.Invoke(""); + + Statistics.AddMedia(dskType, true); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Info/DeviceInfo.cs b/Aaru.Core/Devices/Info/DeviceInfo.cs index bf7ccb3ad..61916737b 100644 --- a/Aaru.Core/Devices/Info/DeviceInfo.cs +++ b/Aaru.Core/Devices/Info/DeviceInfo.cs @@ -45,575 +45,574 @@ using Aaru.Helpers; using DVDDecryption = Aaru.Decryption.DVD.Dump; using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry; -namespace Aaru.Core.Devices.Info +namespace Aaru.Core.Devices.Info; + +/// Obtains and contains information about a device +public partial class DeviceInfo { - /// Obtains and contains information about a device - public partial class DeviceInfo + /// Initializes an instance of this class for the specified device + /// Device + public DeviceInfo(Device dev) { - /// Initializes an instance of this class for the specified device - /// Device - public DeviceInfo(Device dev) + Type = dev.Type; + Manufacturer = dev.Manufacturer; + Model = dev.Model; + FirmwareRevision = dev.FirmwareRevision; + Serial = dev.Serial; + ScsiType = dev.ScsiType; + IsRemovable = dev.IsRemovable; + IsUsb = dev.IsUsb; + UsbVendorId = dev.UsbVendorId; + UsbProductId = dev.UsbProductId; + UsbDescriptors = dev.UsbDescriptors; + UsbManufacturerString = dev.UsbManufacturerString; + UsbProductString = dev.UsbProductString; + UsbSerialString = dev.UsbSerialString; + IsFireWire = dev.IsFireWire; + FireWireGuid = dev.FireWireGuid; + FireWireModel = dev.FireWireModel; + FireWireModelName = dev.FireWireModelName; + FireWireVendor = dev.FireWireVendor; + FireWireVendorName = dev.FireWireVendorName; + IsCompactFlash = dev.IsCompactFlash; + IsPcmcia = dev.IsPcmcia; + Cis = dev.Cis; + + switch(dev.Type) { - Type = dev.Type; - Manufacturer = dev.Manufacturer; - Model = dev.Model; - FirmwareRevision = dev.FirmwareRevision; - Serial = dev.Serial; - ScsiType = dev.ScsiType; - IsRemovable = dev.IsRemovable; - IsUsb = dev.IsUsb; - UsbVendorId = dev.UsbVendorId; - UsbProductId = dev.UsbProductId; - UsbDescriptors = dev.UsbDescriptors; - UsbManufacturerString = dev.UsbManufacturerString; - UsbProductString = dev.UsbProductString; - UsbSerialString = dev.UsbSerialString; - IsFireWire = dev.IsFireWire; - FireWireGuid = dev.FireWireGuid; - FireWireModel = dev.FireWireModel; - FireWireModelName = dev.FireWireModelName; - FireWireVendor = dev.FireWireVendor; - FireWireVendorName = dev.FireWireVendorName; - IsCompactFlash = dev.IsCompactFlash; - IsPcmcia = dev.IsPcmcia; - Cis = dev.Cis; - - switch(dev.Type) + case DeviceType.ATA: { - case DeviceType.ATA: + bool sense = dev.AtaIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters); + + if(sense) { - bool sense = dev.AtaIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters); + AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status); + AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error); - if(sense) - { - AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status); - AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error); + AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}", + errorRegisters.SectorCount); - AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}", - errorRegisters.SectorCount); + AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector); - AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector); + AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}", + errorRegisters.CylinderHigh); - AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}", - errorRegisters.CylinderHigh); + AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}", + errorRegisters.CylinderLow); - AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}", - errorRegisters.CylinderLow); + AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}", + errorRegisters.DeviceHead); - AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}", - errorRegisters.DeviceHead); - - AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError); - - break; - } - - if(dev.Error) - { - AaruConsole.ErrorWriteLine("Error {0} querying ATA IDENTIFY", dev.LastError); - - break; - } - - AtaIdentify = ataBuf; - - dev.EnableMediaCardPassThrough(out errorRegisters, dev.Timeout, out _); - - if(errorRegisters.Sector == 0xAA && - errorRegisters.SectorCount == 0x55) - AtaMcptError = errorRegisters; + AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError); break; } - case DeviceType.ATAPI: + if(dev.Error) { - bool sense = dev.AtapiIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters); + AaruConsole.ErrorWriteLine("Error {0} querying ATA IDENTIFY", dev.LastError); - if(sense) - { - AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status); - AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error); - - AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}", - errorRegisters.SectorCount); - - AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector); - - AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}", - errorRegisters.CylinderHigh); - - AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}", - errorRegisters.CylinderLow); - - AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}", - errorRegisters.DeviceHead); - - AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError); - - break; - } - - if(!dev.Error) - AtapiIdentify = ataBuf; - else - AaruConsole.ErrorWriteLine("Error {0} querying ATA PACKET IDENTIFY", dev.LastError); - - // ATAPI devices are also SCSI devices - goto case DeviceType.SCSI; + break; } - case DeviceType.SCSI: + AtaIdentify = ataBuf; + + dev.EnableMediaCardPassThrough(out errorRegisters, dev.Timeout, out _); + + if(errorRegisters.Sector == 0xAA && + errorRegisters.SectorCount == 0x55) + AtaMcptError = errorRegisters; + + break; + } + + case DeviceType.ATAPI: + { + bool sense = dev.AtapiIdentify(out byte[] ataBuf, out AtaErrorRegistersChs errorRegisters); + + if(sense) { - bool sense = dev.ScsiInquiry(out byte[] inqBuf, out byte[] senseBuf); + AaruConsole.DebugWriteLine("Device-Info command", "STATUS = 0x{0:X2}", errorRegisters.Status); + AaruConsole.DebugWriteLine("Device-Info command", "ERROR = 0x{0:X2}", errorRegisters.Error); - if(sense) - { - AaruConsole.ErrorWriteLine("SCSI error:\n{0}", Sense.PrettifySense(senseBuf)); + AaruConsole.DebugWriteLine("Device-Info command", "NSECTOR = 0x{0:X2}", + errorRegisters.SectorCount); - break; - } + AaruConsole.DebugWriteLine("Device-Info command", "SECTOR = 0x{0:X2}", errorRegisters.Sector); - ScsiInquiryData = inqBuf; - ScsiInquiry = Inquiry.Decode(inqBuf); + AaruConsole.DebugWriteLine("Device-Info command", "CYLHIGH = 0x{0:X2}", + errorRegisters.CylinderHigh); - sense = dev.ScsiInquiry(out inqBuf, out senseBuf, 0x00); + AaruConsole.DebugWriteLine("Device-Info command", "CYLLOW = 0x{0:X2}", + errorRegisters.CylinderLow); - if(!sense) - { - ScsiEvpdPages = new Dictionary(); + AaruConsole.DebugWriteLine("Device-Info command", "DEVICE = 0x{0:X2}", + errorRegisters.DeviceHead); - byte[] pages = EVPD.DecodePage00(inqBuf); + AaruConsole.DebugWriteLine("Device-Info command", "Error code = {0}", dev.LastError); - if(pages != null) - foreach(byte page in pages) - { - sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page); + break; + } - if(sense) - continue; + if(!dev.Error) + AtapiIdentify = ataBuf; + else + AaruConsole.ErrorWriteLine("Error {0} querying ATA PACKET IDENTIFY", dev.LastError); - ScsiEvpdPages.Add(page, inqBuf); - } - } + // ATAPI devices are also SCSI devices + goto case DeviceType.SCSI; + } - var devType = (PeripheralDeviceTypes)ScsiInquiry.Value.PeripheralDeviceType; + case DeviceType.SCSI: + { + bool sense = dev.ScsiInquiry(out byte[] inqBuf, out byte[] senseBuf); - sense = dev.ModeSense10(out byte[] modeBuf, out senseBuf, false, true, - ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _); + if(sense) + { + AaruConsole.ErrorWriteLine("SCSI error:\n{0}", Sense.PrettifySense(senseBuf)); + + break; + } + + ScsiInquiryData = inqBuf; + ScsiInquiry = Inquiry.Decode(inqBuf); + + sense = dev.ScsiInquiry(out inqBuf, out senseBuf, 0x00); + + if(!sense) + { + ScsiEvpdPages = new Dictionary(); + + byte[] pages = EVPD.DecodePage00(inqBuf); + + if(pages != null) + foreach(byte page in pages) + { + sense = dev.ScsiInquiry(out inqBuf, out senseBuf, page); + + if(sense) + continue; + + ScsiEvpdPages.Add(page, inqBuf); + } + } + + var devType = (PeripheralDeviceTypes)ScsiInquiry.Value.PeripheralDeviceType; + + sense = dev.ModeSense10(out byte[] modeBuf, out senseBuf, false, true, + ScsiModeSensePageControl.Current, 0x3F, 0xFF, 5, out _); + + if(!sense && + !dev.Error) + ScsiModeSense10 = modeBuf; + + if(sense || dev.Error) + { + sense = dev.ModeSense10(out modeBuf, out senseBuf, false, true, + ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); if(!sense && !dev.Error) ScsiModeSense10 = modeBuf; + } - if(sense || dev.Error) - { - sense = dev.ModeSense10(out modeBuf, out senseBuf, false, true, - ScsiModeSensePageControl.Current, 0x3F, 0x00, 5, out _); + if(!sense && + !dev.Error) + ScsiMode = Modes.DecodeMode10(modeBuf, devType); - if(!sense && - !dev.Error) - ScsiModeSense10 = modeBuf; - } + bool useMode10 = !(sense || dev.Error || !ScsiMode.HasValue); - if(!sense && - !dev.Error) - ScsiMode = Modes.DecodeMode10(modeBuf, devType); + sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, + 0xFF, 5, out _); - bool useMode10 = !(sense || dev.Error || !ScsiMode.HasValue); + if(!sense && + !dev.Error) + ScsiModeSense6 = modeBuf; + if(sense || dev.Error) + { sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, - 0xFF, 5, out _); + 0x00, 5, out _); if(!sense && !dev.Error) ScsiModeSense6 = modeBuf; + } - if(sense || dev.Error) + if(sense || dev.Error) + { + sense = dev.ModeSense(out modeBuf, out senseBuf, 5, out _); + + if(!sense && + !dev.Error) + ScsiModeSense6 = modeBuf; + } + + if(!sense && + !dev.Error && + !useMode10) + ScsiMode = Modes.DecodeMode6(modeBuf, devType); + + switch(devType) + { + case PeripheralDeviceTypes.MultiMediaDevice: { - sense = dev.ModeSense6(out modeBuf, out senseBuf, false, ScsiModeSensePageControl.Current, 0x3F, - 0x00, 5, out _); + sense = dev.GetConfiguration(out byte[] confBuf, out senseBuf, dev.Timeout, out _); - if(!sense && - !dev.Error) - ScsiModeSense6 = modeBuf; - } + if(!sense) + MmcConfiguration = confBuf; - if(sense || dev.Error) - { - sense = dev.ModeSense(out modeBuf, out senseBuf, 5, out _); + var dvdDecrypt = new DVDDecryption(dev); - if(!sense && - !dev.Error) - ScsiModeSense6 = modeBuf; - } + sense = dvdDecrypt.ReadRpc(out byte[] cmdBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm, + dev.Timeout, out _); - if(!sense && - !dev.Error && - !useMode10) - ScsiMode = Modes.DecodeMode6(modeBuf, devType); - - switch(devType) - { - case PeripheralDeviceTypes.MultiMediaDevice: + if(!sense) { - sense = dev.GetConfiguration(out byte[] confBuf, out senseBuf, dev.Timeout, out _); + CSS_CPRM.RegionalPlaybackControlState? rpc = + CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf); - if(!sense) - MmcConfiguration = confBuf; + if(rpc.HasValue) + RPC = rpc; + } - var dvdDecrypt = new DVDDecryption(dev); + // TODO: DVD drives respond correctly to BD status. + // While specification says if no medium is present + // it should inform all possible capabilities, + // testing drives show only supported media capabilities. + /* + byte[] strBuf; + sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); - sense = dvdDecrypt.ReadRpc(out byte[] cmdBuf, out _, DvdCssKeyClass.DvdCssCppmOrCprm, - dev.Timeout, out _); - - if(!sense) + if (!sense) + { + Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf); + if (caps != null) { - CSS_CPRM.RegionalPlaybackControlState? rpc = - CSS_CPRM.DecodeRegionalPlaybackControlState(cmdBuf); - - if(rpc.HasValue) - RPC = rpc; - } - - // TODO: DVD drives respond correctly to BD status. - // While specification says if no medium is present - // it should inform all possible capabilities, - // testing drives show only supported media capabilities. - /* - byte[] strBuf; - sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); - - if (!sense) - { - Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf); - if (caps != null) + foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps) { - foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps) + if (cap.SDS && cap.RDS) + AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); + else if (cap.SDS) + AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); + else if (cap.RDS) + AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode); + } + } + } + + sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); + + if (!sense) + { + Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf); + if (caps != null) + { + foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps) + { + if (cap.SDS && cap.RDS) + AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); + else if (cap.SDS) + AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); + else if (cap.RDS) + AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode); + } + } + } + */ + + #region Plextor + if(dev.Manufacturer == "PLEXTOR") + { + bool plxtSense = true; + bool plxtDvd = false; + byte[] plxtBuf = null; + + switch(dev.Model) + { + case "DVDR PX-708A": + case "DVDR PX-708A2": + case "DVDR PX-712A": + plxtDvd = true; + + plxtSense = dev.PlextorReadEeprom(out plxtBuf, out senseBuf, dev.Timeout, + out _); + + break; + case "DVDR PX-714A": + case "DVDR PX-716A": + case "DVDR PX-716AL": + case "DVDR PX-755A": + case "DVDR PX-760A": + { + plxtBuf = new byte[256 * 4]; + + for(byte i = 0; i < 4; i++) { - if (cap.SDS && cap.RDS) - AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); - else if (cap.SDS) - AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); - else if (cap.RDS) - AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode); + plxtSense = dev.PlextorReadEepromBlock(out byte[] plxtBufSmall, + out senseBuf, i, 256, dev.Timeout, out _); + + if(plxtSense) + break; + + Array.Copy(plxtBufSmall, 0, plxtBuf, i * 256, 256); } + + plxtDvd = true; + + break; + } + + default: + { + if(dev.Model.StartsWith("CD-R ", StringComparison.Ordinal)) + plxtSense = dev.PlextorReadEepromCdr(out plxtBuf, out senseBuf, dev.Timeout, + out _); + + break; } } - sense = dev.ReadDiscStructure(out strBuf, out senseBuf, MmcDiscStructureMediaType.BD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); - - if (!sense) + PlextorFeatures = new Plextor { - Decoders.SCSI.DiscStructureCapabilities.Capability[] caps = Decoders.SCSI.DiscStructureCapabilities.Decode(strBuf); - if (caps != null) - { - foreach (Decoders.SCSI.DiscStructureCapabilities.Capability cap in caps) - { - if (cap.SDS && cap.RDS) - AaruConsole.WriteLine("Drive can READ/SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); - else if (cap.SDS) - AaruConsole.WriteLine("Drive can SEND DISC STRUCTURE format {0:X2}h", cap.FormatCode); - else if (cap.RDS) - AaruConsole.WriteLine("Drive can READ DISC STRUCTURE format {0:X2}h", cap.FormatCode); - } - } - } - */ + IsDvd = plxtDvd + }; - #region Plextor - if(dev.Manufacturer == "PLEXTOR") + if(!plxtSense) { - bool plxtSense = true; - bool plxtDvd = false; - byte[] plxtBuf = null; - - switch(dev.Model) - { - case "DVDR PX-708A": - case "DVDR PX-708A2": - case "DVDR PX-712A": - plxtDvd = true; - - plxtSense = dev.PlextorReadEeprom(out plxtBuf, out senseBuf, dev.Timeout, - out _); - - break; - case "DVDR PX-714A": - case "DVDR PX-716A": - case "DVDR PX-716AL": - case "DVDR PX-755A": - case "DVDR PX-760A": - { - plxtBuf = new byte[256 * 4]; - - for(byte i = 0; i < 4; i++) - { - plxtSense = dev.PlextorReadEepromBlock(out byte[] plxtBufSmall, - out senseBuf, i, 256, dev.Timeout, out _); - - if(plxtSense) - break; - - Array.Copy(plxtBufSmall, 0, plxtBuf, i * 256, 256); - } - - plxtDvd = true; - - break; - } - - default: - { - if(dev.Model.StartsWith("CD-R ", StringComparison.Ordinal)) - plxtSense = dev.PlextorReadEepromCdr(out plxtBuf, out senseBuf, dev.Timeout, - out _); - - break; - } - } - - PlextorFeatures = new Plextor - { - IsDvd = plxtDvd - }; - - if(!plxtSense) - { - PlextorFeatures.Eeprom = plxtBuf; - - if(plxtDvd) - { - PlextorFeatures.Discs = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0120); - PlextorFeatures.CdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0122); - PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0126); - PlextorFeatures.DvdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012A); - PlextorFeatures.DvdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012E); - } - else - { - PlextorFeatures.Discs = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0078); - PlextorFeatures.CdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x006C); - PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x007A); - } - } - - plxtSense = dev.PlextorGetPoweRec(out senseBuf, out bool plxtPwrRecEnabled, - out ushort plxtPwrRecSpeed, dev.Timeout, out _); - - if(!plxtSense) - { - PlextorFeatures.PoweRec = true; - - if(plxtPwrRecEnabled) - { - PlextorFeatures.PoweRecEnabled = true; - PlextorFeatures.PoweRecRecommendedSpeed = plxtPwrRecSpeed; - - plxtSense = dev.PlextorGetSpeeds(out senseBuf, out ushort plxtPwrRecSelected, - out ushort plxtPwrRecMax, - out ushort plxtPwrRecLast, dev.Timeout, out _); - - if(!plxtSense) - { - PlextorFeatures.PoweRecSelected = plxtPwrRecSelected; - PlextorFeatures.PoweRecMax = plxtPwrRecMax; - PlextorFeatures.PoweRecLast = plxtPwrRecLast; - } - } - } - - // TODO: Check it with a drive - plxtSense = dev.PlextorGetSilentMode(out plxtBuf, out senseBuf, dev.Timeout, out _); - - if(!plxtSense) - if(plxtBuf[0] == 1) - { - PlextorFeatures.SilentModeEnabled = true; - PlextorFeatures.AccessTimeLimit = plxtBuf[1]; - - PlextorFeatures.CdReadSpeedLimit = plxtBuf[2]; - PlextorFeatures.DvdReadSpeedLimit = plxtBuf[3]; - PlextorFeatures.CdWriteSpeedLimit = plxtBuf[4]; - - // TODO: Check which one is each one - /* - if(plxtBuf[6] > 0) - AaruConsole.WriteLine("\tTray eject speed limited to {0}", - -(plxtBuf[6] + 48)); - if(plxtBuf[7] > 0) - AaruConsole.WriteLine("\tTray eject speed limited to {0}", - plxtBuf[7] - 47); - */ - } - - plxtSense = dev.PlextorGetGigaRec(out plxtBuf, out senseBuf, dev.Timeout, out _); - - if(!plxtSense) - PlextorFeatures.GigaRec = true; - - plxtSense = dev.PlextorGetSecuRec(out plxtBuf, out senseBuf, dev.Timeout, out _); - - if(!plxtSense) - PlextorFeatures.SecuRec = true; - - plxtSense = dev.PlextorGetSpeedRead(out plxtBuf, out senseBuf, dev.Timeout, out _); - - if(!plxtSense) - { - PlextorFeatures.SpeedRead = true; - - if((plxtBuf[2] & 0x01) == 0x01) - PlextorFeatures.SpeedReadEnabled = true; - } - - plxtSense = dev.PlextorGetHiding(out plxtBuf, out senseBuf, dev.Timeout, out _); - - if(!plxtSense) - { - PlextorFeatures.Hiding = true; - - if((plxtBuf[2] & 0x02) == 0x02) - PlextorFeatures.HidesRecordables = true; - - if((plxtBuf[2] & 0x01) == 0x01) - PlextorFeatures.HidesSessions = true; - } - - plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, false, dev.Timeout, out _); - - if(!plxtSense) - PlextorFeatures.VariRec = true; + PlextorFeatures.Eeprom = plxtBuf; if(plxtDvd) { - plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, true, dev.Timeout, - out _); - - if(!plxtSense) - PlextorFeatures.VariRecDvd = true; - - plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, false, dev.Timeout, - out _); - - if(!plxtSense) - PlextorFeatures.BitSetting = true; - - plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, true, dev.Timeout, - out _); - - if(!plxtSense) - PlextorFeatures.BitSettingDl = true; - - plxtSense = dev.PlextorGetTestWriteDvdPlus(out plxtBuf, out senseBuf, dev.Timeout, - out _); - - if(!plxtSense) - PlextorFeatures.DvdPlusWriteTest = true; + PlextorFeatures.Discs = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0120); + PlextorFeatures.CdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0122); + PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x0126); + PlextorFeatures.DvdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012A); + PlextorFeatures.DvdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x012E); + } + else + { + PlextorFeatures.Discs = BigEndianBitConverter.ToUInt16(plxtBuf, 0x0078); + PlextorFeatures.CdReadTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x006C); + PlextorFeatures.CdWriteTime = BigEndianBitConverter.ToUInt32(plxtBuf, 0x007A); } } - #endregion Plextor - if(ScsiInquiry.Value.KreonPresent) - if(!dev.KreonGetFeatureList(out senseBuf, out KreonFeatures krFeatures, dev.Timeout, - out _)) - KreonFeatures = krFeatures; + plxtSense = dev.PlextorGetPoweRec(out senseBuf, out bool plxtPwrRecEnabled, + out ushort plxtPwrRecSpeed, dev.Timeout, out _); - break; - } - - case PeripheralDeviceTypes.SequentialAccess: - { - sense = dev.ReadBlockLimits(out byte[] seqBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - AaruConsole.ErrorWriteLine("READ BLOCK LIMITS:\n{0}", Sense.PrettifySense(senseBuf)); - else - BlockLimits = seqBuf; - - sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT:\n{0}", - Sense.PrettifySense(senseBuf)); - else + if(!plxtSense) { - DensitySupport = seqBuf; - DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf); + PlextorFeatures.PoweRec = true; + + if(plxtPwrRecEnabled) + { + PlextorFeatures.PoweRecEnabled = true; + PlextorFeatures.PoweRecRecommendedSpeed = plxtPwrRecSpeed; + + plxtSense = dev.PlextorGetSpeeds(out senseBuf, out ushort plxtPwrRecSelected, + out ushort plxtPwrRecMax, + out ushort plxtPwrRecLast, dev.Timeout, out _); + + if(!plxtSense) + { + PlextorFeatures.PoweRecSelected = plxtPwrRecSelected; + PlextorFeatures.PoweRecMax = plxtPwrRecMax; + PlextorFeatures.PoweRecLast = plxtPwrRecLast; + } + } } - sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _); + // TODO: Check it with a drive + plxtSense = dev.PlextorGetSilentMode(out plxtBuf, out senseBuf, dev.Timeout, out _); - if(sense) - AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT (MEDIUM):\n{0}", - Sense.PrettifySense(senseBuf)); - else + if(!plxtSense) + if(plxtBuf[0] == 1) + { + PlextorFeatures.SilentModeEnabled = true; + PlextorFeatures.AccessTimeLimit = plxtBuf[1]; + + PlextorFeatures.CdReadSpeedLimit = plxtBuf[2]; + PlextorFeatures.DvdReadSpeedLimit = plxtBuf[3]; + PlextorFeatures.CdWriteSpeedLimit = plxtBuf[4]; + + // TODO: Check which one is each one + /* + if(plxtBuf[6] > 0) + AaruConsole.WriteLine("\tTray eject speed limited to {0}", + -(plxtBuf[6] + 48)); + if(plxtBuf[7] > 0) + AaruConsole.WriteLine("\tTray eject speed limited to {0}", + plxtBuf[7] - 47); + */ + } + + plxtSense = dev.PlextorGetGigaRec(out plxtBuf, out senseBuf, dev.Timeout, out _); + + if(!plxtSense) + PlextorFeatures.GigaRec = true; + + plxtSense = dev.PlextorGetSecuRec(out plxtBuf, out senseBuf, dev.Timeout, out _); + + if(!plxtSense) + PlextorFeatures.SecuRec = true; + + plxtSense = dev.PlextorGetSpeedRead(out plxtBuf, out senseBuf, dev.Timeout, out _); + + if(!plxtSense) { - MediumDensitySupport = seqBuf; - MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf); + PlextorFeatures.SpeedRead = true; + + if((plxtBuf[2] & 0x01) == 0x01) + PlextorFeatures.SpeedReadEnabled = true; } - break; + plxtSense = dev.PlextorGetHiding(out plxtBuf, out senseBuf, dev.Timeout, out _); + + if(!plxtSense) + { + PlextorFeatures.Hiding = true; + + if((plxtBuf[2] & 0x02) == 0x02) + PlextorFeatures.HidesRecordables = true; + + if((plxtBuf[2] & 0x01) == 0x01) + PlextorFeatures.HidesSessions = true; + } + + plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, false, dev.Timeout, out _); + + if(!plxtSense) + PlextorFeatures.VariRec = true; + + if(plxtDvd) + { + plxtSense = dev.PlextorGetVariRec(out plxtBuf, out senseBuf, true, dev.Timeout, + out _); + + if(!plxtSense) + PlextorFeatures.VariRecDvd = true; + + plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, false, dev.Timeout, + out _); + + if(!plxtSense) + PlextorFeatures.BitSetting = true; + + plxtSense = dev.PlextorGetBitsetting(out plxtBuf, out senseBuf, true, dev.Timeout, + out _); + + if(!plxtSense) + PlextorFeatures.BitSettingDl = true; + + plxtSense = dev.PlextorGetTestWriteDvdPlus(out plxtBuf, out senseBuf, dev.Timeout, + out _); + + if(!plxtSense) + PlextorFeatures.DvdPlusWriteTest = true; + } } + #endregion Plextor + + if(ScsiInquiry.Value.KreonPresent) + if(!dev.KreonGetFeatureList(out senseBuf, out KreonFeatures krFeatures, dev.Timeout, + out _)) + KreonFeatures = krFeatures; + + break; } - break; + case PeripheralDeviceTypes.SequentialAccess: + { + sense = dev.ReadBlockLimits(out byte[] seqBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + AaruConsole.ErrorWriteLine("READ BLOCK LIMITS:\n{0}", Sense.PrettifySense(senseBuf)); + else + BlockLimits = seqBuf; + + sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT:\n{0}", + Sense.PrettifySense(senseBuf)); + else + { + DensitySupport = seqBuf; + DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf); + } + + sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _); + + if(sense) + AaruConsole.ErrorWriteLine("REPORT DENSITY SUPPORT (MEDIUM):\n{0}", + Sense.PrettifySense(senseBuf)); + else + { + MediumDensitySupport = seqBuf; + MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf); + } + + break; + } } - case DeviceType.MMC: - { - bool sense = dev.ReadCid(out byte[] mmcBuf, out _, dev.Timeout, out _); - - if(!sense) - CID = mmcBuf; - - sense = dev.ReadCsd(out mmcBuf, out _, dev.Timeout, out _); - - if(!sense) - CSD = mmcBuf; - - sense = dev.ReadOcr(out mmcBuf, out _, dev.Timeout, out _); - - if(!sense) - OCR = mmcBuf; - - sense = dev.ReadExtendedCsd(out mmcBuf, out _, dev.Timeout, out _); - - if(!sense && - !ArrayHelpers.ArrayIsNullOrEmpty(mmcBuf)) - ExtendedCSD = mmcBuf; - } - - break; - case DeviceType.SecureDigital: - { - bool sense = dev.ReadCid(out byte[] sdBuf, out _, dev.Timeout, out _); - - if(!sense) - CID = sdBuf; - - sense = dev.ReadCsd(out sdBuf, out _, dev.Timeout, out _); - - if(!sense) - CSD = sdBuf; - - sense = dev.ReadSdocr(out sdBuf, out _, dev.Timeout, out _); - - if(!sense) - OCR = sdBuf; - - sense = dev.ReadScr(out sdBuf, out _, dev.Timeout, out _); - - if(!sense) - SCR = sdBuf; - } - - break; - default: - AaruConsole.ErrorWriteLine("Unknown device type {0}, cannot get information.", dev.Type); - - break; + break; } + + case DeviceType.MMC: + { + bool sense = dev.ReadCid(out byte[] mmcBuf, out _, dev.Timeout, out _); + + if(!sense) + CID = mmcBuf; + + sense = dev.ReadCsd(out mmcBuf, out _, dev.Timeout, out _); + + if(!sense) + CSD = mmcBuf; + + sense = dev.ReadOcr(out mmcBuf, out _, dev.Timeout, out _); + + if(!sense) + OCR = mmcBuf; + + sense = dev.ReadExtendedCsd(out mmcBuf, out _, dev.Timeout, out _); + + if(!sense && + !ArrayHelpers.ArrayIsNullOrEmpty(mmcBuf)) + ExtendedCSD = mmcBuf; + } + + break; + case DeviceType.SecureDigital: + { + bool sense = dev.ReadCid(out byte[] sdBuf, out _, dev.Timeout, out _); + + if(!sense) + CID = sdBuf; + + sense = dev.ReadCsd(out sdBuf, out _, dev.Timeout, out _); + + if(!sense) + CSD = sdBuf; + + sense = dev.ReadSdocr(out sdBuf, out _, dev.Timeout, out _); + + if(!sense) + OCR = sdBuf; + + sense = dev.ReadScr(out sdBuf, out _, dev.Timeout, out _); + + if(!sense) + SCR = sdBuf; + } + + break; + default: + AaruConsole.ErrorWriteLine("Unknown device type {0}, cannot get information.", dev.Type); + + break; } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Info/Plextor.cs b/Aaru.Core/Devices/Info/Plextor.cs index 49907f297..f8da7d5ee 100644 --- a/Aaru.Core/Devices/Info/Plextor.cs +++ b/Aaru.Core/Devices/Info/Plextor.cs @@ -30,72 +30,71 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Core.Devices.Info +namespace Aaru.Core.Devices.Info; + +/// Contains information about Plextor features +public class Plextor { - /// Contains information about Plextor features - public class Plextor - { - /// Access time limit - public byte AccessTimeLimit; - /// Drive supports setting book type bit for DVD+R - public bool BitSetting; - /// Drive supports setting book type bit for DVD+R DL - public bool BitSettingDl; - /// CD read speed limit - public byte CdReadSpeedLimit; - /// Time drive has spent reading CDs - public uint CdReadTime; - /// CD write speed limit - public byte CdWriteSpeedLimit; - /// Time drive has spent writing CDs - public uint CdWriteTime; - /// Total number of loaded discs - public ushort Discs; - /// Drive supports test writing DVD+ - public bool DvdPlusWriteTest; - /// DVD read limit - public byte DvdReadSpeedLimit; - /// Time drive has spent reading DVDs - public uint DvdReadTime; - /// Time drive has spent writing DVDs - public uint DvdWriteTime; - /// Raw contents of EEPROM - public byte[] Eeprom; - /// Drive supports GigaRec - public bool GigaRec; - /// Drive will show recordable CDs as embossed - public bool HidesRecordables; - /// Drive will hide sessions - public bool HidesSessions; - /// Drive supports hiding recordable CDs and sessions - public bool Hiding; - /// Drive is a DVD capable drive - public bool IsDvd; - /// Drive supports PoweRec - public bool PoweRec; - /// Drive has PoweRec enabled - public bool PoweRecEnabled; - /// Last used PoweRec in KiB/sec - public ushort PoweRecLast; - /// Maximum supported PoweRec for currently inserted media in KiB/sec - public ushort PoweRecMax; - /// Recommended supported PoweRec for currently inserted media in KiB/sec - public ushort PoweRecRecommendedSpeed; - /// Selected supported PoweRec for currently inserted media in KiB/sec - public ushort PoweRecSelected; - /// Drive supports SecuRec - public bool SecuRec; - /// Drive supports SilentMode - public bool SilentMode; - /// Drive has SilentMode enabled - public bool SilentModeEnabled; - /// Drive supports SpeedRead - public bool SpeedRead; - /// Drive has SpeedRead enabled - public bool SpeedReadEnabled; - /// Drive supports VariRec - public bool VariRec; - /// Drive supports VariRec for DVDs - public bool VariRecDvd; - } + /// Access time limit + public byte AccessTimeLimit; + /// Drive supports setting book type bit for DVD+R + public bool BitSetting; + /// Drive supports setting book type bit for DVD+R DL + public bool BitSettingDl; + /// CD read speed limit + public byte CdReadSpeedLimit; + /// Time drive has spent reading CDs + public uint CdReadTime; + /// CD write speed limit + public byte CdWriteSpeedLimit; + /// Time drive has spent writing CDs + public uint CdWriteTime; + /// Total number of loaded discs + public ushort Discs; + /// Drive supports test writing DVD+ + public bool DvdPlusWriteTest; + /// DVD read limit + public byte DvdReadSpeedLimit; + /// Time drive has spent reading DVDs + public uint DvdReadTime; + /// Time drive has spent writing DVDs + public uint DvdWriteTime; + /// Raw contents of EEPROM + public byte[] Eeprom; + /// Drive supports GigaRec + public bool GigaRec; + /// Drive will show recordable CDs as embossed + public bool HidesRecordables; + /// Drive will hide sessions + public bool HidesSessions; + /// Drive supports hiding recordable CDs and sessions + public bool Hiding; + /// Drive is a DVD capable drive + public bool IsDvd; + /// Drive supports PoweRec + public bool PoweRec; + /// Drive has PoweRec enabled + public bool PoweRecEnabled; + /// Last used PoweRec in KiB/sec + public ushort PoweRecLast; + /// Maximum supported PoweRec for currently inserted media in KiB/sec + public ushort PoweRecMax; + /// Recommended supported PoweRec for currently inserted media in KiB/sec + public ushort PoweRecRecommendedSpeed; + /// Selected supported PoweRec for currently inserted media in KiB/sec + public ushort PoweRecSelected; + /// Drive supports SecuRec + public bool SecuRec; + /// Drive supports SilentMode + public bool SilentMode; + /// Drive has SilentMode enabled + public bool SilentModeEnabled; + /// Drive supports SpeedRead + public bool SpeedRead; + /// Drive has SpeedRead enabled + public bool SpeedReadEnabled; + /// Drive supports VariRec + public bool VariRec; + /// Drive supports VariRec for DVDs + public bool VariRecDvd; } \ No newline at end of file diff --git a/Aaru.Core/Devices/Info/Properties.cs b/Aaru.Core/Devices/Info/Properties.cs index 89946a524..0f9271780 100644 --- a/Aaru.Core/Devices/Info/Properties.cs +++ b/Aaru.Core/Devices/Info/Properties.cs @@ -40,101 +40,100 @@ using Aaru.Decoders.SCSI.SSC; using Aaru.Devices; using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry; -namespace Aaru.Core.Devices.Info +namespace Aaru.Core.Devices.Info; + +public partial class DeviceInfo { - public partial class DeviceInfo - { - /// Raw IDENTIFY DEVICE response - public byte[] AtaIdentify { get; } - /// Raw IDENTIFY PACKET DEVICE response - public byte[] AtapiIdentify { get; } - /// Raw INQUIRY response - public byte[] ScsiInquiryData { get; } - /// Decoded INQUIRY response - public Inquiry? ScsiInquiry { get; } - /// Response for ATA Memory Card Pass Through - public AtaErrorRegistersChs? AtaMcptError { get; } - /// List of raw EVPD page indexed by page number - public Dictionary ScsiEvpdPages { get; } - /// Decoded MODE SENSE response - public Modes.DecodedMode? ScsiMode { get; } - /// Raw MODE SENSE(6) response - public byte[] ScsiModeSense6 { get; } - /// Raw MODE SENSE(10) response - public byte[] ScsiModeSense10 { get; } - /// Raw GET CONFIGURATION response - public byte[] MmcConfiguration { get; } - /// Decoded Plextor features - public Plextor PlextorFeatures { get; } - /// Decoded Kreon features - public KreonFeatures KreonFeatures { get; } - /// Raw GET BLOCK LIMITS support - public byte[] BlockLimits { get; } - /// Raw density support - public byte[] DensitySupport { get; } - /// Decoded density support - public DensitySupport.DensitySupportHeader? DensitySupportHeader { get; } - /// Raw medium density support - public byte[] MediumDensitySupport { get; } - /// Decoded medium density support - public DensitySupport.MediaTypeSupportHeader? MediaTypeSupportHeader { get; } - /// Raw CID registers - public byte[] CID { get; } - /// Raw CSD - public byte[] CSD { get; } - /// Raw extended CSD - public byte[] ExtendedCSD { get; } - /// Raw SCR registers - public byte[] SCR { get; } - /// Raw OCR registers - public byte[] OCR { get; } - /// Aaru's device type - public DeviceType Type { get; } - /// Device manufacturer - public string Manufacturer { get; } - /// Device model - public string Model { get; } - /// Device firmware version or revision - public string FirmwareRevision { get; } - /// Device serial number - public string Serial { get; } - /// SCSI Peripheral Device Type - public PeripheralDeviceTypes ScsiType { get; } - /// Is media removable from device? - public bool IsRemovable { get; } - /// Is device attached via USB? - public bool IsUsb { get; } - /// USB vendor ID - public ushort UsbVendorId { get; } - /// USB product ID - public ushort UsbProductId { get; } - /// Raw USB descriptors - public byte[] UsbDescriptors { get; } - /// USB manufacturer string - public string UsbManufacturerString { get; } - /// USB product string - public string UsbProductString { get; } - /// USB serial number string - public string UsbSerialString { get; } - /// Is device attached via FireWire? - public bool IsFireWire { get; } - /// FireWire's device GUID - public ulong FireWireGuid { get; } - /// FireWire's device model ID - public uint FireWireModel { get; } - /// FireWire's device model name - public string FireWireModelName { get; } - /// FireWire's device vendor ID - public uint FireWireVendor { get; } - /// FireWire's device vendor name - public string FireWireVendorName { get; } - /// Is device a CompactFlash device? - public bool IsCompactFlash { get; } - /// Is device a PCMCIA or CardBus device? - public bool IsPcmcia { get; } - /// PCMCIA/CardBus CIS - public byte[] Cis { get; } - /// MMC device CSS/CPRM Region Protection Code - public CSS_CPRM.RegionalPlaybackControlState? RPC { get; } - } + /// Raw IDENTIFY DEVICE response + public byte[] AtaIdentify { get; } + /// Raw IDENTIFY PACKET DEVICE response + public byte[] AtapiIdentify { get; } + /// Raw INQUIRY response + public byte[] ScsiInquiryData { get; } + /// Decoded INQUIRY response + public Inquiry? ScsiInquiry { get; } + /// Response for ATA Memory Card Pass Through + public AtaErrorRegistersChs? AtaMcptError { get; } + /// List of raw EVPD page indexed by page number + public Dictionary ScsiEvpdPages { get; } + /// Decoded MODE SENSE response + public Modes.DecodedMode? ScsiMode { get; } + /// Raw MODE SENSE(6) response + public byte[] ScsiModeSense6 { get; } + /// Raw MODE SENSE(10) response + public byte[] ScsiModeSense10 { get; } + /// Raw GET CONFIGURATION response + public byte[] MmcConfiguration { get; } + /// Decoded Plextor features + public Plextor PlextorFeatures { get; } + /// Decoded Kreon features + public KreonFeatures KreonFeatures { get; } + /// Raw GET BLOCK LIMITS support + public byte[] BlockLimits { get; } + /// Raw density support + public byte[] DensitySupport { get; } + /// Decoded density support + public DensitySupport.DensitySupportHeader? DensitySupportHeader { get; } + /// Raw medium density support + public byte[] MediumDensitySupport { get; } + /// Decoded medium density support + public DensitySupport.MediaTypeSupportHeader? MediaTypeSupportHeader { get; } + /// Raw CID registers + public byte[] CID { get; } + /// Raw CSD + public byte[] CSD { get; } + /// Raw extended CSD + public byte[] ExtendedCSD { get; } + /// Raw SCR registers + public byte[] SCR { get; } + /// Raw OCR registers + public byte[] OCR { get; } + /// Aaru's device type + public DeviceType Type { get; } + /// Device manufacturer + public string Manufacturer { get; } + /// Device model + public string Model { get; } + /// Device firmware version or revision + public string FirmwareRevision { get; } + /// Device serial number + public string Serial { get; } + /// SCSI Peripheral Device Type + public PeripheralDeviceTypes ScsiType { get; } + /// Is media removable from device? + public bool IsRemovable { get; } + /// Is device attached via USB? + public bool IsUsb { get; } + /// USB vendor ID + public ushort UsbVendorId { get; } + /// USB product ID + public ushort UsbProductId { get; } + /// Raw USB descriptors + public byte[] UsbDescriptors { get; } + /// USB manufacturer string + public string UsbManufacturerString { get; } + /// USB product string + public string UsbProductString { get; } + /// USB serial number string + public string UsbSerialString { get; } + /// Is device attached via FireWire? + public bool IsFireWire { get; } + /// FireWire's device GUID + public ulong FireWireGuid { get; } + /// FireWire's device model ID + public uint FireWireModel { get; } + /// FireWire's device model name + public string FireWireModelName { get; } + /// FireWire's device vendor ID + public uint FireWireVendor { get; } + /// FireWire's device vendor name + public string FireWireVendorName { get; } + /// Is device a CompactFlash device? + public bool IsCompactFlash { get; } + /// Is device a PCMCIA or CardBus device? + public bool IsPcmcia { get; } + /// PCMCIA/CardBus CIS + public byte[] Cis { get; } + /// MMC device CSS/CPRM Region Protection Code + public CSS_CPRM.RegionalPlaybackControlState? RPC { get; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Reader.cs b/Aaru.Core/Devices/Reader.cs index 003d784f3..1c35c8ed0 100644 --- a/Aaru.Core/Devices/Reader.cs +++ b/Aaru.Core/Devices/Reader.cs @@ -36,172 +36,171 @@ using Aaru.CommonTypes.Structs.Devices.ATA; using Aaru.Core.Logging; using Aaru.Devices; -namespace Aaru.Core.Devices +namespace Aaru.Core.Devices; + +/// Reduces common code used for scanning and dumping +internal sealed partial class Reader { - /// Reduces common code used for scanning and dumping - internal sealed partial class Reader + readonly Device _dev; + readonly ErrorLog _errorLog; + readonly uint _timeout; + + internal Reader(Device dev, uint timeout, byte[] identification, ErrorLog errorLog, bool raw = false) { - readonly Device _dev; - readonly ErrorLog _errorLog; - readonly uint _timeout; + _dev = dev; + _timeout = timeout; + BlocksToRead = 64; + CanReadRaw = raw; + _errorLog = errorLog; - internal Reader(Device dev, uint timeout, byte[] identification, ErrorLog errorLog, bool raw = false) + switch(dev.Type) { - _dev = dev; - _timeout = timeout; - BlocksToRead = 64; - CanReadRaw = raw; - _errorLog = errorLog; + case DeviceType.ATA: + Identify.IdentifyDevice? ataIdNullable = Identify.Decode(identification); - switch(dev.Type) - { - case DeviceType.ATA: - Identify.IdentifyDevice? ataIdNullable = Identify.Decode(identification); + if(ataIdNullable.HasValue) + _ataId = ataIdNullable.Value; - if(ataIdNullable.HasValue) - _ataId = ataIdNullable.Value; - - break; - case DeviceType.NVMe: throw new NotImplementedException("NVMe devices not yet supported."); - } + break; + case DeviceType.NVMe: throw new NotImplementedException("NVMe devices not yet supported."); } + } - internal string ErrorMessage { get; private set; } - internal ulong Blocks { get; private set; } - internal uint BlocksToRead { get; private set; } - internal uint LogicalBlockSize { get; private set; } - internal uint PhysicalBlockSize { get; private set; } - internal uint LongBlockSize { get; private set; } - internal bool CanReadRaw { get; private set; } - internal bool CanSeek => _ataSeek || _seek6 || _seek10; - internal bool CanSeekLba => _ataSeekLba || _seek6 || _seek10; + internal string ErrorMessage { get; private set; } + internal ulong Blocks { get; private set; } + internal uint BlocksToRead { get; private set; } + internal uint LogicalBlockSize { get; private set; } + internal uint PhysicalBlockSize { get; private set; } + internal uint LongBlockSize { get; private set; } + internal bool CanReadRaw { get; private set; } + internal bool CanSeek => _ataSeek || _seek6 || _seek10; + internal bool CanSeekLba => _ataSeekLba || _seek6 || _seek10; - internal ulong GetDeviceBlocks() + internal ulong GetDeviceBlocks() + { + switch(_dev.Type) { - switch(_dev.Type) - { - case DeviceType.ATA: return AtaGetBlocks(); - case DeviceType.ATAPI: - case DeviceType.SCSI: return ScsiGetBlocks(); - default: - ErrorMessage = $"Unknown device type {_dev.Type}."; + case DeviceType.ATA: return AtaGetBlocks(); + case DeviceType.ATAPI: + case DeviceType.SCSI: return ScsiGetBlocks(); + default: + ErrorMessage = $"Unknown device type {_dev.Type}."; - return 0; - } + return 0; } + } - internal bool FindReadCommand() + internal bool FindReadCommand() + { + switch(_dev.Type) { - switch(_dev.Type) - { - case DeviceType.ATA: return AtaFindReadCommand(); - case DeviceType.ATAPI: - case DeviceType.SCSI: return ScsiFindReadCommand(); - default: - ErrorMessage = $"Unknown device type {_dev.Type}."; + case DeviceType.ATA: return AtaFindReadCommand(); + case DeviceType.ATAPI: + case DeviceType.SCSI: return ScsiFindReadCommand(); + default: + ErrorMessage = $"Unknown device type {_dev.Type}."; - return true; - } + return true; } + } - internal bool GetBlockSize() + internal bool GetBlockSize() + { + switch(_dev.Type) { - switch(_dev.Type) - { - case DeviceType.ATA: return AtaGetBlockSize(); - case DeviceType.ATAPI: - case DeviceType.SCSI: return ScsiGetBlockSize(); - default: - ErrorMessage = $"Unknown device type {_dev.Type}."; + case DeviceType.ATA: return AtaGetBlockSize(); + case DeviceType.ATAPI: + case DeviceType.SCSI: return ScsiGetBlockSize(); + default: + ErrorMessage = $"Unknown device type {_dev.Type}."; - return true; - } + return true; } + } - internal bool GetBlocksToRead(uint startWithBlocks = 64) + internal bool GetBlocksToRead(uint startWithBlocks = 64) + { + switch(_dev.Type) { - switch(_dev.Type) - { - case DeviceType.ATA: return AtaGetBlocksToRead(startWithBlocks); - case DeviceType.ATAPI: - case DeviceType.SCSI: return ScsiGetBlocksToRead(startWithBlocks); - default: - ErrorMessage = $"Unknown device type {_dev.Type}."; + case DeviceType.ATA: return AtaGetBlocksToRead(startWithBlocks); + case DeviceType.ATAPI: + case DeviceType.SCSI: return ScsiGetBlocksToRead(startWithBlocks); + default: + ErrorMessage = $"Unknown device type {_dev.Type}."; - return true; - } + return true; } + } - internal bool ReadBlock(out byte[] buffer, ulong block, out double duration, out bool recoveredError, - out bool blankCheck) => - ReadBlocks(out buffer, block, 1, out duration, out recoveredError, out blankCheck); + internal bool ReadBlock(out byte[] buffer, ulong block, out double duration, out bool recoveredError, + out bool blankCheck) => + ReadBlocks(out buffer, block, 1, out duration, out recoveredError, out blankCheck); - internal bool ReadBlocks(out byte[] buffer, ulong block, out double duration, out bool recoveredError, - out bool blankCheck) => ReadBlocks(out buffer, block, BlocksToRead, out duration, - out recoveredError, out blankCheck); + internal bool ReadBlocks(out byte[] buffer, ulong block, out double duration, out bool recoveredError, + out bool blankCheck) => ReadBlocks(out buffer, block, BlocksToRead, out duration, + out recoveredError, out blankCheck); - internal bool ReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, - out bool recoveredError, out bool blankCheck) + internal bool ReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, + out bool recoveredError, out bool blankCheck) + { + switch(_dev.Type) { - switch(_dev.Type) - { - case DeviceType.ATA: - blankCheck = false; + case DeviceType.ATA: + blankCheck = false; - return AtaReadBlocks(out buffer, block, count, out duration, out recoveredError); - case DeviceType.ATAPI: - case DeviceType.SCSI: - return ScsiReadBlocks(out buffer, block, count, out duration, out recoveredError, out blankCheck); - default: - buffer = null; - duration = 0d; - recoveredError = false; - blankCheck = false; + return AtaReadBlocks(out buffer, block, count, out duration, out recoveredError); + case DeviceType.ATAPI: + case DeviceType.SCSI: + return ScsiReadBlocks(out buffer, block, count, out duration, out recoveredError, out blankCheck); + default: + buffer = null; + duration = 0d; + recoveredError = false; + blankCheck = false; - return true; - } + return true; } + } - internal bool ReadChs(out byte[] buffer, ushort cylinder, byte head, byte sector, out double duration, - out bool recoveredError) + internal bool ReadChs(out byte[] buffer, ushort cylinder, byte head, byte sector, out double duration, + out bool recoveredError) + { + switch(_dev.Type) { - switch(_dev.Type) - { - case DeviceType.ATA: - return AtaReadChs(out buffer, cylinder, head, sector, out duration, out recoveredError); - default: - buffer = null; - duration = 0d; - recoveredError = false; + case DeviceType.ATA: + return AtaReadChs(out buffer, cylinder, head, sector, out duration, out recoveredError); + default: + buffer = null; + duration = 0d; + recoveredError = false; - return true; - } + return true; } + } - internal bool Seek(ulong block, out double duration) + internal bool Seek(ulong block, out double duration) + { + switch(_dev.Type) { - switch(_dev.Type) - { - case DeviceType.ATA: return AtaSeek(block, out duration); - case DeviceType.ATAPI: - case DeviceType.SCSI: return ScsiSeek(block, out duration); - default: - duration = 0d; + case DeviceType.ATA: return AtaSeek(block, out duration); + case DeviceType.ATAPI: + case DeviceType.SCSI: return ScsiSeek(block, out duration); + default: + duration = 0d; - return true; - } + return true; } + } - internal bool SeekChs(ushort cylinder, byte head, byte sector, out double duration) + internal bool SeekChs(ushort cylinder, byte head, byte sector, out double duration) + { + switch(_dev.Type) { - switch(_dev.Type) - { - case DeviceType.ATA: return AtaSeekChs(cylinder, head, sector, out duration); - default: - duration = 0; + case DeviceType.ATA: return AtaSeekChs(cylinder, head, sector, out duration); + default: + duration = 0; - return true; - } + return true; } } } \ No newline at end of file diff --git a/Aaru.Core/Devices/ReaderATA.cs b/Aaru.Core/Devices/ReaderATA.cs index 5c37e76ce..178e5718d 100644 --- a/Aaru.Core/Devices/ReaderATA.cs +++ b/Aaru.Core/Devices/ReaderATA.cs @@ -36,482 +36,481 @@ using Aaru.Console; using Aaru.Decoders.ATA; using Identify = Aaru.CommonTypes.Structs.Devices.ATA.Identify; -namespace Aaru.Core.Devices +namespace Aaru.Core.Devices; + +internal sealed partial class Reader { - internal sealed partial class Reader + Identify.IdentifyDevice _ataId; + bool _ataRead; + bool _ataReadDma; + bool _ataReadDmaLba; + bool _ataReadDmaLba48; + bool _ataReadDmaRetry; + bool _ataReadDmaRetryLba; + bool _ataReadLba; + bool _ataReadLba48; + bool _ataReadRetry; + bool _ataReadRetryLba; + bool _ataSeek; + bool _ataSeekLba; + + internal bool IsLba { get; private set; } + internal ushort Cylinders { get; private set; } + internal byte Heads { get; private set; } + internal byte Sectors { get; private set; } + + void GetDeviceChs() { - Identify.IdentifyDevice _ataId; - bool _ataRead; - bool _ataReadDma; - bool _ataReadDmaLba; - bool _ataReadDmaLba48; - bool _ataReadDmaRetry; - bool _ataReadDmaRetryLba; - bool _ataReadLba; - bool _ataReadLba48; - bool _ataReadRetry; - bool _ataReadRetryLba; - bool _ataSeek; - bool _ataSeekLba; + if(_dev.Type != DeviceType.ATA) + return; - internal bool IsLba { get; private set; } - internal ushort Cylinders { get; private set; } - internal byte Heads { get; private set; } - internal byte Sectors { get; private set; } - - void GetDeviceChs() + if(_ataId.CurrentCylinders > 0 && + _ataId.CurrentHeads > 0 && + _ataId.CurrentSectorsPerTrack > 0) { - if(_dev.Type != DeviceType.ATA) - return; - - if(_ataId.CurrentCylinders > 0 && - _ataId.CurrentHeads > 0 && - _ataId.CurrentSectorsPerTrack > 0) - { - Cylinders = _ataId.CurrentCylinders; - Heads = (byte)_ataId.CurrentHeads; - Sectors = (byte)_ataId.CurrentSectorsPerTrack; - Blocks = (ulong)(Cylinders * Heads * Sectors); - } - - if((_ataId.CurrentCylinders != 0 && _ataId.CurrentHeads != 0 && _ataId.CurrentSectorsPerTrack != 0) || - _ataId.Cylinders <= 0 || - _ataId.Heads <= 0 || - _ataId.SectorsPerTrack <= 0) - return; - - Cylinders = _ataId.Cylinders; - Heads = (byte)_ataId.Heads; - Sectors = (byte)_ataId.SectorsPerTrack; + Cylinders = _ataId.CurrentCylinders; + Heads = (byte)_ataId.CurrentHeads; + Sectors = (byte)_ataId.CurrentSectorsPerTrack; Blocks = (ulong)(Cylinders * Heads * Sectors); } - ulong AtaGetBlocks() + if((_ataId.CurrentCylinders != 0 && _ataId.CurrentHeads != 0 && _ataId.CurrentSectorsPerTrack != 0) || + _ataId.Cylinders <= 0 || + _ataId.Heads <= 0 || + _ataId.SectorsPerTrack <= 0) + return; + + Cylinders = _ataId.Cylinders; + Heads = (byte)_ataId.Heads; + Sectors = (byte)_ataId.SectorsPerTrack; + Blocks = (ulong)(Cylinders * Heads * Sectors); + } + + ulong AtaGetBlocks() + { + GetDeviceChs(); + + if(_ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport)) { - GetDeviceChs(); - - if(_ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport)) - { - Blocks = _ataId.LBASectors; - IsLba = true; - } - - if(!_ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48)) - return Blocks; - - Blocks = _ataId.LBA48Sectors; + Blocks = _ataId.LBASectors; IsLba = true; - - return Blocks; } - bool AtaFindReadCommand() + if(!_ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48)) + return Blocks; + + Blocks = _ataId.LBA48Sectors; + IsLba = true; + + return Blocks; + } + + bool AtaFindReadCommand() + { + if(Blocks == 0) + GetDeviceBlocks(); + + bool sense; + int tries = 0; + uint lba = 0; + ushort cyl = 0; + byte head = 0; + byte sector = 1; + AtaErrorRegistersChs errorChs; + AtaErrorRegistersLba28 errorLba; + var rnd = new Random(); + + while(tries < 10) { - if(Blocks == 0) - GetDeviceBlocks(); + sense = _dev.Read(out byte[] cmdBuf, out errorChs, false, cyl, head, sector, 1, _timeout, out _); + _ataRead = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0; + sense = _dev.Read(out cmdBuf, out errorChs, true, cyl, head, sector, 1, _timeout, out _); + _ataReadRetry = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0; + sense = _dev.ReadDma(out cmdBuf, out errorChs, false, cyl, head, sector, 1, _timeout, out _); + _ataReadDma = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0; + sense = _dev.ReadDma(out cmdBuf, out errorChs, true, cyl, head, sector, 1, _timeout, out _); + _ataReadDmaRetry = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0; - bool sense; - int tries = 0; - uint lba = 0; - ushort cyl = 0; - byte head = 0; - byte sector = 1; - AtaErrorRegistersChs errorChs; - AtaErrorRegistersLba28 errorLba; - var rnd = new Random(); + sense = _dev.Read(out cmdBuf, out errorLba, false, lba, 1, _timeout, out _); + _ataReadLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0; + sense = _dev.Read(out cmdBuf, out errorLba, true, lba, 1, _timeout, out _); + _ataReadRetryLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0; + sense = _dev.ReadDma(out cmdBuf, out errorLba, false, lba, 1, _timeout, out _); + _ataReadDmaLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0; + sense = _dev.ReadDma(out cmdBuf, out errorLba, true, lba, 1, _timeout, out _); - while(tries < 10) + _ataReadDmaRetryLba = + !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0; + + sense = _dev.Read(out cmdBuf, out AtaErrorRegistersLba48 errorLba48, lba, 1, _timeout, out _); + _ataReadLba48 = !sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0; + sense = _dev.ReadDma(out cmdBuf, out errorLba48, lba, 1, _timeout, out _); + + _ataReadDmaLba48 = !sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && + cmdBuf.Length > 0; + + if(_ataRead || + _ataReadRetry || + _ataReadDma || + _ataReadDmaRetry || + _ataReadLba || + _ataReadRetryLba || + _ataReadDmaLba || + _ataReadDmaRetryLba || + _ataReadLba48 || + _ataReadDmaLba48) { - sense = _dev.Read(out byte[] cmdBuf, out errorChs, false, cyl, head, sector, 1, _timeout, out _); - _ataRead = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0; - sense = _dev.Read(out cmdBuf, out errorChs, true, cyl, head, sector, 1, _timeout, out _); - _ataReadRetry = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0; - sense = _dev.ReadDma(out cmdBuf, out errorChs, false, cyl, head, sector, 1, _timeout, out _); - _ataReadDma = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0; - sense = _dev.ReadDma(out cmdBuf, out errorChs, true, cyl, head, sector, 1, _timeout, out _); - _ataReadDmaRetry = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && cmdBuf.Length > 0; - - sense = _dev.Read(out cmdBuf, out errorLba, false, lba, 1, _timeout, out _); - _ataReadLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0; - sense = _dev.Read(out cmdBuf, out errorLba, true, lba, 1, _timeout, out _); - _ataReadRetryLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0; - sense = _dev.ReadDma(out cmdBuf, out errorLba, false, lba, 1, _timeout, out _); - _ataReadDmaLba = !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0; - sense = _dev.ReadDma(out cmdBuf, out errorLba, true, lba, 1, _timeout, out _); - - _ataReadDmaRetryLba = - !sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0; - - sense = _dev.Read(out cmdBuf, out AtaErrorRegistersLba48 errorLba48, lba, 1, _timeout, out _); - _ataReadLba48 = !sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0; - sense = _dev.ReadDma(out cmdBuf, out errorLba48, lba, 1, _timeout, out _); - - _ataReadDmaLba48 = !sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && - cmdBuf.Length > 0; - - if(_ataRead || - _ataReadRetry || - _ataReadDma || - _ataReadDmaRetry || - _ataReadLba || - _ataReadRetryLba || - _ataReadDmaLba || - _ataReadDmaRetryLba || - _ataReadLba48 || - _ataReadDmaLba48) - { - break; - } - - lba = (uint)rnd.Next(1, (int)Blocks); - cyl = (ushort)rnd.Next(0, Cylinders); - head = (byte)rnd.Next(0, Heads); - sector = (byte)rnd.Next(1, Sectors); - tries++; + break; } - sense = _dev.Seek(out errorChs, 0, 0, 1, _timeout, out _); - _ataSeek = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0; - sense = _dev.Seek(out errorLba, 0, _timeout, out _); - _ataSeekLba = !sense && (errorLba.Status & 0x27) == 0 && errorChs.Error == 0; + lba = (uint)rnd.Next(1, (int)Blocks); + cyl = (ushort)rnd.Next(0, Cylinders); + head = (byte)rnd.Next(0, Heads); + sector = (byte)rnd.Next(1, Sectors); + tries++; + } - if(IsLba) + sense = _dev.Seek(out errorChs, 0, 0, 1, _timeout, out _); + _ataSeek = !sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0; + sense = _dev.Seek(out errorLba, 0, _timeout, out _); + _ataSeekLba = !sense && (errorLba.Status & 0x27) == 0 && errorChs.Error == 0; + + if(IsLba) + { + if(Blocks > 0xFFFFFFF && + !_ataReadLba48 && + !_ataReadDmaLba48) { - if(Blocks > 0xFFFFFFF && - !_ataReadLba48 && - !_ataReadDmaLba48) - { - ErrorMessage = "Device needs 48-bit LBA commands but I can't issue them... Aborting."; - - return true; - } - - if(!_ataReadLba && - !_ataReadRetryLba && - !_ataReadDmaLba && - !_ataReadDmaRetryLba) - { - ErrorMessage = "Device needs 28-bit LBA commands but I can't issue them... Aborting."; - - return true; - } - } - else - { - if(!_ataRead && - !_ataReadRetry && - !_ataReadDma && - !_ataReadDmaRetry) - { - ErrorMessage = "Device needs CHS commands but I can't issue them... Aborting."; - - return true; - } - } - - if(_ataReadDmaLba48) - AaruConsole.WriteLine("Using ATA READ DMA EXT command."); - else if(_ataReadLba48) - AaruConsole.WriteLine("Using ATA READ EXT command."); - else if(_ataReadDmaRetryLba) - AaruConsole.WriteLine("Using ATA READ DMA command with retries (LBA)."); - else if(_ataReadDmaLba) - AaruConsole.WriteLine("Using ATA READ DMA command (LBA)."); - else if(_ataReadRetryLba) - AaruConsole.WriteLine("Using ATA READ command with retries (LBA)."); - else if(_ataReadLba) - AaruConsole.WriteLine("Using ATA READ command (LBA)."); - else if(_ataReadDmaRetry) - AaruConsole.WriteLine("Using ATA READ DMA command with retries (CHS)."); - else if(_ataReadDma) - AaruConsole.WriteLine("Using ATA READ DMA command (CHS)."); - else if(_ataReadRetry) - AaruConsole.WriteLine("Using ATA READ command with retries (CHS)."); - else if(_ataRead) - AaruConsole.WriteLine("Using ATA READ command (CHS)."); - else - { - ErrorMessage = "Could not get a working read command!"; + ErrorMessage = "Device needs 48-bit LBA commands but I can't issue them... Aborting."; return true; } - return false; + if(!_ataReadLba && + !_ataReadRetryLba && + !_ataReadDmaLba && + !_ataReadDmaRetryLba) + { + ErrorMessage = "Device needs 28-bit LBA commands but I can't issue them... Aborting."; + + return true; + } + } + else + { + if(!_ataRead && + !_ataReadRetry && + !_ataReadDma && + !_ataReadDmaRetry) + { + ErrorMessage = "Device needs CHS commands but I can't issue them... Aborting."; + + return true; + } } - bool AtaGetBlockSize() + if(_ataReadDmaLba48) + AaruConsole.WriteLine("Using ATA READ DMA EXT command."); + else if(_ataReadLba48) + AaruConsole.WriteLine("Using ATA READ EXT command."); + else if(_ataReadDmaRetryLba) + AaruConsole.WriteLine("Using ATA READ DMA command with retries (LBA)."); + else if(_ataReadDmaLba) + AaruConsole.WriteLine("Using ATA READ DMA command (LBA)."); + else if(_ataReadRetryLba) + AaruConsole.WriteLine("Using ATA READ command with retries (LBA)."); + else if(_ataReadLba) + AaruConsole.WriteLine("Using ATA READ command (LBA)."); + else if(_ataReadDmaRetry) + AaruConsole.WriteLine("Using ATA READ DMA command with retries (CHS)."); + else if(_ataReadDma) + AaruConsole.WriteLine("Using ATA READ DMA command (CHS)."); + else if(_ataReadRetry) + AaruConsole.WriteLine("Using ATA READ command with retries (CHS)."); + else if(_ataRead) + AaruConsole.WriteLine("Using ATA READ command (CHS)."); + else { - if((_ataId.PhysLogSectorSize & 0x8000) == 0x0000 && - (_ataId.PhysLogSectorSize & 0x4000) == 0x4000) - { - if((_ataId.PhysLogSectorSize & 0x1000) == 0x1000) - if(_ataId.LogicalSectorWords <= 255 || - _ataId.LogicalAlignment == 0xFFFF) - LogicalBlockSize = 512; - else - LogicalBlockSize = _ataId.LogicalSectorWords * 2; - else - LogicalBlockSize = 512; - - if((_ataId.PhysLogSectorSize & 0x2000) == 0x2000) - PhysicalBlockSize = LogicalBlockSize * (uint)Math.Pow(2, _ataId.PhysLogSectorSize & 0xF); - else - PhysicalBlockSize = LogicalBlockSize; - } - else - { - LogicalBlockSize = 512; - PhysicalBlockSize = 512; - } - - // TODO: ATA READ LONG - LongBlockSize = 0; - - return false; - } - - bool AtaGetBlocksToRead(uint startWithBlocks) - { - BlocksToRead = startWithBlocks; - - if(!IsLba) - { - BlocksToRead = 1; - - return false; - } - - bool error = true; - - while(IsLba) - { - byte[] cmdBuf; - bool sense; - AtaErrorRegistersLba48 errorLba48; - - if(_ataReadDmaLba48) - { - sense = _dev.ReadDma(out cmdBuf, out errorLba48, 0, (byte)BlocksToRead, _timeout, out _); - error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0); - } - else if(_ataReadLba48) - { - sense = _dev.Read(out cmdBuf, out errorLba48, 0, (byte)BlocksToRead, _timeout, out _); - error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0); - } - else - { - AtaErrorRegistersLba28 errorLba; - - if(_ataReadDmaRetryLba) - { - sense = _dev.ReadDma(out cmdBuf, out errorLba, true, 0, (byte)BlocksToRead, _timeout, out _); - error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0); - } - else if(_ataReadDmaLba) - { - sense = _dev.ReadDma(out cmdBuf, out errorLba, false, 0, (byte)BlocksToRead, _timeout, out _); - error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0); - } - else if(_ataReadRetryLba) - { - sense = _dev.Read(out cmdBuf, out errorLba, true, 0, (byte)BlocksToRead, _timeout, out _); - error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0); - } - else if(_ataReadLba) - { - sense = _dev.Read(out cmdBuf, out errorLba, false, 0, (byte)BlocksToRead, _timeout, out _); - error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0); - } - } - - if(error) - BlocksToRead /= 2; - - if(!error || - BlocksToRead == 1) - break; - } - - if(!error || - !IsLba) - return false; - - BlocksToRead = 1; - ErrorMessage = $"Device error {_dev.LastError} trying to guess ideal transfer length."; + ErrorMessage = "Could not get a working read command!"; return true; } - bool AtaReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, out bool recoveredError) + return false; + } + + bool AtaGetBlockSize() + { + if((_ataId.PhysLogSectorSize & 0x8000) == 0x0000 && + (_ataId.PhysLogSectorSize & 0x4000) == 0x4000) { - bool error = true; + if((_ataId.PhysLogSectorSize & 0x1000) == 0x1000) + if(_ataId.LogicalSectorWords <= 255 || + _ataId.LogicalAlignment == 0xFFFF) + LogicalBlockSize = 512; + else + LogicalBlockSize = _ataId.LogicalSectorWords * 2; + else + LogicalBlockSize = 512; + + if((_ataId.PhysLogSectorSize & 0x2000) == 0x2000) + PhysicalBlockSize = LogicalBlockSize * (uint)Math.Pow(2, _ataId.PhysLogSectorSize & 0xF); + else + PhysicalBlockSize = LogicalBlockSize; + } + else + { + LogicalBlockSize = 512; + PhysicalBlockSize = 512; + } + + // TODO: ATA READ LONG + LongBlockSize = 0; + + return false; + } + + bool AtaGetBlocksToRead(uint startWithBlocks) + { + BlocksToRead = startWithBlocks; + + if(!IsLba) + { + BlocksToRead = 1; + + return false; + } + + bool error = true; + + while(IsLba) + { + byte[] cmdBuf; bool sense; - AtaErrorRegistersLba28 errorLba; AtaErrorRegistersLba48 errorLba48; - byte status = 0, errorByte = 0; - buffer = null; - duration = 0; - recoveredError = false; if(_ataReadDmaLba48) { - sense = _dev.ReadDma(out buffer, out errorLba48, block, (byte)count, _timeout, out duration); - error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && buffer.Length > 0); - status = errorLba48.Status; - errorByte = errorLba48.Error; - - if(error) - _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba48); + sense = _dev.ReadDma(out cmdBuf, out errorLba48, 0, (byte)BlocksToRead, _timeout, out _); + error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0); } else if(_ataReadLba48) { - sense = _dev.Read(out buffer, out errorLba48, block, (byte)count, _timeout, out duration); - error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && buffer.Length > 0); - status = errorLba48.Status; - errorByte = errorLba48.Error; - - if(error) - _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba48); + sense = _dev.Read(out cmdBuf, out errorLba48, 0, (byte)BlocksToRead, _timeout, out _); + error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && cmdBuf.Length > 0); } - else if(_ataReadDmaRetryLba) + else { - sense = _dev.ReadDma(out buffer, out errorLba, true, (uint)block, (byte)count, _timeout, out duration); + AtaErrorRegistersLba28 errorLba; - error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0); - status = errorLba.Status; - errorByte = errorLba.Error; - - if(error) - _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); - } - else if(_ataReadDmaLba) - { - sense = _dev.ReadDma(out buffer, out errorLba, false, (uint)block, (byte)count, _timeout, out duration); - - error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0); - status = errorLba.Status; - errorByte = errorLba.Error; - - if(error) - _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); - } - else if(_ataReadRetryLba) - { - sense = _dev.Read(out buffer, out errorLba, true, (uint)block, (byte)count, _timeout, out duration); - error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0); - status = errorLba.Status; - errorByte = errorLba.Error; - - if(error) - _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); - } - else if(_ataReadLba) - { - sense = _dev.Read(out buffer, out errorLba, false, (uint)block, (byte)count, _timeout, out duration); - error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0); - status = errorLba.Status; - errorByte = errorLba.Error; - - if(error) - _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); + if(_ataReadDmaRetryLba) + { + sense = _dev.ReadDma(out cmdBuf, out errorLba, true, 0, (byte)BlocksToRead, _timeout, out _); + error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0); + } + else if(_ataReadDmaLba) + { + sense = _dev.ReadDma(out cmdBuf, out errorLba, false, 0, (byte)BlocksToRead, _timeout, out _); + error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0); + } + else if(_ataReadRetryLba) + { + sense = _dev.Read(out cmdBuf, out errorLba, true, 0, (byte)BlocksToRead, _timeout, out _); + error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0); + } + else if(_ataReadLba) + { + sense = _dev.Read(out cmdBuf, out errorLba, false, 0, (byte)BlocksToRead, _timeout, out _); + error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && cmdBuf.Length > 0); + } } - if(!error) - return false; + if(error) + BlocksToRead /= 2; - if((status & 0x04) == 0x04) - recoveredError = true; - - AaruConsole.DebugWriteLine("ATA Reader", "ATA ERROR: {0} STATUS: {1}", errorByte, status); - - return true; + if(!error || + BlocksToRead == 1) + break; } - bool AtaReadChs(out byte[] buffer, ushort cylinder, byte head, byte sector, out double duration, - out bool recoveredError) + if(!error || + !IsLba) + return false; + + BlocksToRead = 1; + ErrorMessage = $"Device error {_dev.LastError} trying to guess ideal transfer length."; + + return true; + } + + bool AtaReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, out bool recoveredError) + { + bool error = true; + bool sense; + AtaErrorRegistersLba28 errorLba; + AtaErrorRegistersLba48 errorLba48; + byte status = 0, errorByte = 0; + buffer = null; + duration = 0; + recoveredError = false; + + if(_ataReadDmaLba48) { - bool error = true; - bool sense; - AtaErrorRegistersChs errorChs; - byte status = 0, errorByte = 0; - buffer = null; - duration = 0; - recoveredError = false; + sense = _dev.ReadDma(out buffer, out errorLba48, block, (byte)count, _timeout, out duration); + error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && buffer.Length > 0); + status = errorLba48.Status; + errorByte = errorLba48.Error; - if(_ataReadDmaRetry) - { - sense = _dev.ReadDma(out buffer, out errorChs, true, cylinder, head, sector, 1, _timeout, out duration); - - error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0); - status = errorChs.Status; - errorByte = errorChs.Error; - - if(error) - _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); - } - else if(_ataReadDma) - { - sense = _dev.ReadDma(out buffer, out errorChs, false, cylinder, head, sector, 1, _timeout, - out duration); - - error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0); - status = errorChs.Status; - errorByte = errorChs.Error; - - if(error) - _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); - } - else if(_ataReadRetry) - { - sense = _dev.Read(out buffer, out errorChs, true, cylinder, head, sector, 1, _timeout, out duration); - error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0); - status = errorChs.Status; - errorByte = errorChs.Error; - - if(error) - _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); - } - else if(_ataRead) - { - sense = _dev.Read(out buffer, out errorChs, false, cylinder, head, sector, 1, _timeout, out duration); - error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0); - status = errorChs.Status; - errorByte = errorChs.Error; - - if(error) - _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); - } - - if(!error) - return false; - - if((status & 0x04) == 0x04) - recoveredError = true; - - AaruConsole.DebugWriteLine("ATA Reader", "ATA ERROR: {0} STATUS: {1}", errorByte, status); - - return true; + if(error) + _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba48); } - - bool AtaSeek(ulong block, out double duration) + else if(_ataReadLba48) { - bool sense = _dev.Seek(out AtaErrorRegistersLba28 errorLba, (uint)block, _timeout, out duration); + sense = _dev.Read(out buffer, out errorLba48, block, (byte)count, _timeout, out duration); + error = !(!sense && (errorLba48.Status & 0x27) == 0 && errorLba48.Error == 0 && buffer.Length > 0); + status = errorLba48.Status; + errorByte = errorLba48.Error; - if(!(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0)) + if(error) + _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba48); + } + else if(_ataReadDmaRetryLba) + { + sense = _dev.ReadDma(out buffer, out errorLba, true, (uint)block, (byte)count, _timeout, out duration); + + error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0); + status = errorLba.Status; + errorByte = errorLba.Error; + + if(error) _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); - - return !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0); } - - bool AtaSeekChs(ushort cylinder, byte head, byte sector, out double duration) + else if(_ataReadDmaLba) { - bool sense = _dev.Seek(out AtaErrorRegistersChs errorChs, cylinder, head, sector, _timeout, out duration); + sense = _dev.ReadDma(out buffer, out errorLba, false, (uint)block, (byte)count, _timeout, out duration); - if(!(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0)) - _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); + error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0); + status = errorLba.Status; + errorByte = errorLba.Error; - return !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0); + if(error) + _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); } + else if(_ataReadRetryLba) + { + sense = _dev.Read(out buffer, out errorLba, true, (uint)block, (byte)count, _timeout, out duration); + error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0); + status = errorLba.Status; + errorByte = errorLba.Error; + + if(error) + _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); + } + else if(_ataReadLba) + { + sense = _dev.Read(out buffer, out errorLba, false, (uint)block, (byte)count, _timeout, out duration); + error = !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0 && buffer.Length > 0); + status = errorLba.Status; + errorByte = errorLba.Error; + + if(error) + _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); + } + + if(!error) + return false; + + if((status & 0x04) == 0x04) + recoveredError = true; + + AaruConsole.DebugWriteLine("ATA Reader", "ATA ERROR: {0} STATUS: {1}", errorByte, status); + + return true; + } + + bool AtaReadChs(out byte[] buffer, ushort cylinder, byte head, byte sector, out double duration, + out bool recoveredError) + { + bool error = true; + bool sense; + AtaErrorRegistersChs errorChs; + byte status = 0, errorByte = 0; + buffer = null; + duration = 0; + recoveredError = false; + + if(_ataReadDmaRetry) + { + sense = _dev.ReadDma(out buffer, out errorChs, true, cylinder, head, sector, 1, _timeout, out duration); + + error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0); + status = errorChs.Status; + errorByte = errorChs.Error; + + if(error) + _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); + } + else if(_ataReadDma) + { + sense = _dev.ReadDma(out buffer, out errorChs, false, cylinder, head, sector, 1, _timeout, + out duration); + + error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0); + status = errorChs.Status; + errorByte = errorChs.Error; + + if(error) + _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); + } + else if(_ataReadRetry) + { + sense = _dev.Read(out buffer, out errorChs, true, cylinder, head, sector, 1, _timeout, out duration); + error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0); + status = errorChs.Status; + errorByte = errorChs.Error; + + if(error) + _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); + } + else if(_ataRead) + { + sense = _dev.Read(out buffer, out errorChs, false, cylinder, head, sector, 1, _timeout, out duration); + error = !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0 && buffer.Length > 0); + status = errorChs.Status; + errorByte = errorChs.Error; + + if(error) + _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); + } + + if(!error) + return false; + + if((status & 0x04) == 0x04) + recoveredError = true; + + AaruConsole.DebugWriteLine("ATA Reader", "ATA ERROR: {0} STATUS: {1}", errorByte, status); + + return true; + } + + bool AtaSeek(ulong block, out double duration) + { + bool sense = _dev.Seek(out AtaErrorRegistersLba28 errorLba, (uint)block, _timeout, out duration); + + if(!(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0)) + _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, errorLba); + + return !(!sense && (errorLba.Status & 0x27) == 0 && errorLba.Error == 0); + } + + bool AtaSeekChs(ushort cylinder, byte head, byte sector, out double duration) + { + bool sense = _dev.Seek(out AtaErrorRegistersChs errorChs, cylinder, head, sector, _timeout, out duration); + + if(!(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0)) + _errorLog?.WriteLine(cylinder, head, sector, _dev.Error, _dev.LastError, errorChs); + + return !(!sense && (errorChs.Status & 0x27) == 0 && errorChs.Error == 0); } } \ No newline at end of file diff --git a/Aaru.Core/Devices/ReaderSCSI.cs b/Aaru.Core/Devices/ReaderSCSI.cs index 29b0a4f0f..d2605b9ec 100644 --- a/Aaru.Core/Devices/ReaderSCSI.cs +++ b/Aaru.Core/Devices/ReaderSCSI.cs @@ -35,41 +35,75 @@ using Aaru.CommonTypes.Structs.Devices.SCSI; using Aaru.Console; using Aaru.Decoders.SCSI; -namespace Aaru.Core.Devices +namespace Aaru.Core.Devices; + +internal sealed partial class Reader { - internal sealed partial class Reader + // TODO: Raw reading + bool _hldtstReadRaw; + bool _plextorReadRaw; + bool _read10; + bool _read12; + bool _read16; + bool _read6; + bool _readLong10; + bool _readLong16; + bool _readLongDvd; + bool _seek10; + bool _seek6; + bool _syqReadLong10; + bool _syqReadLong6; + + ulong ScsiGetBlocks() => ScsiGetBlockSize() ? 0 : Blocks; + + bool ScsiFindReadCommand() { - // TODO: Raw reading - bool _hldtstReadRaw; - bool _plextorReadRaw; - bool _read10; - bool _read12; - bool _read16; - bool _read6; - bool _readLong10; - bool _readLong16; - bool _readLongDvd; - bool _seek10; - bool _seek6; - bool _syqReadLong10; - bool _syqReadLong6; + if(Blocks == 0) + GetDeviceBlocks(); - ulong ScsiGetBlocks() => ScsiGetBlockSize() ? 0 : Blocks; + if(Blocks == 0) + return true; - bool ScsiFindReadCommand() + byte[] senseBuf; + int tries = 0; + uint lba = 0; + bool mediumScan = false; + + if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) { - if(Blocks == 0) - GetDeviceBlocks(); + mediumScan = !_dev.MediumScan(out _, true, false, false, false, false, lba, 1, (uint)Blocks, + out uint foundLba, out _, _timeout, out _); - if(Blocks == 0) - return true; + if(mediumScan) + lba = foundLba; + } - byte[] senseBuf; - int tries = 0; - uint lba = 0; - bool mediumScan = false; + var rnd = new Random(); - if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) + while(tries < 10) + { + _read6 = !_dev.Read6(out _, out senseBuf, lba, LogicalBlockSize, _timeout, out _); + + _read10 = !_dev.Read10(out _, out senseBuf, 0, false, false, false, false, lba, LogicalBlockSize, 0, 1, + _timeout, out _); + + _read12 = !_dev.Read12(out _, out senseBuf, 0, false, false, false, false, lba, LogicalBlockSize, 0, 1, + false, _timeout, out _); + + _read16 = !_dev.Read16(out _, out senseBuf, 0, false, false, false, lba, LogicalBlockSize, 0, 1, false, + _timeout, out _); + + if(_read6 || + _read10 || + _read12 || + _read16) + { + break; + } + + lba = (uint)rnd.Next(1, (int)Blocks); + + if(mediumScan) { mediumScan = !_dev.MediumScan(out _, true, false, false, false, false, lba, 1, (uint)Blocks, out uint foundLba, out _, _timeout, out _); @@ -78,668 +112,633 @@ namespace Aaru.Core.Devices lba = foundLba; } - var rnd = new Random(); + tries++; + } - while(tries < 10) + if(!_read6 && + !_read10 && + !_read12 && + !_read16) + { + // Magneto-opticals may have empty LBA 0 but we know they work with READ(12) + if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) { - _read6 = !_dev.Read6(out _, out senseBuf, lba, LogicalBlockSize, _timeout, out _); - - _read10 = !_dev.Read10(out _, out senseBuf, 0, false, false, false, false, lba, LogicalBlockSize, 0, 1, - _timeout, out _); - - _read12 = !_dev.Read12(out _, out senseBuf, 0, false, false, false, false, lba, LogicalBlockSize, 0, 1, - false, _timeout, out _); - - _read16 = !_dev.Read16(out _, out senseBuf, 0, false, false, false, lba, LogicalBlockSize, 0, 1, false, - _timeout, out _); - - if(_read6 || - _read10 || - _read12 || - _read16) - { - break; - } - - lba = (uint)rnd.Next(1, (int)Blocks); - - if(mediumScan) - { - mediumScan = !_dev.MediumScan(out _, true, false, false, false, false, lba, 1, (uint)Blocks, - out uint foundLba, out _, _timeout, out _); - - if(mediumScan) - lba = foundLba; - } - - tries++; - } - - if(!_read6 && - !_read10 && - !_read12 && - !_read16) - { - // Magneto-opticals may have empty LBA 0 but we know they work with READ(12) - if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) - { - ErrorMessage = "Cannot read medium, aborting scan..."; - - return true; - } - - _read12 = true; - } - - if(_read6 && - !_read10 && - !_read12 && - !_read16 && - Blocks > 0x001FFFFF + 1) - { - ErrorMessage = - $"Device only supports SCSI READ (6) but has more than {0x001FFFFF + 1} blocks ({Blocks} blocks total)"; + ErrorMessage = "Cannot read medium, aborting scan..."; return true; } - if(Blocks > 0x001FFFFF + 1) - _read6 = false; + _read12 = true; + } - if(_read10) - _read12 = false; + if(_read6 && + !_read10 && + !_read12 && + !_read16 && + Blocks > 0x001FFFFF + 1) + { + ErrorMessage = + $"Device only supports SCSI READ (6) but has more than {0x001FFFFF + 1} blocks ({Blocks} blocks total)"; - if(!_read16 && - Blocks > 0xFFFFFFFF + (long)1) + return true; + } + + if(Blocks > 0x001FFFFF + 1) + _read6 = false; + + if(_read10) + _read12 = false; + + if(!_read16 && + Blocks > 0xFFFFFFFF + (long)1) + { + ErrorMessage = + $"Device only supports SCSI READ (10) but has more than {0xFFFFFFFF + (long)1} blocks ({Blocks} blocks total)"; + + return true; + } + + if(Blocks > 0xFFFFFFFF + (long)1) + { + _read10 = false; + _read12 = false; + } + + _seek6 = !_dev.Seek6(out senseBuf, lba, _timeout, out _); + + _seek10 = !_dev.Seek10(out senseBuf, lba, _timeout, out _); + + if(CanReadRaw) + { + bool testSense; + CanReadRaw = false; + + if(_dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) { - ErrorMessage = - $"Device only supports SCSI READ (10) but has more than {0xFFFFFFFF + (long)1} blocks ({Blocks} blocks total)"; - - return true; - } - - if(Blocks > 0xFFFFFFFF + (long)1) - { - _read10 = false; - _read12 = false; - } - - _seek6 = !_dev.Seek6(out senseBuf, lba, _timeout, out _); - - _seek10 = !_dev.Seek10(out senseBuf, lba, _timeout, out _); - - if(CanReadRaw) - { - bool testSense; - CanReadRaw = false; - - if(_dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) + /*testSense = dev.ReadLong16(out readBuffer, out senseBuf, false, 0, 0xFFFF, timeout, out duration); + if (testSense && !dev.Error) { - /*testSense = dev.ReadLong16(out readBuffer, out senseBuf, false, 0, 0xFFFF, timeout, out duration); - if (testSense && !dev.Error) + decSense = Decoders.SCSI.Sense.DecodeFixed(senseBuf); + if (decSense.HasValue) { - decSense = Decoders.SCSI.Sense.DecodeFixed(senseBuf); - if (decSense.HasValue) + if (decSense.Value.SenseKey == Aaru.Decoders.SCSI.SenseKeys.IllegalRequest && + decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { - if (decSense.Value.SenseKey == Aaru.Decoders.SCSI.SenseKeys.IllegalRequest && - decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) + readRaw = true; + if (decSense.Value.InformationValid && decSense.Value.ILI) { - readRaw = true; - if (decSense.Value.InformationValid && decSense.Value.ILI) - { - longBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); - readLong16 = !dev.ReadLong16(out readBuffer, out senseBuf, false, 0, longBlockSize, timeout, out duration); - } - } - } - }*/ - - testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 0xFFFF, _timeout, out _); - DecodedSense? decSense; - - if(testSense && !_dev.Error) - { - decSense = Sense.Decode(senseBuf); - - if(decSense?.SenseKey == SenseKeys.IllegalRequest && - decSense.Value.ASC == 0x24 && - decSense.Value.ASCQ == 0x00) - { - CanReadRaw = true; - - bool valid = decSense?.Fixed?.InformationValid == true; - bool ili = decSense?.Fixed?.ILI == true; - uint information = decSense?.Fixed?.Information ?? 0; - - if(decSense.Value.Descriptor.HasValue && - decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) - { - valid = true; - ili = true; - information = (uint)Sense.DecodeDescriptor00(desc00); - - if(decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] desc04)) - Sense.DecodeDescriptor04(desc04, out _, out _, out ili); - } - - if(valid && ili) - { - LongBlockSize = 0xFFFF - (information & 0xFFFF); - - _readLong10 = !_dev.ReadLong10(out _, out senseBuf, false, false, 0, - (ushort)LongBlockSize, _timeout, out _); + longBlockSize = 0xFFFF - (decSense.Value.Information & 0xFFFF); + readLong16 = !dev.ReadLong16(out readBuffer, out senseBuf, false, 0, longBlockSize, timeout, out duration); } } } + }*/ - if(CanReadRaw && LongBlockSize == LogicalBlockSize) - switch(LogicalBlockSize) - { - case 512: - { - foreach(ushort testSize in new ushort[] - { - // Long sector sizes for floppies - 514, + testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 0xFFFF, _timeout, out _); + DecodedSense? decSense; - // Long sector sizes for SuperDisk - 536, 558, + if(testSense && !_dev.Error) + { + decSense = Sense.Decode(senseBuf); - // Long sector sizes for 512-byte magneto-opticals - 600, 610, 630 - }) - { - testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, testSize, _timeout, - out _); - - if(!testSense && - !_dev.Error) - { - _readLong16 = true; - LongBlockSize = testSize; - CanReadRaw = true; - - break; - } - - testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, testSize, - _timeout, out _); - - if(testSense || _dev.Error) - continue; - - _readLong10 = true; - LongBlockSize = testSize; - CanReadRaw = true; - - break; - } - - break; - } - case 1024: - { - foreach(ushort testSize in new ushort[] - { - // Long sector sizes for floppies - 1026, - - // Long sector sizes for 1024-byte magneto-opticals - 1200 - }) - { - testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, testSize, _timeout, - out _); - - if(!testSense && - !_dev.Error) - { - _readLong16 = true; - LongBlockSize = testSize; - CanReadRaw = true; - - break; - } - - testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, testSize, - _timeout, out _); - - if(testSense || _dev.Error) - continue; - - _readLong10 = true; - LongBlockSize = testSize; - CanReadRaw = true; - - break; - } - - break; - } - case 2048: - { - testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 2380, _timeout, out _); - - if(!testSense && - !_dev.Error) - { - _readLong16 = true; - LongBlockSize = 2380; - CanReadRaw = true; - } - else - { - testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 2380, _timeout, - out _); - - if(!testSense && - !_dev.Error) - { - _readLong10 = true; - LongBlockSize = 2380; - CanReadRaw = true; - } - } - - break; - } - case 4096: - { - testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 4760, _timeout, out _); - - if(!testSense && - !_dev.Error) - { - _readLong16 = true; - LongBlockSize = 4760; - CanReadRaw = true; - } - else - { - testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 4760, _timeout, - out _); - - if(!testSense && - !_dev.Error) - { - _readLong10 = true; - LongBlockSize = 4760; - CanReadRaw = true; - } - } - - break; - } - case 8192: - { - testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 9424, _timeout, out _); - - if(!testSense && - !_dev.Error) - { - _readLong16 = true; - LongBlockSize = 9424; - CanReadRaw = true; - } - else - { - testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 9424, _timeout, - out _); - - if(!testSense && - !_dev.Error) - { - _readLong10 = true; - LongBlockSize = 9424; - CanReadRaw = true; - } - } - - break; - } - } - - if(!CanReadRaw && - _dev.Manufacturer == "SYQUEST") + if(decSense?.SenseKey == SenseKeys.IllegalRequest && + decSense.Value.ASC == 0x24 && + decSense.Value.ASCQ == 0x00) { - testSense = _dev.SyQuestReadLong10(out _, out senseBuf, 0, 0xFFFF, _timeout, out _); + CanReadRaw = true; - if(testSense) + bool valid = decSense?.Fixed?.InformationValid == true; + bool ili = decSense?.Fixed?.ILI == true; + uint information = decSense?.Fixed?.Information ?? 0; + + if(decSense.Value.Descriptor.HasValue && + decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) { - decSense = Sense.Decode(senseBuf); + valid = true; + ili = true; + information = (uint)Sense.DecodeDescriptor00(desc00); - if(decSense.HasValue) - if(decSense.Value.SenseKey == SenseKeys.IllegalRequest && - decSense.Value.ASC == 0x24 && - decSense.Value.ASCQ == 0x00) - { - CanReadRaw = true; - - bool valid = decSense?.Fixed?.InformationValid == true; - bool ili = decSense?.Fixed?.ILI == true; - uint information = decSense?.Fixed?.Information ?? 0; - - if(decSense.Value.Descriptor.HasValue && - decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) - { - valid = true; - ili = true; - information = (uint)Sense.DecodeDescriptor00(desc00); - - if(decSense.Value.Descriptor.Value.Descriptors. - TryGetValue(4, out byte[] desc04)) - Sense.DecodeDescriptor04(desc04, out _, out _, out ili); - } - - if(valid && ili) - { - LongBlockSize = 0xFFFF - (information & 0xFFFF); - - _syqReadLong10 = - !_dev.SyQuestReadLong10(out _, out senseBuf, 0, LongBlockSize, _timeout, - out _); - } - } - else - { - testSense = _dev.SyQuestReadLong6(out _, out senseBuf, 0, 0xFFFF, _timeout, out _); - - if(testSense) - { - decSense = Sense.Decode(senseBuf); - - if(decSense?.SenseKey == SenseKeys.IllegalRequest && - decSense.Value.ASC == 0x24 && - decSense.Value.ASCQ == 0x00) - { - CanReadRaw = true; - - bool valid = decSense?.Fixed?.InformationValid == true; - bool ili = decSense?.Fixed?.ILI == true; - uint information = decSense?.Fixed?.Information ?? 0; - - if(decSense.Value.Descriptor.HasValue && - decSense.Value.Descriptor.Value.Descriptors. - TryGetValue(0, out byte[] desc00)) - { - valid = true; - ili = true; - information = (uint)Sense.DecodeDescriptor00(desc00); - - if(decSense.Value.Descriptor.Value.Descriptors. - TryGetValue(4, out byte[] desc04)) - Sense.DecodeDescriptor04(desc04, out _, out _, out ili); - } - - if(valid && ili) - { - LongBlockSize = 0xFFFF - (information & 0xFFFF); - - _syqReadLong6 = - !_dev.SyQuestReadLong6(out _, out senseBuf, 0, LongBlockSize, - _timeout, out _); - } - } - } - } + if(decSense.Value.Descriptor.Value.Descriptors.TryGetValue(4, out byte[] desc04)) + Sense.DecodeDescriptor04(desc04, out _, out _, out ili); } - if(!CanReadRaw && - LogicalBlockSize == 256) + if(valid && ili) { - testSense = _dev.SyQuestReadLong6(out _, out senseBuf, 0, 262, _timeout, out _); + LongBlockSize = 0xFFFF - (information & 0xFFFF); + + _readLong10 = !_dev.ReadLong10(out _, out senseBuf, false, false, 0, + (ushort)LongBlockSize, _timeout, out _); + } + } + } + + if(CanReadRaw && LongBlockSize == LogicalBlockSize) + switch(LogicalBlockSize) + { + case 512: + { + foreach(ushort testSize in new ushort[] + { + // Long sector sizes for floppies + 514, + + // Long sector sizes for SuperDisk + 536, 558, + + // Long sector sizes for 512-byte magneto-opticals + 600, 610, 630 + }) + { + testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, testSize, _timeout, + out _); + + if(!testSense && + !_dev.Error) + { + _readLong16 = true; + LongBlockSize = testSize; + CanReadRaw = true; + + break; + } + + testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, testSize, + _timeout, out _); + + if(testSense || _dev.Error) + continue; + + _readLong10 = true; + LongBlockSize = testSize; + CanReadRaw = true; + + break; + } + + break; + } + case 1024: + { + foreach(ushort testSize in new ushort[] + { + // Long sector sizes for floppies + 1026, + + // Long sector sizes for 1024-byte magneto-opticals + 1200 + }) + { + testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, testSize, _timeout, + out _); + + if(!testSense && + !_dev.Error) + { + _readLong16 = true; + LongBlockSize = testSize; + CanReadRaw = true; + + break; + } + + testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, testSize, + _timeout, out _); + + if(testSense || _dev.Error) + continue; + + _readLong10 = true; + LongBlockSize = testSize; + CanReadRaw = true; + + break; + } + + break; + } + case 2048: + { + testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 2380, _timeout, out _); if(!testSense && !_dev.Error) { - _syqReadLong6 = true; - LongBlockSize = 262; + _readLong16 = true; + LongBlockSize = 2380; CanReadRaw = true; } + else + { + testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 2380, _timeout, + out _); + + if(!testSense && + !_dev.Error) + { + _readLong10 = true; + LongBlockSize = 2380; + CanReadRaw = true; + } + } + + break; + } + case 4096: + { + testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 4760, _timeout, out _); + + if(!testSense && + !_dev.Error) + { + _readLong16 = true; + LongBlockSize = 4760; + CanReadRaw = true; + } + else + { + testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 4760, _timeout, + out _); + + if(!testSense && + !_dev.Error) + { + _readLong10 = true; + LongBlockSize = 4760; + CanReadRaw = true; + } + } + + break; + } + case 8192: + { + testSense = _dev.ReadLong16(out _, out senseBuf, false, 0, 9424, _timeout, out _); + + if(!testSense && + !_dev.Error) + { + _readLong16 = true; + LongBlockSize = 9424; + CanReadRaw = true; + } + else + { + testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 9424, _timeout, + out _); + + if(!testSense && + !_dev.Error) + { + _readLong10 = true; + LongBlockSize = 9424; + CanReadRaw = true; + } + } + + break; } } - } - else + + if(!CanReadRaw && + _dev.Manufacturer == "SYQUEST") { - switch(_dev.Manufacturer) + testSense = _dev.SyQuestReadLong10(out _, out senseBuf, 0, 0xFFFF, _timeout, out _); + + if(testSense) { - case "HL-DT-ST": - _hldtstReadRaw = !_dev.HlDtStReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _); + decSense = Sense.Decode(senseBuf); - break; - case "PLEXTOR": - _plextorReadRaw = !_dev.PlextorReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _); + if(decSense.HasValue) + if(decSense.Value.SenseKey == SenseKeys.IllegalRequest && + decSense.Value.ASC == 0x24 && + decSense.Value.ASCQ == 0x00) + { + CanReadRaw = true; - break; + bool valid = decSense?.Fixed?.InformationValid == true; + bool ili = decSense?.Fixed?.ILI == true; + uint information = decSense?.Fixed?.Information ?? 0; + + if(decSense.Value.Descriptor.HasValue && + decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) + { + valid = true; + ili = true; + information = (uint)Sense.DecodeDescriptor00(desc00); + + if(decSense.Value.Descriptor.Value.Descriptors. + TryGetValue(4, out byte[] desc04)) + Sense.DecodeDescriptor04(desc04, out _, out _, out ili); + } + + if(valid && ili) + { + LongBlockSize = 0xFFFF - (information & 0xFFFF); + + _syqReadLong10 = + !_dev.SyQuestReadLong10(out _, out senseBuf, 0, LongBlockSize, _timeout, + out _); + } + } + else + { + testSense = _dev.SyQuestReadLong6(out _, out senseBuf, 0, 0xFFFF, _timeout, out _); + + if(testSense) + { + decSense = Sense.Decode(senseBuf); + + if(decSense?.SenseKey == SenseKeys.IllegalRequest && + decSense.Value.ASC == 0x24 && + decSense.Value.ASCQ == 0x00) + { + CanReadRaw = true; + + bool valid = decSense?.Fixed?.InformationValid == true; + bool ili = decSense?.Fixed?.ILI == true; + uint information = decSense?.Fixed?.Information ?? 0; + + if(decSense.Value.Descriptor.HasValue && + decSense.Value.Descriptor.Value.Descriptors. + TryGetValue(0, out byte[] desc00)) + { + valid = true; + ili = true; + information = (uint)Sense.DecodeDescriptor00(desc00); + + if(decSense.Value.Descriptor.Value.Descriptors. + TryGetValue(4, out byte[] desc04)) + Sense.DecodeDescriptor04(desc04, out _, out _, out ili); + } + + if(valid && ili) + { + LongBlockSize = 0xFFFF - (information & 0xFFFF); + + _syqReadLong6 = + !_dev.SyQuestReadLong6(out _, out senseBuf, 0, LongBlockSize, + _timeout, out _); + } + } + } + } } - if(_hldtstReadRaw || _plextorReadRaw) - { - CanReadRaw = true; - LongBlockSize = 2064; - } - - // READ LONG (10) for some DVD drives if(!CanReadRaw && - _dev.Manufacturer == "MATSHITA") + LogicalBlockSize == 256) { - testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 37856, _timeout, out _); + testSense = _dev.SyQuestReadLong6(out _, out senseBuf, 0, 262, _timeout, out _); if(!testSense && !_dev.Error) { - _readLongDvd = true; - LongBlockSize = 37856; + _syqReadLong6 = true; + LongBlockSize = 262; CanReadRaw = true; } } } } - - if(CanReadRaw) + else { - if(_readLong16) - AaruConsole.WriteLine("Using SCSI READ LONG (16) command."); - else if(_readLong10 || _readLongDvd) - AaruConsole.WriteLine("Using SCSI READ LONG (10) command."); - else if(_syqReadLong10) - AaruConsole.WriteLine("Using SyQuest READ LONG (10) command."); - else if(_syqReadLong6) - AaruConsole.WriteLine("Using SyQuest READ LONG (6) command."); - else if(_hldtstReadRaw) - AaruConsole.WriteLine("Using HL-DT-ST raw DVD reading."); - else if(_plextorReadRaw) - AaruConsole.WriteLine("Using Plextor raw DVD reading."); - } - else if(_read6) - AaruConsole.WriteLine("Using SCSI READ (6) command."); - else if(_read10) - AaruConsole.WriteLine("Using SCSI READ (10) command."); - else if(_read12) - AaruConsole.WriteLine("Using SCSI READ (12) command."); - else if(_read16) - AaruConsole.WriteLine("Using SCSI READ (16) command."); + switch(_dev.Manufacturer) + { + case "HL-DT-ST": + _hldtstReadRaw = !_dev.HlDtStReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _); - return false; + break; + case "PLEXTOR": + _plextorReadRaw = !_dev.PlextorReadRawDvd(out _, out senseBuf, 0, 1, _timeout, out _); + + break; + } + + if(_hldtstReadRaw || _plextorReadRaw) + { + CanReadRaw = true; + LongBlockSize = 2064; + } + + // READ LONG (10) for some DVD drives + if(!CanReadRaw && + _dev.Manufacturer == "MATSHITA") + { + testSense = _dev.ReadLong10(out _, out senseBuf, false, false, 0, 37856, _timeout, out _); + + if(!testSense && + !_dev.Error) + { + _readLongDvd = true; + LongBlockSize = 37856; + CanReadRaw = true; + } + } + } } - bool ScsiGetBlockSize() + if(CanReadRaw) { - Blocks = 0; + if(_readLong16) + AaruConsole.WriteLine("Using SCSI READ LONG (16) command."); + else if(_readLong10 || _readLongDvd) + AaruConsole.WriteLine("Using SCSI READ LONG (10) command."); + else if(_syqReadLong10) + AaruConsole.WriteLine("Using SyQuest READ LONG (10) command."); + else if(_syqReadLong6) + AaruConsole.WriteLine("Using SyQuest READ LONG (6) command."); + else if(_hldtstReadRaw) + AaruConsole.WriteLine("Using HL-DT-ST raw DVD reading."); + else if(_plextorReadRaw) + AaruConsole.WriteLine("Using Plextor raw DVD reading."); + } + else if(_read6) + AaruConsole.WriteLine("Using SCSI READ (6) command."); + else if(_read10) + AaruConsole.WriteLine("Using SCSI READ (10) command."); + else if(_read12) + AaruConsole.WriteLine("Using SCSI READ (12) command."); + else if(_read16) + AaruConsole.WriteLine("Using SCSI READ (16) command."); - bool sense = _dev.ReadCapacity(out byte[] cmdBuf, out byte[] senseBuf, _timeout, out _); + return false; + } + + bool ScsiGetBlockSize() + { + Blocks = 0; + + bool sense = _dev.ReadCapacity(out byte[] cmdBuf, out byte[] senseBuf, _timeout, out _); + + if(!sense) + { + Blocks = (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & 0xFFFFFFFF; + LogicalBlockSize = (uint)((cmdBuf[4] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); + } + + if(sense || Blocks == 0xFFFFFFFF) + { + sense = _dev.ReadCapacity16(out cmdBuf, out senseBuf, _timeout, out _); + + if(sense && Blocks == 0) + if(_dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) + { + ErrorMessage = "Unable to get media capacity\n" + $"{Sense.PrettifySense(senseBuf)}"; + + return true; + } if(!sense) { - Blocks = (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & 0xFFFFFFFF; - LogicalBlockSize = (uint)((cmdBuf[4] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); - } + byte[] temp = new byte[8]; - if(sense || Blocks == 0xFFFFFFFF) + Array.Copy(cmdBuf, 0, temp, 0, 8); + Array.Reverse(temp); + Blocks = BitConverter.ToUInt64(temp, 0); + LogicalBlockSize = (uint)((cmdBuf[8] << 24) + (cmdBuf[9] << 16) + (cmdBuf[10] << 8) + cmdBuf[11]); + } + } + + PhysicalBlockSize = LogicalBlockSize; + LongBlockSize = LogicalBlockSize; + + return false; + } + + bool ScsiGetBlocksToRead(uint startWithBlocks) + { + BlocksToRead = startWithBlocks; + + while(true) + { + if(_read6) { - sense = _dev.ReadCapacity16(out cmdBuf, out senseBuf, _timeout, out _); + _dev.Read6(out _, out _, 0, LogicalBlockSize, (byte)BlocksToRead, _timeout, out _); - if(sense && Blocks == 0) - if(_dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) - { - ErrorMessage = "Unable to get media capacity\n" + $"{Sense.PrettifySense(senseBuf)}"; + if(_dev.Error) + BlocksToRead /= 2; + } + else if(_read10) + { + _dev.Read10(out _, out _, 0, false, true, false, false, 0, LogicalBlockSize, 0, + (ushort)BlocksToRead, _timeout, out _); - return true; - } + if(_dev.Error) + BlocksToRead /= 2; + } + else if(_read12) + { + _dev.Read12(out _, out _, 0, false, false, false, false, 0, LogicalBlockSize, 0, BlocksToRead, + false, _timeout, out _); - if(!sense) - { - byte[] temp = new byte[8]; + if(_dev.Error) + BlocksToRead /= 2; + } + else if(_read16) + { + _dev.Read16(out _, out _, 0, false, true, false, 0, LogicalBlockSize, 0, BlocksToRead, false, + _timeout, out _); - Array.Copy(cmdBuf, 0, temp, 0, 8); - Array.Reverse(temp); - Blocks = BitConverter.ToUInt64(temp, 0); - LogicalBlockSize = (uint)((cmdBuf[8] << 24) + (cmdBuf[9] << 16) + (cmdBuf[10] << 8) + cmdBuf[11]); - } + if(_dev.Error) + BlocksToRead /= 2; } - PhysicalBlockSize = LogicalBlockSize; - LongBlockSize = LogicalBlockSize; + if(!_dev.Error || + BlocksToRead == 1) + break; + } + + if(!_dev.Error) + return false; + + // Magneto-opticals may have LBA 0 empty, we can hard code the value to a safe one + if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) + { + BlocksToRead = 16; return false; } - bool ScsiGetBlocksToRead(uint startWithBlocks) - { - BlocksToRead = startWithBlocks; + BlocksToRead = 1; + ErrorMessage = $"Device error {_dev.LastError} trying to guess ideal transfer length."; - while(true) - { - if(_read6) - { - _dev.Read6(out _, out _, 0, LogicalBlockSize, (byte)BlocksToRead, _timeout, out _); + return true; + } - if(_dev.Error) - BlocksToRead /= 2; - } - else if(_read10) - { - _dev.Read10(out _, out _, 0, false, true, false, false, 0, LogicalBlockSize, 0, - (ushort)BlocksToRead, _timeout, out _); + bool ScsiReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, out bool recoveredError, + out bool blankCheck) + { + bool sense; + byte[] senseBuf; + buffer = null; + duration = 0; + recoveredError = false; + blankCheck = false; - if(_dev.Error) - BlocksToRead /= 2; - } - else if(_read12) - { - _dev.Read12(out _, out _, 0, false, false, false, false, 0, LogicalBlockSize, 0, BlocksToRead, - false, _timeout, out _); - - if(_dev.Error) - BlocksToRead /= 2; - } - else if(_read16) - { - _dev.Read16(out _, out _, 0, false, true, false, 0, LogicalBlockSize, 0, BlocksToRead, false, - _timeout, out _); - - if(_dev.Error) - BlocksToRead /= 2; - } - - if(!_dev.Error || - BlocksToRead == 1) - break; - } - - if(!_dev.Error) - return false; - - // Magneto-opticals may have LBA 0 empty, we can hard code the value to a safe one - if(_dev.ScsiType == PeripheralDeviceTypes.OpticalDevice) - { - BlocksToRead = 16; - - return false; - } - - BlocksToRead = 1; - ErrorMessage = $"Device error {_dev.LastError} trying to guess ideal transfer length."; - - return true; - } - - bool ScsiReadBlocks(out byte[] buffer, ulong block, uint count, out double duration, out bool recoveredError, - out bool blankCheck) - { - bool sense; - byte[] senseBuf; - buffer = null; - duration = 0; - recoveredError = false; - blankCheck = false; - - if(CanReadRaw) - if(_readLong16) - sense = _dev.ReadLong16(out buffer, out senseBuf, false, block, LongBlockSize, _timeout, - out duration); - else if(_readLong10) - sense = _dev.ReadLong10(out buffer, out senseBuf, false, false, (uint)block, (ushort)LongBlockSize, - _timeout, out duration); - else if(_syqReadLong10) - sense = _dev.SyQuestReadLong10(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, - out duration); - else if(_syqReadLong6) - sense = _dev.SyQuestReadLong6(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, - out duration); - else if(_hldtstReadRaw) - sense = _dev.HlDtStReadRawDvd(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, - out duration); - else if(_plextorReadRaw) - sense = _dev.PlextorReadRawDvd(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, - out duration); - else - return true; + if(CanReadRaw) + if(_readLong16) + sense = _dev.ReadLong16(out buffer, out senseBuf, false, block, LongBlockSize, _timeout, + out duration); + else if(_readLong10) + sense = _dev.ReadLong10(out buffer, out senseBuf, false, false, (uint)block, (ushort)LongBlockSize, + _timeout, out duration); + else if(_syqReadLong10) + sense = _dev.SyQuestReadLong10(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, + out duration); + else if(_syqReadLong6) + sense = _dev.SyQuestReadLong6(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, + out duration); + else if(_hldtstReadRaw) + sense = _dev.HlDtStReadRawDvd(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, + out duration); + else if(_plextorReadRaw) + sense = _dev.PlextorReadRawDvd(out buffer, out senseBuf, (uint)block, LongBlockSize, _timeout, + out duration); else - { - if(_read6) - sense = _dev.Read6(out buffer, out senseBuf, (uint)block, LogicalBlockSize, (byte)count, _timeout, - out duration); - else if(_read10) - sense = _dev.Read10(out buffer, out senseBuf, 0, false, false, false, false, (uint)block, - LogicalBlockSize, 0, (ushort)count, _timeout, out duration); - else if(_read12) - sense = _dev.Read12(out buffer, out senseBuf, 0, false, false, false, false, (uint)block, - LogicalBlockSize, 0, count, false, _timeout, out duration); - else if(_read16) - sense = _dev.Read16(out buffer, out senseBuf, 0, false, false, false, block, LogicalBlockSize, 0, - count, false, _timeout, out duration); - else - return true; - } - - if(sense || _dev.Error) - _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, senseBuf); - - if(!sense && - !_dev.Error) - return false; - - recoveredError = Sense.Decode(senseBuf)?.SenseKey == SenseKeys.RecoveredError; - - blankCheck = Sense.Decode(senseBuf)?.SenseKey == SenseKeys.BlankCheck; - - AaruConsole.DebugWriteLine("SCSI Reader", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); - - return sense; - } - - bool ScsiSeek(ulong block, out double duration) + return true; + else { - bool sense = true; - duration = 0; - - if(_seek6) - sense = _dev.Seek6(out _, (uint)block, _timeout, out duration); - else if(_seek10) - sense = _dev.Seek10(out _, (uint)block, _timeout, out duration); - - return sense; + if(_read6) + sense = _dev.Read6(out buffer, out senseBuf, (uint)block, LogicalBlockSize, (byte)count, _timeout, + out duration); + else if(_read10) + sense = _dev.Read10(out buffer, out senseBuf, 0, false, false, false, false, (uint)block, + LogicalBlockSize, 0, (ushort)count, _timeout, out duration); + else if(_read12) + sense = _dev.Read12(out buffer, out senseBuf, 0, false, false, false, false, (uint)block, + LogicalBlockSize, 0, count, false, _timeout, out duration); + else if(_read16) + sense = _dev.Read16(out buffer, out senseBuf, 0, false, false, false, block, LogicalBlockSize, 0, + count, false, _timeout, out duration); + else + return true; } + + if(sense || _dev.Error) + _errorLog?.WriteLine(block, _dev.Error, _dev.LastError, senseBuf); + + if(!sense && + !_dev.Error) + return false; + + recoveredError = Sense.Decode(senseBuf)?.SenseKey == SenseKeys.RecoveredError; + + blankCheck = Sense.Decode(senseBuf)?.SenseKey == SenseKeys.BlankCheck; + + AaruConsole.DebugWriteLine("SCSI Reader", "READ error:\n{0}", Sense.PrettifySense(senseBuf)); + + return sense; + } + + bool ScsiSeek(ulong block, out double duration) + { + bool sense = true; + duration = 0; + + if(_seek6) + sense = _dev.Seek6(out _, (uint)block, _timeout, out duration); + else if(_seek10) + sense = _dev.Seek10(out _, (uint)block, _timeout, out duration); + + return sense; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/ATA.cs b/Aaru.Core/Devices/Report/ATA.cs index 4c03b7c41..85d5a2168 100644 --- a/Aaru.Core/Devices/Report/ATA.cs +++ b/Aaru.Core/Devices/Report/ATA.cs @@ -38,492 +38,98 @@ using Aaru.Devices; using Spectre.Console; using Identify = Aaru.CommonTypes.Structs.Devices.ATA.Identify; -namespace Aaru.Core.Devices.Report +namespace Aaru.Core.Devices.Report; + +public sealed partial class DeviceReport { - public sealed partial class DeviceReport + /// Creates a report for media inserted into an ATA device + /// Media report + public TestedMedia ReportAtaMedia() { - /// Creates a report for media inserted into an ATA device - /// Media report - public TestedMedia ReportAtaMedia() + bool sense = true; + AtaErrorRegistersChs errorChs = new(); + AtaErrorRegistersLba28 errorLba = new(); + AtaErrorRegistersLba48 errorLba48 = new(); + byte[] buffer = Array.Empty(); + byte[] readBuf = Array.Empty(); + + var mediaTest = new TestedMedia { - bool sense = true; - AtaErrorRegistersChs errorChs = new(); - AtaErrorRegistersLba28 errorLba = new(); - AtaErrorRegistersLba48 errorLba48 = new(); - byte[] buffer = Array.Empty(); - byte[] readBuf = Array.Empty(); + MediumTypeName = + AnsiConsole.Ask("Please write a description of the media type and press enter: "), + Model = AnsiConsole.Ask("Please write the media model and press enter: "), + MediaIsRecognized = true + }; - var mediaTest = new TestedMedia - { - MediumTypeName = - AnsiConsole.Ask("Please write a description of the media type and press enter: "), - Model = AnsiConsole.Ask("Please write the media model and press enter: "), - MediaIsRecognized = true - }; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying ATA IDENTIFY...").IsIndeterminate(); - _dev.AtaIdentify(out buffer, out _, _dev.Timeout, out _); - }); - - mediaTest.IdentifyData = ClearIdentify(buffer); - mediaTest.IdentifyDevice = Identify.Decode(buffer); - - if(mediaTest.IdentifyDevice.HasValue) - { - Identify.IdentifyDevice ataId = mediaTest.IdentifyDevice.Value; - - if(ataId.UnformattedBPT != 0) - mediaTest.UnformattedBPT = ataId.UnformattedBPT; - - if(ataId.UnformattedBPS != 0) - mediaTest.UnformattedBPS = ataId.UnformattedBPS; - - if(ataId.Cylinders > 0 && - ataId.Heads > 0 && - ataId.SectorsPerTrack > 0) - { - mediaTest.CHS = new Chs - { - Cylinders = ataId.Cylinders, - Heads = ataId.Heads, - Sectors = ataId.SectorsPerTrack - }; - - mediaTest.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack); - } - - if(ataId.CurrentCylinders > 0 && - ataId.CurrentHeads > 0 && - ataId.CurrentSectorsPerTrack > 0) - { - mediaTest.CurrentCHS = new Chs - { - Cylinders = ataId.CurrentCylinders, - Heads = ataId.CurrentHeads, - Sectors = ataId.CurrentSectorsPerTrack - }; - - if(mediaTest.Blocks == 0) - mediaTest.Blocks = - (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack); - } - - if(ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport)) - { - mediaTest.LBASectors = ataId.LBASectors; - mediaTest.Blocks = ataId.LBASectors; - } - - if(ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48)) - { - mediaTest.LBA48Sectors = ataId.LBA48Sectors; - mediaTest.Blocks = ataId.LBA48Sectors; - } - - if(ataId.NominalRotationRate != 0x0000 && - ataId.NominalRotationRate != 0xFFFF) - if(ataId.NominalRotationRate == 0x0001) - mediaTest.SolidStateDevice = true; - else - { - mediaTest.SolidStateDevice = false; - mediaTest.NominalRotationRate = ataId.NominalRotationRate; - } - - uint logicalSectorSize; - uint physicalSectorSize; - - if((ataId.PhysLogSectorSize & 0x8000) == 0x0000 && - (ataId.PhysLogSectorSize & 0x4000) == 0x4000) - { - if((ataId.PhysLogSectorSize & 0x1000) == 0x1000) - if(ataId.LogicalSectorWords <= 255 || - ataId.LogicalAlignment == 0xFFFF) - logicalSectorSize = 512; - else - logicalSectorSize = ataId.LogicalSectorWords * 2; - else - logicalSectorSize = 512; - - if((ataId.PhysLogSectorSize & 0x2000) == 0x2000) - physicalSectorSize = (uint)(logicalSectorSize * ((1 << ataId.PhysLogSectorSize) & 0xF)); - else - physicalSectorSize = logicalSectorSize; - } - else - { - logicalSectorSize = 512; - physicalSectorSize = 512; - } - - mediaTest.BlockSize = logicalSectorSize; - - if(physicalSectorSize != logicalSectorSize) - { - mediaTest.PhysicalBlockSize = physicalSectorSize; - - if((ataId.LogicalAlignment & 0x8000) == 0x0000 && - (ataId.LogicalAlignment & 0x4000) == 0x4000) - mediaTest.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF); - } - - if(ataId.EccBytes != 0x0000 && - ataId.EccBytes != 0xFFFF) - mediaTest.LongBlockSize = logicalSectorSize + ataId.EccBytes; - - if(ataId.UnformattedBPS > logicalSectorSize && - (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || mediaTest.LongBlockSize == 516)) - mediaTest.LongBlockSize = ataId.UnformattedBPS; - - if(ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeSet) && - !ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeClear) && - ataId.EnabledCommandSet3.HasFlag(Identify.CommandSetBit3.MediaSerial)) - { - mediaTest.CanReadMediaSerial = true; - - if(!string.IsNullOrWhiteSpace(ataId.MediaManufacturer)) - mediaTest.Manufacturer = ataId.MediaManufacturer; - } - - ulong checkCorrectRead = BitConverter.ToUInt64(buffer, 0); - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ SECTOR(S) in CHS mode...").IsIndeterminate(); - sense = _dev.Read(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadSectors = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadSectorsData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ SECTOR(S) RETRY in CHS mode...").IsIndeterminate(); - sense = _dev.Read(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadSectorsRetryData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ DMA in CHS mode...").IsIndeterminate(); - sense = _dev.ReadDma(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadDma = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadDmaData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ DMA RETRY in CHS mode...").IsIndeterminate(); - sense = _dev.ReadDma(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadDmaRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadDmaRetryData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SEEK in CHS mode...").IsIndeterminate(); - sense = _dev.Seek(out errorChs, 0, 0, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsSeek = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0; - - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense, - errorChs.Status, errorChs.Error); - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ SECTOR(S) in LBA mode...").IsIndeterminate(); - sense = _dev.Read(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadLbaData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ SECTOR(S) RETRY in LBA mode...").IsIndeterminate(); - sense = _dev.Read(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadRetryLbaData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ DMA in LBA mode...").IsIndeterminate(); - sense = _dev.ReadDma(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadDmaLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadDmaLbaData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ DMA RETRY in LBA mode...").IsIndeterminate(); - sense = _dev.ReadDma(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadDmaRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadDmaRetryLbaData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SEEK in LBA mode...").IsIndeterminate(); - sense = _dev.Seek(out errorLba, 0, _dev.Timeout, out _); - }); - - mediaTest.SupportsSeekLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0; - - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense, - errorChs.Status, errorChs.Error); - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ SECTOR(S) in LBA48 mode...").IsIndeterminate(); - sense = _dev.Read(out readBuf, out AtaErrorRegistersLba48 errorLba48, 0, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 && - readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadLba48Data = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ DMA in LBA48 mode...").IsIndeterminate(); - sense = _dev.ReadDma(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _); - }); - - mediaTest.SupportsReadDmaLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && - errorLba48.Error == 0 && readBuf.Length > 0; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadDmaLba48Data = readBuf; - - // Send SET FEATURES before sending READ LONG commands, retrieve IDENTIFY again and - // check if ECC size changed. Sector is set to 1 because without it most drives just return - // CORRECTABLE ERROR for this command. - _dev.SetFeatures(out _, AtaFeatures.EnableReadLongVendorLength, 0, 0, 1, 0, _dev.Timeout, out _); - - _dev.AtaIdentify(out buffer, out _, _dev.Timeout, out _); - - if(Identify.Decode(buffer).HasValue) - { - ataId = Identify.Decode(buffer).Value; - - if(ataId.EccBytes != 0x0000 && - ataId.EccBytes != 0xFFFF) - mediaTest.LongBlockSize = logicalSectorSize + ataId.EccBytes; - - if(ataId.UnformattedBPS > logicalSectorSize && - (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || mediaTest.LongBlockSize == 516)) - mediaTest.LongBlockSize = ataId.UnformattedBPS; - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ LONG in CHS mode...").IsIndeterminate(); - - sense = _dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, mediaTest.LongBlockSize ?? 0, - _dev.Timeout, out _); - }); - - mediaTest.SupportsReadLong = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0 && - BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadLongData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ LONG RETRY in CHS mode...").IsIndeterminate(); - - sense = _dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, mediaTest.LongBlockSize ?? 0, - _dev.Timeout, out _); - }); - - mediaTest.SupportsReadLongRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != - checkCorrectRead; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadLongRetryData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ LONG in LBA mode...").IsIndeterminate(); - - sense = _dev.ReadLong(out readBuf, out errorLba, false, 0, mediaTest.LongBlockSize ?? 0, - _dev.Timeout, out _); - }); - - mediaTest.SupportsReadLongLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0 && - BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadLongLbaData = readBuf; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying READ LONG RETRY in LBA mode...").IsIndeterminate(); - - sense = _dev.ReadLong(out readBuf, out errorLba, true, 0, mediaTest.LongBlockSize ?? 0, - _dev.Timeout, out _); - }); - - mediaTest.SupportsReadLongRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && - errorLba.Error == 0 && readBuf.Length > 0 && - BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; - - AaruConsole.DebugWriteLine("ATA Report", - "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, - errorChs.Status, errorChs.Error, readBuf.Length); - - mediaTest.ReadLongRetryLbaData = readBuf; - } - else - mediaTest.MediaIsRecognized = false; - - return mediaTest; - } - - /// Creates a report of an ATA device - public TestedMedia ReportAta(Identify.IdentifyDevice ataId) + Spectre.ProgressSingleSpinner(ctx => { - bool sense = true; - byte[] readBuf = Array.Empty(); - AtaErrorRegistersChs errorChs = new(); - AtaErrorRegistersLba28 errorLba = new(); - AtaErrorRegistersLba48 errorLba48 = new(); - var capabilities = new TestedMedia(); + ctx.AddTask("Querying ATA IDENTIFY...").IsIndeterminate(); + _dev.AtaIdentify(out buffer, out _, _dev.Timeout, out _); + }); + + mediaTest.IdentifyData = ClearIdentify(buffer); + mediaTest.IdentifyDevice = Identify.Decode(buffer); + + if(mediaTest.IdentifyDevice.HasValue) + { + Identify.IdentifyDevice ataId = mediaTest.IdentifyDevice.Value; if(ataId.UnformattedBPT != 0) - capabilities.UnformattedBPT = ataId.UnformattedBPT; + mediaTest.UnformattedBPT = ataId.UnformattedBPT; if(ataId.UnformattedBPS != 0) - capabilities.UnformattedBPS = ataId.UnformattedBPS; + mediaTest.UnformattedBPS = ataId.UnformattedBPS; if(ataId.Cylinders > 0 && ataId.Heads > 0 && ataId.SectorsPerTrack > 0) { - capabilities.CHS = new Chs + mediaTest.CHS = new Chs { Cylinders = ataId.Cylinders, Heads = ataId.Heads, Sectors = ataId.SectorsPerTrack }; - capabilities.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack); + mediaTest.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack); } if(ataId.CurrentCylinders > 0 && ataId.CurrentHeads > 0 && ataId.CurrentSectorsPerTrack > 0) { - capabilities.CurrentCHS = new Chs + mediaTest.CurrentCHS = new Chs { Cylinders = ataId.CurrentCylinders, Heads = ataId.CurrentHeads, Sectors = ataId.CurrentSectorsPerTrack }; - capabilities.Blocks = - (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack); + if(mediaTest.Blocks == 0) + mediaTest.Blocks = + (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack); } if(ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport)) { - capabilities.LBASectors = ataId.LBASectors; - capabilities.Blocks = ataId.LBASectors; + mediaTest.LBASectors = ataId.LBASectors; + mediaTest.Blocks = ataId.LBASectors; } if(ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48)) { - capabilities.LBA48Sectors = ataId.LBA48Sectors; - capabilities.Blocks = ataId.LBA48Sectors; + mediaTest.LBA48Sectors = ataId.LBA48Sectors; + mediaTest.Blocks = ataId.LBA48Sectors; } if(ataId.NominalRotationRate != 0x0000 && ataId.NominalRotationRate != 0xFFFF) if(ataId.NominalRotationRate == 0x0001) - capabilities.SolidStateDevice = true; + mediaTest.SolidStateDevice = true; else { - capabilities.SolidStateDevice = false; - capabilities.NominalRotationRate = ataId.NominalRotationRate; + mediaTest.SolidStateDevice = false; + mediaTest.NominalRotationRate = ataId.NominalRotationRate; } uint logicalSectorSize; @@ -542,7 +148,7 @@ namespace Aaru.Core.Devices.Report logicalSectorSize = 512; if((ataId.PhysLogSectorSize & 0x2000) == 0x2000) - physicalSectorSize = logicalSectorSize * (uint)Math.Pow(2, ataId.PhysLogSectorSize & 0xF); + physicalSectorSize = (uint)(logicalSectorSize * ((1 << ataId.PhysLogSectorSize) & 0xF)); else physicalSectorSize = logicalSectorSize; } @@ -552,36 +158,36 @@ namespace Aaru.Core.Devices.Report physicalSectorSize = 512; } - capabilities.BlockSize = logicalSectorSize; + mediaTest.BlockSize = logicalSectorSize; if(physicalSectorSize != logicalSectorSize) { - capabilities.PhysicalBlockSize = physicalSectorSize; + mediaTest.PhysicalBlockSize = physicalSectorSize; if((ataId.LogicalAlignment & 0x8000) == 0x0000 && (ataId.LogicalAlignment & 0x4000) == 0x4000) - capabilities.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF); + mediaTest.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF); } if(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) - capabilities.LongBlockSize = logicalSectorSize + ataId.EccBytes; + mediaTest.LongBlockSize = logicalSectorSize + ataId.EccBytes; if(ataId.UnformattedBPS > logicalSectorSize && - (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || capabilities.LongBlockSize == 516)) - capabilities.LongBlockSize = ataId.UnformattedBPS; + (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || mediaTest.LongBlockSize == 516)) + mediaTest.LongBlockSize = ataId.UnformattedBPS; if(ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeSet) && !ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeClear) && ataId.EnabledCommandSet3.HasFlag(Identify.CommandSetBit3.MediaSerial)) { - capabilities.CanReadMediaSerial = true; + mediaTest.CanReadMediaSerial = true; if(!string.IsNullOrWhiteSpace(ataId.MediaManufacturer)) - capabilities.Manufacturer = ataId.MediaManufacturer; + mediaTest.Manufacturer = ataId.MediaManufacturer; } - ulong checkCorrectRead = 0; + ulong checkCorrectRead = BitConverter.ToUInt64(buffer, 0); Spectre.ProgressSingleSpinner(ctx => { @@ -589,13 +195,14 @@ namespace Aaru.Core.Devices.Report sense = _dev.Read(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadSectors = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0; + mediaTest.SupportsReadSectors = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorChs.Status, errorChs.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadSectorsData = readBuf; + mediaTest.ReadSectorsData = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -603,13 +210,14 @@ namespace Aaru.Core.Devices.Report sense = _dev.Read(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadRetry = - !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && readBuf.Length > 0; + mediaTest.SupportsReadRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorChs.Status, errorChs.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadSectorsRetryData = readBuf; + mediaTest.ReadSectorsRetryData = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -617,13 +225,14 @@ namespace Aaru.Core.Devices.Report sense = _dev.ReadDma(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadDma = - !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && readBuf.Length > 0; + mediaTest.SupportsReadDma = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorChs.Status, errorChs.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadDmaData = readBuf; + mediaTest.ReadDmaData = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -631,13 +240,14 @@ namespace Aaru.Core.Devices.Report sense = _dev.ReadDma(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadDmaRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0; + mediaTest.SupportsReadDmaRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorChs.Status, errorChs.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadDmaRetryData = readBuf; + mediaTest.ReadDmaRetryData = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -645,7 +255,7 @@ namespace Aaru.Core.Devices.Report sense = _dev.Seek(out errorChs, 0, 0, 1, _dev.Timeout, out _); }); - capabilities.SupportsSeek = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0; + mediaTest.SupportsSeek = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0; AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense, errorChs.Status, errorChs.Error); @@ -656,13 +266,14 @@ namespace Aaru.Core.Devices.Report sense = _dev.Read(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadLba = - !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && readBuf.Length > 0; + mediaTest.SupportsReadLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorLba.Status, errorLba.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadLbaData = readBuf; + mediaTest.ReadLbaData = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -670,13 +281,14 @@ namespace Aaru.Core.Devices.Report sense = _dev.Read(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0; + mediaTest.SupportsReadRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorLba.Status, errorLba.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadRetryLbaData = readBuf; + mediaTest.ReadRetryLbaData = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -684,13 +296,14 @@ namespace Aaru.Core.Devices.Report sense = _dev.ReadDma(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadDmaLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0; + mediaTest.SupportsReadDmaLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorLba.Status, errorLba.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadDmaLbaData = readBuf; + mediaTest.ReadDmaLbaData = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -698,13 +311,14 @@ namespace Aaru.Core.Devices.Report sense = _dev.ReadDma(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadDmaRetryLba = - !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && readBuf.Length > 0; + mediaTest.SupportsReadDmaRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorLba.Status, errorLba.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadDmaRetryLbaData = readBuf; + mediaTest.ReadDmaRetryLbaData = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -712,24 +326,25 @@ namespace Aaru.Core.Devices.Report sense = _dev.Seek(out errorLba, 0, _dev.Timeout, out _); }); - capabilities.SupportsSeekLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0; + mediaTest.SupportsSeekLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0; AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense, - errorLba.Status, errorLba.Error); + errorChs.Status, errorChs.Error); Spectre.ProgressSingleSpinner(ctx => { ctx.AddTask("Trying READ SECTOR(S) in LBA48 mode...").IsIndeterminate(); - sense = _dev.Read(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _); + sense = _dev.Read(out readBuf, out AtaErrorRegistersLba48 errorLba48, 0, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 && - readBuf.Length > 0; + mediaTest.SupportsReadLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 && + readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorLba48.Status, errorLba48.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadLba48Data = readBuf; + mediaTest.ReadLba48Data = readBuf; Spectre.ProgressSingleSpinner(ctx => { @@ -737,20 +352,21 @@ namespace Aaru.Core.Devices.Report sense = _dev.ReadDma(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _); }); - capabilities.SupportsReadDmaLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 && - readBuf.Length > 0; + mediaTest.SupportsReadDmaLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && + errorLba48.Error == 0 && readBuf.Length > 0; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorLba48.Status, errorLba48.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadDmaLba48Data = readBuf; + mediaTest.ReadDmaLba48Data = readBuf; // Send SET FEATURES before sending READ LONG commands, retrieve IDENTIFY again and // check if ECC size changed. Sector is set to 1 because without it most drives just return // CORRECTABLE ERROR for this command. _dev.SetFeatures(out _, AtaFeatures.EnableReadLongVendorLength, 0, 0, 1, 0, _dev.Timeout, out _); - _dev.AtaIdentify(out byte[] buffer, out _, _dev.Timeout, out _); + _dev.AtaIdentify(out buffer, out _, _dev.Timeout, out _); if(Identify.Decode(buffer).HasValue) { @@ -758,96 +374,479 @@ namespace Aaru.Core.Devices.Report if(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) - capabilities.LongBlockSize = logicalSectorSize + ataId.EccBytes; + mediaTest.LongBlockSize = logicalSectorSize + ataId.EccBytes; if(ataId.UnformattedBPS > logicalSectorSize && - (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || capabilities.LongBlockSize == 516)) - capabilities.LongBlockSize = ataId.UnformattedBPS; + (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || mediaTest.LongBlockSize == 516)) + mediaTest.LongBlockSize = ataId.UnformattedBPS; } Spectre.ProgressSingleSpinner(ctx => { ctx.AddTask("Trying READ LONG in CHS mode...").IsIndeterminate(); - sense = _dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, capabilities.LongBlockSize ?? 0, + sense = _dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, mediaTest.LongBlockSize ?? 0, _dev.Timeout, out _); }); - capabilities.SupportsReadLong = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; + mediaTest.SupportsReadLong = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0 && + BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorChs.Status, errorChs.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadLongData = readBuf; + mediaTest.ReadLongData = readBuf; Spectre.ProgressSingleSpinner(ctx => { ctx.AddTask("Trying READ LONG RETRY in CHS mode...").IsIndeterminate(); - sense = _dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, capabilities.LongBlockSize ?? 0, + sense = _dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, mediaTest.LongBlockSize ?? 0, _dev.Timeout, out _); }); - capabilities.SupportsReadLongRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && - readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != - checkCorrectRead; + mediaTest.SupportsReadLongRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != + checkCorrectRead; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorChs.Status, errorChs.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadLongRetryData = readBuf; + mediaTest.ReadLongRetryData = readBuf; Spectre.ProgressSingleSpinner(ctx => { ctx.AddTask("Trying READ LONG in LBA mode...").IsIndeterminate(); - sense = _dev.ReadLong(out readBuf, out errorLba, false, 0, capabilities.LongBlockSize ?? 0, + sense = _dev.ReadLong(out readBuf, out errorLba, false, 0, mediaTest.LongBlockSize ?? 0, _dev.Timeout, out _); }); - capabilities.SupportsReadLongLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0 && - BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; + mediaTest.SupportsReadLongLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0 && + BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorLba.Status, errorLba.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadLongLbaData = readBuf; + mediaTest.ReadLongLbaData = readBuf; Spectre.ProgressSingleSpinner(ctx => { ctx.AddTask("Trying READ LONG RETRY in LBA mode...").IsIndeterminate(); - sense = _dev.ReadLong(out readBuf, out errorLba, true, 0, capabilities.LongBlockSize ?? 0, _dev.Timeout, - out _); + sense = _dev.ReadLong(out readBuf, out errorLba, true, 0, mediaTest.LongBlockSize ?? 0, + _dev.Timeout, out _); }); - capabilities.SupportsReadLongRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && - readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != - checkCorrectRead; + mediaTest.SupportsReadLongRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && + errorLba.Error == 0 && readBuf.Length > 0 && + BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; - AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", - sense, errorLba.Status, errorLba.Error, readBuf.Length); + AaruConsole.DebugWriteLine("ATA Report", + "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", sense, + errorChs.Status, errorChs.Error, readBuf.Length); - capabilities.ReadLongRetryLbaData = readBuf; - - return capabilities; + mediaTest.ReadLongRetryLbaData = readBuf; } + else + mediaTest.MediaIsRecognized = false; - /// Clear serial numbers and other private fields from an IDENTIFY ATA DEVICE response - /// IDENTIFY ATA DEVICE response - /// IDENTIFY ATA DEVICE response without the private fields - public static byte[] ClearIdentify(byte[] buffer) + return mediaTest; + } + + /// Creates a report of an ATA device + public TestedMedia ReportAta(Identify.IdentifyDevice ataId) + { + bool sense = true; + byte[] readBuf = Array.Empty(); + AtaErrorRegistersChs errorChs = new(); + AtaErrorRegistersLba28 errorLba = new(); + AtaErrorRegistersLba48 errorLba48 = new(); + var capabilities = new TestedMedia(); + + if(ataId.UnformattedBPT != 0) + capabilities.UnformattedBPT = ataId.UnformattedBPT; + + if(ataId.UnformattedBPS != 0) + capabilities.UnformattedBPS = ataId.UnformattedBPS; + + if(ataId.Cylinders > 0 && + ataId.Heads > 0 && + ataId.SectorsPerTrack > 0) { - byte[] empty = new byte[512]; + capabilities.CHS = new Chs + { + Cylinders = ataId.Cylinders, + Heads = ataId.Heads, + Sectors = ataId.SectorsPerTrack + }; - Array.Copy(empty, 0, buffer, 20, 20); - Array.Copy(empty, 0, buffer, 216, 8); - Array.Copy(empty, 0, buffer, 224, 8); - Array.Copy(empty, 0, buffer, 352, 40); - - return buffer; + capabilities.Blocks = (ulong)(ataId.Cylinders * ataId.Heads * ataId.SectorsPerTrack); } + + if(ataId.CurrentCylinders > 0 && + ataId.CurrentHeads > 0 && + ataId.CurrentSectorsPerTrack > 0) + { + capabilities.CurrentCHS = new Chs + { + Cylinders = ataId.CurrentCylinders, + Heads = ataId.CurrentHeads, + Sectors = ataId.CurrentSectorsPerTrack + }; + + capabilities.Blocks = + (ulong)(ataId.CurrentCylinders * ataId.CurrentHeads * ataId.CurrentSectorsPerTrack); + } + + if(ataId.Capabilities.HasFlag(Identify.CapabilitiesBit.LBASupport)) + { + capabilities.LBASectors = ataId.LBASectors; + capabilities.Blocks = ataId.LBASectors; + } + + if(ataId.CommandSet2.HasFlag(Identify.CommandSetBit2.LBA48)) + { + capabilities.LBA48Sectors = ataId.LBA48Sectors; + capabilities.Blocks = ataId.LBA48Sectors; + } + + if(ataId.NominalRotationRate != 0x0000 && + ataId.NominalRotationRate != 0xFFFF) + if(ataId.NominalRotationRate == 0x0001) + capabilities.SolidStateDevice = true; + else + { + capabilities.SolidStateDevice = false; + capabilities.NominalRotationRate = ataId.NominalRotationRate; + } + + uint logicalSectorSize; + uint physicalSectorSize; + + if((ataId.PhysLogSectorSize & 0x8000) == 0x0000 && + (ataId.PhysLogSectorSize & 0x4000) == 0x4000) + { + if((ataId.PhysLogSectorSize & 0x1000) == 0x1000) + if(ataId.LogicalSectorWords <= 255 || + ataId.LogicalAlignment == 0xFFFF) + logicalSectorSize = 512; + else + logicalSectorSize = ataId.LogicalSectorWords * 2; + else + logicalSectorSize = 512; + + if((ataId.PhysLogSectorSize & 0x2000) == 0x2000) + physicalSectorSize = logicalSectorSize * (uint)Math.Pow(2, ataId.PhysLogSectorSize & 0xF); + else + physicalSectorSize = logicalSectorSize; + } + else + { + logicalSectorSize = 512; + physicalSectorSize = 512; + } + + capabilities.BlockSize = logicalSectorSize; + + if(physicalSectorSize != logicalSectorSize) + { + capabilities.PhysicalBlockSize = physicalSectorSize; + + if((ataId.LogicalAlignment & 0x8000) == 0x0000 && + (ataId.LogicalAlignment & 0x4000) == 0x4000) + capabilities.LogicalAlignment = (ushort)(ataId.LogicalAlignment & 0x3FFF); + } + + if(ataId.EccBytes != 0x0000 && + ataId.EccBytes != 0xFFFF) + capabilities.LongBlockSize = logicalSectorSize + ataId.EccBytes; + + if(ataId.UnformattedBPS > logicalSectorSize && + (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || capabilities.LongBlockSize == 516)) + capabilities.LongBlockSize = ataId.UnformattedBPS; + + if(ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeSet) && + !ataId.CommandSet3.HasFlag(Identify.CommandSetBit3.MustBeClear) && + ataId.EnabledCommandSet3.HasFlag(Identify.CommandSetBit3.MediaSerial)) + { + capabilities.CanReadMediaSerial = true; + + if(!string.IsNullOrWhiteSpace(ataId.MediaManufacturer)) + capabilities.Manufacturer = ataId.MediaManufacturer; + } + + ulong checkCorrectRead = 0; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ SECTOR(S) in CHS mode...").IsIndeterminate(); + sense = _dev.Read(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadSectors = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorChs.Status, errorChs.Error, readBuf.Length); + + capabilities.ReadSectorsData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ SECTOR(S) RETRY in CHS mode...").IsIndeterminate(); + sense = _dev.Read(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadRetry = + !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorChs.Status, errorChs.Error, readBuf.Length); + + capabilities.ReadSectorsRetryData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ DMA in CHS mode...").IsIndeterminate(); + sense = _dev.ReadDma(out readBuf, out errorChs, false, 0, 0, 1, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadDma = + !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorChs.Status, errorChs.Error, readBuf.Length); + + capabilities.ReadDmaData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ DMA RETRY in CHS mode...").IsIndeterminate(); + sense = _dev.ReadDma(out readBuf, out errorChs, true, 0, 0, 1, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadDmaRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorChs.Status, errorChs.Error, readBuf.Length); + + capabilities.ReadDmaRetryData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SEEK in CHS mode...").IsIndeterminate(); + sense = _dev.Seek(out errorChs, 0, 0, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsSeek = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense, + errorChs.Status, errorChs.Error); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ SECTOR(S) in LBA mode...").IsIndeterminate(); + sense = _dev.Read(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadLba = + !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorLba.Status, errorLba.Error, readBuf.Length); + + capabilities.ReadLbaData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ SECTOR(S) RETRY in LBA mode...").IsIndeterminate(); + sense = _dev.Read(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorLba.Status, errorLba.Error, readBuf.Length); + + capabilities.ReadRetryLbaData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ DMA in LBA mode...").IsIndeterminate(); + sense = _dev.ReadDma(out readBuf, out errorLba, false, 0, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadDmaLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorLba.Status, errorLba.Error, readBuf.Length); + + capabilities.ReadDmaLbaData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ DMA RETRY in LBA mode...").IsIndeterminate(); + sense = _dev.ReadDma(out readBuf, out errorLba, true, 0, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadDmaRetryLba = + !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorLba.Status, errorLba.Error, readBuf.Length); + + capabilities.ReadDmaRetryLbaData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SEEK in LBA mode...").IsIndeterminate(); + sense = _dev.Seek(out errorLba, 0, _dev.Timeout, out _); + }); + + capabilities.SupportsSeekLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}", sense, + errorLba.Status, errorLba.Error); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ SECTOR(S) in LBA48 mode...").IsIndeterminate(); + sense = _dev.Read(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 && + readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorLba48.Status, errorLba48.Error, readBuf.Length); + + capabilities.ReadLba48Data = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ DMA in LBA48 mode...").IsIndeterminate(); + sense = _dev.ReadDma(out readBuf, out errorLba48, 0, 1, _dev.Timeout, out _); + }); + + capabilities.SupportsReadDmaLba48 = !sense && (errorLba48.Status & 0x01) != 0x01 && errorLba48.Error == 0 && + readBuf.Length > 0; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorLba48.Status, errorLba48.Error, readBuf.Length); + + capabilities.ReadDmaLba48Data = readBuf; + + // Send SET FEATURES before sending READ LONG commands, retrieve IDENTIFY again and + // check if ECC size changed. Sector is set to 1 because without it most drives just return + // CORRECTABLE ERROR for this command. + _dev.SetFeatures(out _, AtaFeatures.EnableReadLongVendorLength, 0, 0, 1, 0, _dev.Timeout, out _); + + _dev.AtaIdentify(out byte[] buffer, out _, _dev.Timeout, out _); + + if(Identify.Decode(buffer).HasValue) + { + ataId = Identify.Decode(buffer).Value; + + if(ataId.EccBytes != 0x0000 && + ataId.EccBytes != 0xFFFF) + capabilities.LongBlockSize = logicalSectorSize + ataId.EccBytes; + + if(ataId.UnformattedBPS > logicalSectorSize && + (!(ataId.EccBytes != 0x0000 && ataId.EccBytes != 0xFFFF) || capabilities.LongBlockSize == 516)) + capabilities.LongBlockSize = ataId.UnformattedBPS; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ LONG in CHS mode...").IsIndeterminate(); + + sense = _dev.ReadLong(out readBuf, out errorChs, false, 0, 0, 1, capabilities.LongBlockSize ?? 0, + _dev.Timeout, out _); + }); + + capabilities.SupportsReadLong = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorChs.Status, errorChs.Error, readBuf.Length); + + capabilities.ReadLongData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ LONG RETRY in CHS mode...").IsIndeterminate(); + + sense = _dev.ReadLong(out readBuf, out errorChs, true, 0, 0, 1, capabilities.LongBlockSize ?? 0, + _dev.Timeout, out _); + }); + + capabilities.SupportsReadLongRetry = !sense && (errorChs.Status & 0x01) != 0x01 && errorChs.Error == 0 && + readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != + checkCorrectRead; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorChs.Status, errorChs.Error, readBuf.Length); + + capabilities.ReadLongRetryData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ LONG in LBA mode...").IsIndeterminate(); + + sense = _dev.ReadLong(out readBuf, out errorLba, false, 0, capabilities.LongBlockSize ?? 0, + _dev.Timeout, out _); + }); + + capabilities.SupportsReadLongLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0 && + BitConverter.ToUInt64(readBuf, 0) != checkCorrectRead; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorLba.Status, errorLba.Error, readBuf.Length); + + capabilities.ReadLongLbaData = readBuf; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying READ LONG RETRY in LBA mode...").IsIndeterminate(); + + sense = _dev.ReadLong(out readBuf, out errorLba, true, 0, capabilities.LongBlockSize ?? 0, _dev.Timeout, + out _); + }); + + capabilities.SupportsReadLongRetryLba = !sense && (errorLba.Status & 0x01) != 0x01 && errorLba.Error == 0 && + readBuf.Length > 0 && BitConverter.ToUInt64(readBuf, 0) != + checkCorrectRead; + + AaruConsole.DebugWriteLine("ATA Report", "Sense = {0}, Status = 0x{1:X2}, Error = 0x{2:X2}, Length = {3}", + sense, errorLba.Status, errorLba.Error, readBuf.Length); + + capabilities.ReadLongRetryLbaData = readBuf; + + return capabilities; + } + + /// Clear serial numbers and other private fields from an IDENTIFY ATA DEVICE response + /// IDENTIFY ATA DEVICE response + /// IDENTIFY ATA DEVICE response without the private fields + public static byte[] ClearIdentify(byte[] buffer) + { + byte[] empty = new byte[512]; + + Array.Copy(empty, 0, buffer, 20, 20); + Array.Copy(empty, 0, buffer, 216, 8); + Array.Copy(empty, 0, buffer, 224, 8); + Array.Copy(empty, 0, buffer, 352, 40); + + return buffer; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/DeviceReport.cs b/Aaru.Core/Devices/Report/DeviceReport.cs index eea805e39..05342629a 100644 --- a/Aaru.Core/Devices/Report/DeviceReport.cs +++ b/Aaru.Core/Devices/Report/DeviceReport.cs @@ -32,14 +32,13 @@ using Aaru.Devices; -namespace Aaru.Core.Devices.Report -{ - public sealed partial class DeviceReport - { - readonly Device _dev; +namespace Aaru.Core.Devices.Report; - /// Initializes a device report for the specified device (must be opened) - /// Device - public DeviceReport(Device device) => _dev = device; - } +public sealed partial class DeviceReport +{ + readonly Device _dev; + + /// Initializes a device report for the specified device (must be opened) + /// Device + public DeviceReport(Device device) => _dev = device; } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/FireWire.cs b/Aaru.Core/Devices/Report/FireWire.cs index ae3dfd7e3..fa367c85e 100644 --- a/Aaru.Core/Devices/Report/FireWire.cs +++ b/Aaru.Core/Devices/Report/FireWire.cs @@ -32,18 +32,17 @@ using Aaru.CommonTypes.Metadata; -namespace Aaru.Core.Devices.Report +namespace Aaru.Core.Devices.Report; + +/// Implements creating a report for a FireWire device +public sealed partial class DeviceReport { - /// Implements creating a report for a FireWire device - public sealed partial class DeviceReport + /// Fills a device report with parameters specific to a FireWire device + public FireWire FireWireReport() => new FireWire { - /// Fills a device report with parameters specific to a FireWire device - public FireWire FireWireReport() => new FireWire - { - Manufacturer = _dev.FireWireVendorName, - Product = _dev.FireWireModelName, - ProductID = _dev.FireWireModel, - VendorID = _dev.FireWireVendor - }; - } + Manufacturer = _dev.FireWireVendorName, + Product = _dev.FireWireModelName, + ProductID = _dev.FireWireModel, + VendorID = _dev.FireWireVendor + }; } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/GdRomSwapTrick.cs b/Aaru.Core/Devices/Report/GdRomSwapTrick.cs index 5384c1ae0..a1886ce30 100644 --- a/Aaru.Core/Devices/Report/GdRomSwapTrick.cs +++ b/Aaru.Core/Devices/Report/GdRomSwapTrick.cs @@ -41,1570 +41,1569 @@ using Aaru.Devices; // ReSharper disable InlineOutVariableDeclaration -namespace Aaru.Core.Devices.Report +namespace Aaru.Core.Devices.Report; + +public sealed partial class DeviceReport { - public sealed partial class DeviceReport + /// Tries and checks reading a GD-ROM disc using the swap disc trick and adds the result to a device report + /// Device report + public void ReportGdRomSwapTrick(ref DeviceReportV2 report) { - /// Tries and checks reading a GD-ROM disc using the swap disc trick and adds the result to a device report - /// Device report - public void ReportGdRomSwapTrick(ref DeviceReportV2 report) + report.GdRomSwapDiscCapabilities = new GdRomSwapDiscCapabilities(); + + var pressedKey = new ConsoleKeyInfo(); + + while(pressedKey.Key != ConsoleKey.Y && + pressedKey.Key != ConsoleKey.N) { - report.GdRomSwapDiscCapabilities = new GdRomSwapDiscCapabilities(); + AaruConsole. + Write("Have you previously tried with a GD-ROM disc and did the computer hang or crash? (Y/N): "); - var pressedKey = new ConsoleKeyInfo(); + pressedKey = System.Console.ReadKey(); + AaruConsole.WriteLine(); + } - while(pressedKey.Key != ConsoleKey.Y && - pressedKey.Key != ConsoleKey.N) - { - AaruConsole. - Write("Have you previously tried with a GD-ROM disc and did the computer hang or crash? (Y/N): "); + if(pressedKey.Key == ConsoleKey.Y) + { + report.GdRomSwapDiscCapabilities.TestCrashed = true; - pressedKey = System.Console.ReadKey(); - AaruConsole.WriteLine(); - } + return; + } - if(pressedKey.Key == ConsoleKey.Y) - { - report.GdRomSwapDiscCapabilities.TestCrashed = true; + AaruConsole.WriteLine("Ejecting disc..."); - return; - } + _dev.AllowMediumRemoval(out _, _dev.Timeout, out _); + _dev.EjectTray(out _, _dev.Timeout, out _); - AaruConsole.WriteLine("Ejecting disc..."); + AaruConsole.WriteLine("Please insert trap disc inside..."); + AaruConsole.WriteLine("Press any key to continue..."); + System.Console.ReadLine(); - _dev.AllowMediumRemoval(out _, _dev.Timeout, out _); - _dev.EjectTray(out _, _dev.Timeout, out _); + AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); - AaruConsole.WriteLine("Please insert trap disc inside..."); - AaruConsole.WriteLine("Press any key to continue..."); - System.Console.ReadLine(); + int retries = 0; + bool sense; + byte[] buffer; + byte[] senseBuffer; - AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); + do + { + retries++; + sense = _dev.ScsiTestUnitReady(out senseBuffer, _dev.Timeout, out _); - int retries = 0; - bool sense; - byte[] buffer; - byte[] senseBuffer; + if(!sense) + break; - do - { - retries++; - sense = _dev.ScsiTestUnitReady(out senseBuffer, _dev.Timeout, out _); + DecodedSense? decodedSense = Sense.Decode(senseBuffer); - if(!sense) - break; + if(decodedSense?.ASC != 0x04) + break; - DecodedSense? decodedSense = Sense.Decode(senseBuffer); + if(decodedSense?.ASCQ != 0x01) + break; - if(decodedSense?.ASC != 0x04) - break; + Thread.Sleep(2000); + } while(retries < 25); - if(decodedSense?.ASCQ != 0x01) - break; + sense = _dev.ReadRawToc(out buffer, out senseBuffer, 1, _dev.Timeout, out _); - Thread.Sleep(2000); - } while(retries < 25); + if(sense) + { + AaruConsole.WriteLine("READ FULL TOC failed..."); + AaruConsole.DebugWriteLine("GD-ROM reporter", "{0}", Sense.PrettifySense(senseBuffer)); + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; + report.GdRomSwapDiscCapabilities.TestCrashed = false; + + return; + } + + FullTOC.CDFullTOC? decodedToc = FullTOC.Decode(buffer); + + if(decodedToc is null) + { + AaruConsole.WriteLine("Could not decode TOC..."); + + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; + report.GdRomSwapDiscCapabilities.TestCrashed = false; + + return; + } + + FullTOC.CDFullTOC toc = decodedToc.Value; + + FullTOC.TrackDataDescriptor leadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); + + if(leadOutTrack.POINT != 0xA2) + { + AaruConsole.WriteLine("Cannot find lead-out..."); + + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; + report.GdRomSwapDiscCapabilities.TestCrashed = false; + + return; + } + + int min = 0, sec, frame; + bool tocIsNotBcd = false; + + report.GdRomSwapDiscCapabilities.SwapDiscLeadOutPMIN = leadOutTrack.PMIN; + report.GdRomSwapDiscCapabilities.SwapDiscLeadOutPSEC = leadOutTrack.PSEC; + report.GdRomSwapDiscCapabilities.SwapDiscLeadOutPFRAM = leadOutTrack.PFRAME; + + if(leadOutTrack.PMIN == 122) + tocIsNotBcd = true; + + if(leadOutTrack.PMIN >= 0xA0 && + !tocIsNotBcd) + { + min += 90; + leadOutTrack.PMIN -= 0x90; + } + + if(tocIsNotBcd) + { + min = leadOutTrack.PMIN; + sec = leadOutTrack.PSEC; + frame = leadOutTrack.PFRAME; + } + else + { + min += ((leadOutTrack.PMIN >> 4) * 10) + (leadOutTrack.PMIN & 0x0F); + sec = ((leadOutTrack.PSEC >> 4) * 10) + (leadOutTrack.PSEC & 0x0F); + frame = ((leadOutTrack.PFRAME >> 4) * 10) + (leadOutTrack.PFRAME & 0x0F); + } + + int sectors = (min * 60 * 75) + (sec * 75) + frame - 150; + + AaruConsole.WriteLine("Trap disc shows {0} sectors...", sectors); + + if(sectors < 450000) + { + AaruConsole.WriteLine("Trap disc doesn't have enough sectors..."); + + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; + report.GdRomSwapDiscCapabilities.TestCrashed = false; + + return; + } + + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = true; + + AaruConsole.WriteLine("Stopping motor..."); + + _dev.StopUnit(out _, _dev.Timeout, out _); + + AaruConsole.WriteLine("Please MANUALLY get the trap disc out and put the GD-ROM disc inside..."); + AaruConsole.WriteLine("Press any key to continue..."); + System.Console.ReadLine(); + + AaruConsole.WriteLine("Waiting 5 seconds..."); + Thread.Sleep(5000); + + AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); + + retries = 0; + + do + { + retries++; sense = _dev.ReadRawToc(out buffer, out senseBuffer, 1, _dev.Timeout, out _); - if(sense) + if(!sense) + break; + + DecodedSense? decodedSense = Sense.Decode(senseBuffer); + + if(decodedSense?.ASC != 0x04) + break; + + if(decodedSense?.ASCQ != 0x01) + break; + } while(retries < 25); + + if(sense) + { + AaruConsole.WriteLine("READ FULL TOC failed..."); + AaruConsole.DebugWriteLine("GD-ROM reporter", "{0}", Sense.PrettifySense(senseBuffer)); + + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; + report.GdRomSwapDiscCapabilities.TestCrashed = false; + + return; + } + + decodedToc = FullTOC.Decode(buffer); + + if(decodedToc is null) + { + AaruConsole.WriteLine("Could not decode TOC..."); + + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; + report.GdRomSwapDiscCapabilities.TestCrashed = false; + + return; + } + + toc = decodedToc.Value; + + FullTOC.TrackDataDescriptor newLeadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); + + if(newLeadOutTrack.POINT != 0xA2) + { + AaruConsole.WriteLine("Cannot find lead-out..."); + + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; + report.GdRomSwapDiscCapabilities.TestCrashed = false; + + return; + } + + if(newLeadOutTrack.PMIN >= 0xA0 && + !tocIsNotBcd) + newLeadOutTrack.PMIN -= 0x90; + + if(newLeadOutTrack.PMIN != leadOutTrack.PMIN || + newLeadOutTrack.PSEC != leadOutTrack.PSEC || + newLeadOutTrack.PFRAME != leadOutTrack.PFRAME) + { + AaruConsole.WriteLine("Lead-out has changed, this drive does not support hot swapping discs..."); + + report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; + report.GdRomSwapDiscCapabilities.TestCrashed = false; + + return; + } + + _dev.SetCdSpeed(out _, RotationalControl.PureCav, 170, 0, _dev.Timeout, out _); + + AaruConsole.Write("Reading LBA 0... "); + + report.GdRomSwapDiscCapabilities.Lba0Readable = !_dev.ReadCd(out byte[] lba0Buffer, out byte[] lba0Sense, 0, + 2352, 1, MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba0Data = lba0Buffer; + report.GdRomSwapDiscCapabilities.Lba0Sense = lba0Sense; + report.GdRomSwapDiscCapabilities.Lba0DecodedSense = Sense.PrettifySense(lba0Sense); + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba0Readable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 0 as audio (scrambled)... "); + + report.GdRomSwapDiscCapabilities.Lba0ScrambledReadable = !_dev.ReadCd(out byte[] lba0ScrambledBuffer, + out byte[] lba0ScrambledSense, 0, 2352, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba0ScrambledData = lba0ScrambledBuffer; + report.GdRomSwapDiscCapabilities.Lba0ScrambledSense = lba0ScrambledSense; + report.GdRomSwapDiscCapabilities.Lba0ScrambledDecodedSense = Sense.PrettifySense(lba0ScrambledSense); + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba0ScrambledReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 100000 as audio... "); + uint cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba100000AudioReadable = !_dev.ReadCd(out byte[] lba100000AudioBuffer, + out byte[] lba100000AudioSenseBuffer, + 100000, 2352, cluster, + MmcSectorTypes.Cdda, false, false, + false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, + out _); + + report.GdRomSwapDiscCapabilities.Lba100000AudioData = lba100000AudioBuffer; + report.GdRomSwapDiscCapabilities.Lba100000AudioSense = lba100000AudioSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba100000AudioDecodedSense = + Sense.PrettifySense(lba100000AudioSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba100000AudioReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba100000AudioReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000AudioReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 50000 as audio... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba50000AudioReadable = !_dev.ReadCd(out byte[] lba50000AudioBuffer, + out byte[] lba50000AudioSenseBuffer, + 50000, 2352, cluster, + MmcSectorTypes.Cdda, false, false, + false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, + out _); + + report.GdRomSwapDiscCapabilities.Lba50000AudioData = lba50000AudioBuffer; + report.GdRomSwapDiscCapabilities.Lba50000AudioSense = lba50000AudioSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba50000AudioDecodedSense = + Sense.PrettifySense(lba50000AudioSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba50000AudioReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba50000AudioReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000AudioReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 450000 as audio... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba450000AudioReadable = !_dev.ReadCd(out byte[] lba450000AudioBuffer, + out byte[] lba450000AudioSenseBuffer, + 450000, 2352, cluster, + MmcSectorTypes.Cdda, false, false, + false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, + out _); + + report.GdRomSwapDiscCapabilities.Lba450000AudioData = lba450000AudioBuffer; + report.GdRomSwapDiscCapabilities.Lba450000AudioSense = lba450000AudioSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba450000AudioDecodedSense = + Sense.PrettifySense(lba450000AudioSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba450000AudioReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba450000AudioReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000AudioReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 400000 as audio... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba400000AudioReadable = !_dev.ReadCd(out byte[] lba400000AudioBuffer, + out byte[] lba400000AudioSenseBuffer, + 400000, 2352, cluster, + MmcSectorTypes.Cdda, false, false, + false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, + out _); + + report.GdRomSwapDiscCapabilities.Lba400000AudioData = lba400000AudioBuffer; + report.GdRomSwapDiscCapabilities.Lba400000AudioSense = lba400000AudioSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba400000AudioDecodedSense = + Sense.PrettifySense(lba400000AudioSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba400000AudioReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba400000AudioReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000AudioReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 45000 as audio... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba45000AudioReadable = !_dev.ReadCd(out byte[] lba45000AudioBuffer, + out byte[] lba45000AudioSenseBuffer, + 45000, 2352, cluster, + MmcSectorTypes.Cdda, false, false, + false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, + out _); + + report.GdRomSwapDiscCapabilities.Lba45000AudioData = lba45000AudioBuffer; + report.GdRomSwapDiscCapabilities.Lba45000AudioSense = lba45000AudioSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba45000AudioDecodedSense = + Sense.PrettifySense(lba45000AudioSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba45000AudioReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba45000AudioReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000AudioReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 44990 as audio... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba44990AudioReadable = !_dev.ReadCd(out byte[] lba44990AudioBuffer, + out byte[] lba44990AudioSenseBuffer, + 44990, 2352, cluster, + MmcSectorTypes.Cdda, false, false, + false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, + out _); + + report.GdRomSwapDiscCapabilities.Lba44990AudioData = lba44990AudioBuffer; + report.GdRomSwapDiscCapabilities.Lba44990AudioSense = lba44990AudioSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba44990AudioDecodedSense = + Sense.PrettifySense(lba44990AudioSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba44990AudioReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba44990AudioReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990AudioReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 100000 as audio with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadable = + !_dev.ReadCd(out byte[] lba100000AudioPqBuffer, out byte[] lba100000AudioPqSenseBuffer, 100000, + 2368, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba100000AudioPqData = lba100000AudioPqBuffer; + report.GdRomSwapDiscCapabilities.Lba100000AudioPqSense = lba100000AudioPqSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba100000AudioPqDecodedSense = + Sense.PrettifySense(lba100000AudioPqSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 50000 as audio with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadable = + !_dev.ReadCd(out byte[] lba50000AudioPqBuffer, out byte[] lba50000AudioPqSenseBuffer, 50000, 2368, + cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba50000AudioPqData = lba50000AudioPqBuffer; + report.GdRomSwapDiscCapabilities.Lba50000AudioPqSense = lba50000AudioPqSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba50000AudioPqDecodedSense = + Sense.PrettifySense(lba50000AudioPqSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 450000 as audio with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadable = + !_dev.ReadCd(out byte[] lba450000AudioPqBuffer, out byte[] lba450000AudioPqSenseBuffer, 450000, + 2368, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba450000AudioPqData = lba450000AudioPqBuffer; + report.GdRomSwapDiscCapabilities.Lba450000AudioPqSense = lba450000AudioPqSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba450000AudioPqDecodedSense = + Sense.PrettifySense(lba450000AudioPqSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 400000 as audio with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadable = + !_dev.ReadCd(out byte[] lba400000AudioPqBuffer, out byte[] lba400000AudioPqSenseBuffer, 400000, + 2368, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba400000AudioPqData = lba400000AudioPqBuffer; + report.GdRomSwapDiscCapabilities.Lba400000AudioPqSense = lba400000AudioPqSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba400000AudioPqDecodedSense = + Sense.PrettifySense(lba400000AudioPqSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 45000 as audio with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadable = + !_dev.ReadCd(out byte[] lba45000AudioPqBuffer, out byte[] lba45000AudioPqSenseBuffer, 45000, 2368, + cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba45000AudioPqData = lba45000AudioPqBuffer; + report.GdRomSwapDiscCapabilities.Lba45000AudioPqSense = lba45000AudioPqSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba45000AudioPqDecodedSense = + Sense.PrettifySense(lba45000AudioPqSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 44990 as audio with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba44990AudioPqReadable = + !_dev.ReadCd(out byte[] lba44990AudioPqBuffer, out byte[] lba44990AudioPqSenseBuffer, 44990, 2368, + cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba44990AudioPqData = lba44990AudioPqBuffer; + report.GdRomSwapDiscCapabilities.Lba44990AudioPqSense = lba44990AudioPqSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba44990AudioPqDecodedSense = + Sense.PrettifySense(lba44990AudioPqSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba44990AudioPqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba44990AudioPqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990AudioPqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 100000 as audio with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadable = + !_dev.ReadCd(out byte[] lba100000AudioRwBuffer, out byte[] lba100000AudioRwSenseBuffer, 100000, + 2448, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba100000AudioRwData = lba100000AudioRwBuffer; + report.GdRomSwapDiscCapabilities.Lba100000AudioRwSense = lba100000AudioRwSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba100000AudioRwDecodedSense = + Sense.PrettifySense(lba100000AudioRwSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 50000 as audio with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadable = + !_dev.ReadCd(out byte[] lba50000AudioRwBuffer, out byte[] lba50000AudioRwSenseBuffer, 50000, 2448, + cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba50000AudioRwData = lba50000AudioRwBuffer; + report.GdRomSwapDiscCapabilities.Lba50000AudioRwSense = lba50000AudioRwSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba50000AudioRwDecodedSense = + Sense.PrettifySense(lba50000AudioRwSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 450000 as audio with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadable = + !_dev.ReadCd(out byte[] lba450000AudioRwBuffer, out byte[] lba450000AudioRwSenseBuffer, 450000, + 2448, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba450000AudioRwData = lba450000AudioRwBuffer; + report.GdRomSwapDiscCapabilities.Lba450000AudioRwSense = lba450000AudioRwSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba450000AudioRwDecodedSense = + Sense.PrettifySense(lba450000AudioRwSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 400000 as audio with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadable = + !_dev.ReadCd(out byte[] lba400000AudioRwBuffer, out byte[] lba400000AudioRwSenseBuffer, 400000, + 2448, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba400000AudioRwData = lba400000AudioRwBuffer; + report.GdRomSwapDiscCapabilities.Lba400000AudioRwSense = lba400000AudioRwSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba400000AudioRwDecodedSense = + Sense.PrettifySense(lba400000AudioRwSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 45000 as audio with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadable = + !_dev.ReadCd(out byte[] lba45000AudioRwBuffer, out byte[] lba45000AudioRwSenseBuffer, 45000, 2448, + cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba45000AudioRwData = lba45000AudioRwBuffer; + report.GdRomSwapDiscCapabilities.Lba45000AudioRwSense = lba45000AudioRwSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba45000AudioRwDecodedSense = + Sense.PrettifySense(lba45000AudioRwSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 44990 as audio with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba44990AudioRwReadable = + !_dev.ReadCd(out byte[] lba44990AudioRwBuffer, out byte[] lba44990AudioRwSenseBuffer, 44990, 2448, + cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba44990AudioRwData = lba44990AudioRwBuffer; + report.GdRomSwapDiscCapabilities.Lba44990AudioRwSense = lba44990AudioRwSenseBuffer; + + report.GdRomSwapDiscCapabilities.Lba44990AudioRwDecodedSense = + Sense.PrettifySense(lba44990AudioRwSenseBuffer); + + report.GdRomSwapDiscCapabilities.Lba44990AudioRwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba44990AudioRwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990AudioRwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 100000... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba100000Readable = !_dev.ReadCd(out byte[] lba100000Buffer, + out byte[] lba100000SenseBuffer, 100000, + 2352, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba100000Data = lba100000Buffer; + report.GdRomSwapDiscCapabilities.Lba100000Sense = lba100000SenseBuffer; + report.GdRomSwapDiscCapabilities.Lba100000DecodedSense = Sense.PrettifySense(lba100000SenseBuffer); + report.GdRomSwapDiscCapabilities.Lba100000ReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba100000Readable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000Readable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 50000... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba50000Readable = !_dev.ReadCd(out byte[] lba50000Buffer, + out byte[] lba50000SenseBuffer, 50000, + 2352, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba50000Data = lba50000Buffer; + report.GdRomSwapDiscCapabilities.Lba50000Sense = lba50000SenseBuffer; + report.GdRomSwapDiscCapabilities.Lba50000DecodedSense = Sense.PrettifySense(lba50000SenseBuffer); + report.GdRomSwapDiscCapabilities.Lba50000ReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba50000Readable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000Readable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 450000... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba450000Readable = !_dev.ReadCd(out byte[] lba450000Buffer, + out byte[] lba450000SenseBuffer, 450000, + 2352, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba450000Data = lba450000Buffer; + report.GdRomSwapDiscCapabilities.Lba450000Sense = lba450000SenseBuffer; + report.GdRomSwapDiscCapabilities.Lba450000DecodedSense = Sense.PrettifySense(lba450000SenseBuffer); + report.GdRomSwapDiscCapabilities.Lba450000ReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba450000Readable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000Readable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 400000... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba400000Readable = !_dev.ReadCd(out byte[] lba400000Buffer, + out byte[] lba400000SenseBuffer, 400000, + 2352, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba400000Data = lba400000Buffer; + report.GdRomSwapDiscCapabilities.Lba400000Sense = lba400000SenseBuffer; + report.GdRomSwapDiscCapabilities.Lba400000DecodedSense = Sense.PrettifySense(lba400000SenseBuffer); + report.GdRomSwapDiscCapabilities.Lba400000ReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba400000Readable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000Readable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 45000... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba45000Readable = !_dev.ReadCd(out byte[] lba45000Buffer, + out byte[] lba45000SenseBuffer, 45000, + 2352, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba45000Data = lba45000Buffer; + report.GdRomSwapDiscCapabilities.Lba45000Sense = lba45000SenseBuffer; + report.GdRomSwapDiscCapabilities.Lba45000DecodedSense = Sense.PrettifySense(lba45000SenseBuffer); + report.GdRomSwapDiscCapabilities.Lba45000ReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba45000Readable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000Readable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 44990... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba44990Readable = !_dev.ReadCd(out byte[] lba44990Buffer, + out byte[] lba44990SenseBuffer, 44990, + 2352, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba44990Data = lba44990Buffer; + report.GdRomSwapDiscCapabilities.Lba44990Sense = lba44990SenseBuffer; + report.GdRomSwapDiscCapabilities.Lba44990DecodedSense = Sense.PrettifySense(lba44990SenseBuffer); + report.GdRomSwapDiscCapabilities.Lba44990ReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba44990Readable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990Readable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 100000 with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba100000PqReadable = !_dev.ReadCd(out byte[] lba100000PqBuffer, + out byte[] lba100000PqSenseBuffer, + 100000, 2368, cluster, + MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, + MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba100000PqData = lba100000PqBuffer; + report.GdRomSwapDiscCapabilities.Lba100000PqSense = lba100000PqSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba100000PqDecodedSense = Sense.PrettifySense(lba100000PqSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba100000PqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba100000PqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000PqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 50000 with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba50000PqReadable = !_dev.ReadCd(out byte[] lba50000PqBuffer, + out byte[] lba50000PqSenseBuffer, 50000, + 2368, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Q16, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba50000PqData = lba50000PqBuffer; + report.GdRomSwapDiscCapabilities.Lba50000PqSense = lba50000PqSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba50000PqDecodedSense = Sense.PrettifySense(lba50000PqSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba50000PqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba50000PqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000PqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 450000 with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba450000PqReadable = !_dev.ReadCd(out byte[] lba450000PqBuffer, + out byte[] lba450000PqSenseBuffer, + 450000, 2368, cluster, + MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, + MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba450000PqData = lba450000PqBuffer; + report.GdRomSwapDiscCapabilities.Lba450000PqSense = lba450000PqSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba450000PqDecodedSense = Sense.PrettifySense(lba450000PqSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba450000PqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba450000PqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000PqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 400000 with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba400000PqReadable = !_dev.ReadCd(out byte[] lba400000PqBuffer, + out byte[] lba400000PqSenseBuffer, + 400000, 2368, cluster, + MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, + MmcSubchannel.Q16, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba400000PqData = lba400000PqBuffer; + report.GdRomSwapDiscCapabilities.Lba400000PqSense = lba400000PqSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba400000PqDecodedSense = Sense.PrettifySense(lba400000PqSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba400000PqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba400000PqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000PqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 45000 with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba45000PqReadable = !_dev.ReadCd(out byte[] lba45000PqBuffer, + out byte[] lba45000PqSenseBuffer, 45000, + 2368, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Q16, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba45000PqData = lba45000PqBuffer; + report.GdRomSwapDiscCapabilities.Lba45000PqSense = lba45000PqSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba45000PqDecodedSense = Sense.PrettifySense(lba45000PqSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba45000PqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba45000PqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000PqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 44990 with PQ subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba44990PqReadable = !_dev.ReadCd(out byte[] lba44990PqBuffer, + out byte[] lba44990PqSenseBuffer, 44990, + 2368, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Q16, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba44990PqData = lba44990PqBuffer; + report.GdRomSwapDiscCapabilities.Lba44990PqSense = lba44990PqSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba44990PqDecodedSense = Sense.PrettifySense(lba44990PqSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba44990PqReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba44990PqReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990PqReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 100000 with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba100000RwReadable = !_dev.ReadCd(out byte[] lba100000RwBuffer, + out byte[] lba100000RwSenseBuffer, + 100000, 2448, cluster, + MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, + MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba100000RwData = lba100000RwBuffer; + report.GdRomSwapDiscCapabilities.Lba100000RwSense = lba100000RwSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba100000RwDecodedSense = Sense.PrettifySense(lba100000RwSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba100000RwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba100000RwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000RwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 50000 with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba50000RwReadable = !_dev.ReadCd(out byte[] lba50000RwBuffer, + out byte[] lba50000RwSenseBuffer, 50000, + 2448, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Raw, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba50000RwData = lba50000RwBuffer; + report.GdRomSwapDiscCapabilities.Lba50000RwSense = lba50000RwSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba50000RwDecodedSense = Sense.PrettifySense(lba50000RwSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba50000RwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba50000RwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000RwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 450000 with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba450000RwReadable = !_dev.ReadCd(out byte[] lba450000RwBuffer, + out byte[] lba450000RwSenseBuffer, + 450000, 2448, cluster, + MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, + MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba450000RwData = lba450000RwBuffer; + report.GdRomSwapDiscCapabilities.Lba450000RwSense = lba450000RwSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba450000RwDecodedSense = Sense.PrettifySense(lba450000RwSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba450000RwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba450000RwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000RwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 400000 with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba400000RwReadable = !_dev.ReadCd(out byte[] lba400000RwBuffer, + out byte[] lba400000RwSenseBuffer, + 400000, 2448, cluster, + MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, + MmcSubchannel.Raw, _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba400000RwData = lba400000RwBuffer; + report.GdRomSwapDiscCapabilities.Lba400000RwSense = lba400000RwSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba400000RwDecodedSense = Sense.PrettifySense(lba400000RwSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba400000RwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba400000RwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000RwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 45000 with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba45000RwReadable = !_dev.ReadCd(out byte[] lba45000RwBuffer, + out byte[] lba45000RwSenseBuffer, 45000, + 2448, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Raw, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba45000RwData = lba45000RwBuffer; + report.GdRomSwapDiscCapabilities.Lba45000RwSense = lba45000RwSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba45000RwDecodedSense = Sense.PrettifySense(lba45000RwSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba45000RwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba45000RwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000RwReadable ? "Success!" : "FAIL!"); + + AaruConsole.Write("Reading LBA 44990 with RW subchannel... "); + cluster = 16; + + while(true) + { + report.GdRomSwapDiscCapabilities.Lba44990RwReadable = !_dev.ReadCd(out byte[] lba44990RwBuffer, + out byte[] lba44990RwSenseBuffer, 44990, + 2448, cluster, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Raw, + _dev.Timeout, out _); + + report.GdRomSwapDiscCapabilities.Lba44990RwData = lba44990RwBuffer; + report.GdRomSwapDiscCapabilities.Lba44990RwSense = lba44990RwSenseBuffer; + report.GdRomSwapDiscCapabilities.Lba44990RwDecodedSense = Sense.PrettifySense(lba44990RwSenseBuffer); + report.GdRomSwapDiscCapabilities.Lba44990RwReadableCluster = (int)cluster; + + if(report.GdRomSwapDiscCapabilities.Lba44990RwReadable) + break; + + if(cluster == 1) + break; + + cluster /= 2; + } + + AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990RwReadable ? "Success!" : "FAIL!"); + + if(report.GdRomSwapDiscCapabilities.Lba45000Readable == false && + report.GdRomSwapDiscCapabilities.Lba50000Readable == false && + report.GdRomSwapDiscCapabilities.Lba100000Readable == false && + report.GdRomSwapDiscCapabilities.Lba400000Readable == false && + report.GdRomSwapDiscCapabilities.Lba450000Readable == false && + report.GdRomSwapDiscCapabilities.Lba45000AudioReadable == false && + report.GdRomSwapDiscCapabilities.Lba50000AudioReadable == false && + report.GdRomSwapDiscCapabilities.Lba100000AudioReadable == false && + report.GdRomSwapDiscCapabilities.Lba400000AudioReadable == false && + report.GdRomSwapDiscCapabilities.Lba450000AudioReadable == false) + return; + + pressedKey = new ConsoleKeyInfo(); + + while(pressedKey.Key != ConsoleKey.Y && + pressedKey.Key != ConsoleKey.N) + { + AaruConsole. + Write("The next part of the test will read the whole high density area of a GD-ROM from the smallest known readable sector until the first error happens\n" + + "Do you want to proceed? (Y/N): "); + + pressedKey = System.Console.ReadKey(); + AaruConsole.WriteLine(); + } + + if(pressedKey.Key == ConsoleKey.N) + return; + + uint startingSector = 45000; + bool readAsAudio = false; + bool aborted = false; + MmcSubchannel subchannel = MmcSubchannel.None; + uint blockSize = 2352; + + if(report.GdRomSwapDiscCapabilities.Lba45000Readable == false) + { + startingSector = 45000; + readAsAudio = false; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba45000ReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba45000RwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba45000PqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba50000Readable == false) + { + startingSector = 50000; + readAsAudio = false; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba50000ReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba50000RwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba50000PqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba100000Readable == false) + { + startingSector = 100000; + readAsAudio = false; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba100000ReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba100000RwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba100000PqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba400000Readable == false) + { + startingSector = 400000; + readAsAudio = false; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba400000ReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba400000RwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba400000PqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba450000Readable == false) + { + startingSector = 450000; + readAsAudio = false; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba450000ReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba450000RwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba450000PqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba45000AudioReadable == false) + { + startingSector = 45000; + readAsAudio = true; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba45000AudioReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba50000AudioReadable == false) + { + startingSector = 50000; + readAsAudio = true; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba50000AudioReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba100000AudioReadable == false) + { + startingSector = 100000; + readAsAudio = true; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba100000AudioReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba400000AudioReadable == false) + { + startingSector = 400000; + readAsAudio = true; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba400000AudioReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadable) + subchannel = MmcSubchannel.Q16; + } + else if(report.GdRomSwapDiscCapabilities.Lba450000AudioReadable == false) + { + startingSector = 450000; + readAsAudio = true; + cluster = (uint)report.GdRomSwapDiscCapabilities.Lba450000AudioReadableCluster; + + if(report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadable) + subchannel = MmcSubchannel.Raw; + else if(report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadable) + subchannel = MmcSubchannel.Q16; + } + + System.Console.CancelKeyPress += (sender, e) => + { + e.Cancel = true; + aborted = true; + }; + + report.GdRomSwapDiscCapabilities.MinimumReadableSectorInHdArea = startingSector; + + switch(subchannel) + { + case MmcSubchannel.Raw: + blockSize += 96; + + break; + case MmcSubchannel.Q16: + blockSize += 16; + + break; + } + + byte[] lastSuccessfulPq = null; + byte[] lastSuccessfulRw = null; + bool trackModeChange = false; + + AaruConsole.WriteLine(); + + for(uint lba = startingSector; lba < sectors; lba += cluster) + { + if(aborted) { - AaruConsole.WriteLine("READ FULL TOC failed..."); - AaruConsole.DebugWriteLine("GD-ROM reporter", "{0}", Sense.PrettifySense(senseBuffer)); - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; - report.GdRomSwapDiscCapabilities.TestCrashed = false; - - return; - } - - FullTOC.CDFullTOC? decodedToc = FullTOC.Decode(buffer); - - if(decodedToc is null) - { - AaruConsole.WriteLine("Could not decode TOC..."); - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; - report.GdRomSwapDiscCapabilities.TestCrashed = false; - - return; - } - - FullTOC.CDFullTOC toc = decodedToc.Value; - - FullTOC.TrackDataDescriptor leadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); - - if(leadOutTrack.POINT != 0xA2) - { - AaruConsole.WriteLine("Cannot find lead-out..."); - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; - report.GdRomSwapDiscCapabilities.TestCrashed = false; - - return; - } - - int min = 0, sec, frame; - bool tocIsNotBcd = false; - - report.GdRomSwapDiscCapabilities.SwapDiscLeadOutPMIN = leadOutTrack.PMIN; - report.GdRomSwapDiscCapabilities.SwapDiscLeadOutPSEC = leadOutTrack.PSEC; - report.GdRomSwapDiscCapabilities.SwapDiscLeadOutPFRAM = leadOutTrack.PFRAME; - - if(leadOutTrack.PMIN == 122) - tocIsNotBcd = true; - - if(leadOutTrack.PMIN >= 0xA0 && - !tocIsNotBcd) - { - min += 90; - leadOutTrack.PMIN -= 0x90; - } - - if(tocIsNotBcd) - { - min = leadOutTrack.PMIN; - sec = leadOutTrack.PSEC; - frame = leadOutTrack.PFRAME; - } - else - { - min += ((leadOutTrack.PMIN >> 4) * 10) + (leadOutTrack.PMIN & 0x0F); - sec = ((leadOutTrack.PSEC >> 4) * 10) + (leadOutTrack.PSEC & 0x0F); - frame = ((leadOutTrack.PFRAME >> 4) * 10) + (leadOutTrack.PFRAME & 0x0F); - } - - int sectors = (min * 60 * 75) + (sec * 75) + frame - 150; - - AaruConsole.WriteLine("Trap disc shows {0} sectors...", sectors); - - if(sectors < 450000) - { - AaruConsole.WriteLine("Trap disc doesn't have enough sectors..."); - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; - report.GdRomSwapDiscCapabilities.TestCrashed = false; - - return; - } - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = true; - - AaruConsole.WriteLine("Stopping motor..."); - - _dev.StopUnit(out _, _dev.Timeout, out _); - - AaruConsole.WriteLine("Please MANUALLY get the trap disc out and put the GD-ROM disc inside..."); - AaruConsole.WriteLine("Press any key to continue..."); - System.Console.ReadLine(); - - AaruConsole.WriteLine("Waiting 5 seconds..."); - Thread.Sleep(5000); - - AaruConsole.WriteLine("Sending READ FULL TOC to the device..."); - - retries = 0; - - do - { - retries++; - sense = _dev.ReadRawToc(out buffer, out senseBuffer, 1, _dev.Timeout, out _); - - if(!sense) - break; - - DecodedSense? decodedSense = Sense.Decode(senseBuffer); - - if(decodedSense?.ASC != 0x04) - break; - - if(decodedSense?.ASCQ != 0x01) - break; - } while(retries < 25); - - if(sense) - { - AaruConsole.WriteLine("READ FULL TOC failed..."); - AaruConsole.DebugWriteLine("GD-ROM reporter", "{0}", Sense.PrettifySense(senseBuffer)); - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; - report.GdRomSwapDiscCapabilities.TestCrashed = false; - - return; - } - - decodedToc = FullTOC.Decode(buffer); - - if(decodedToc is null) - { - AaruConsole.WriteLine("Could not decode TOC..."); - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; - report.GdRomSwapDiscCapabilities.TestCrashed = false; - - return; - } - - toc = decodedToc.Value; - - FullTOC.TrackDataDescriptor newLeadOutTrack = toc.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA2); - - if(newLeadOutTrack.POINT != 0xA2) - { - AaruConsole.WriteLine("Cannot find lead-out..."); - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; - report.GdRomSwapDiscCapabilities.TestCrashed = false; - - return; - } - - if(newLeadOutTrack.PMIN >= 0xA0 && - !tocIsNotBcd) - newLeadOutTrack.PMIN -= 0x90; - - if(newLeadOutTrack.PMIN != leadOutTrack.PMIN || - newLeadOutTrack.PSEC != leadOutTrack.PSEC || - newLeadOutTrack.PFRAME != leadOutTrack.PFRAME) - { - AaruConsole.WriteLine("Lead-out has changed, this drive does not support hot swapping discs..."); - - report.GdRomSwapDiscCapabilities.RecognizedSwapDisc = false; - report.GdRomSwapDiscCapabilities.TestCrashed = false; - - return; - } - - _dev.SetCdSpeed(out _, RotationalControl.PureCav, 170, 0, _dev.Timeout, out _); - - AaruConsole.Write("Reading LBA 0... "); - - report.GdRomSwapDiscCapabilities.Lba0Readable = !_dev.ReadCd(out byte[] lba0Buffer, out byte[] lba0Sense, 0, - 2352, 1, MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba0Data = lba0Buffer; - report.GdRomSwapDiscCapabilities.Lba0Sense = lba0Sense; - report.GdRomSwapDiscCapabilities.Lba0DecodedSense = Sense.PrettifySense(lba0Sense); - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba0Readable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 0 as audio (scrambled)... "); - - report.GdRomSwapDiscCapabilities.Lba0ScrambledReadable = !_dev.ReadCd(out byte[] lba0ScrambledBuffer, - out byte[] lba0ScrambledSense, 0, 2352, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba0ScrambledData = lba0ScrambledBuffer; - report.GdRomSwapDiscCapabilities.Lba0ScrambledSense = lba0ScrambledSense; - report.GdRomSwapDiscCapabilities.Lba0ScrambledDecodedSense = Sense.PrettifySense(lba0ScrambledSense); - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba0ScrambledReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 100000 as audio... "); - uint cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba100000AudioReadable = !_dev.ReadCd(out byte[] lba100000AudioBuffer, - out byte[] lba100000AudioSenseBuffer, - 100000, 2352, cluster, - MmcSectorTypes.Cdda, false, false, - false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, - out _); - - report.GdRomSwapDiscCapabilities.Lba100000AudioData = lba100000AudioBuffer; - report.GdRomSwapDiscCapabilities.Lba100000AudioSense = lba100000AudioSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba100000AudioDecodedSense = - Sense.PrettifySense(lba100000AudioSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba100000AudioReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba100000AudioReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000AudioReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 50000 as audio... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba50000AudioReadable = !_dev.ReadCd(out byte[] lba50000AudioBuffer, - out byte[] lba50000AudioSenseBuffer, - 50000, 2352, cluster, - MmcSectorTypes.Cdda, false, false, - false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, - out _); - - report.GdRomSwapDiscCapabilities.Lba50000AudioData = lba50000AudioBuffer; - report.GdRomSwapDiscCapabilities.Lba50000AudioSense = lba50000AudioSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba50000AudioDecodedSense = - Sense.PrettifySense(lba50000AudioSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba50000AudioReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba50000AudioReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000AudioReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 450000 as audio... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba450000AudioReadable = !_dev.ReadCd(out byte[] lba450000AudioBuffer, - out byte[] lba450000AudioSenseBuffer, - 450000, 2352, cluster, - MmcSectorTypes.Cdda, false, false, - false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, - out _); - - report.GdRomSwapDiscCapabilities.Lba450000AudioData = lba450000AudioBuffer; - report.GdRomSwapDiscCapabilities.Lba450000AudioSense = lba450000AudioSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba450000AudioDecodedSense = - Sense.PrettifySense(lba450000AudioSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba450000AudioReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba450000AudioReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000AudioReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 400000 as audio... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba400000AudioReadable = !_dev.ReadCd(out byte[] lba400000AudioBuffer, - out byte[] lba400000AudioSenseBuffer, - 400000, 2352, cluster, - MmcSectorTypes.Cdda, false, false, - false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, - out _); - - report.GdRomSwapDiscCapabilities.Lba400000AudioData = lba400000AudioBuffer; - report.GdRomSwapDiscCapabilities.Lba400000AudioSense = lba400000AudioSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba400000AudioDecodedSense = - Sense.PrettifySense(lba400000AudioSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba400000AudioReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba400000AudioReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000AudioReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 45000 as audio... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba45000AudioReadable = !_dev.ReadCd(out byte[] lba45000AudioBuffer, - out byte[] lba45000AudioSenseBuffer, - 45000, 2352, cluster, - MmcSectorTypes.Cdda, false, false, - false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, - out _); - - report.GdRomSwapDiscCapabilities.Lba45000AudioData = lba45000AudioBuffer; - report.GdRomSwapDiscCapabilities.Lba45000AudioSense = lba45000AudioSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba45000AudioDecodedSense = - Sense.PrettifySense(lba45000AudioSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba45000AudioReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba45000AudioReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000AudioReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 44990 as audio... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba44990AudioReadable = !_dev.ReadCd(out byte[] lba44990AudioBuffer, - out byte[] lba44990AudioSenseBuffer, - 44990, 2352, cluster, - MmcSectorTypes.Cdda, false, false, - false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, - out _); - - report.GdRomSwapDiscCapabilities.Lba44990AudioData = lba44990AudioBuffer; - report.GdRomSwapDiscCapabilities.Lba44990AudioSense = lba44990AudioSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba44990AudioDecodedSense = - Sense.PrettifySense(lba44990AudioSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba44990AudioReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba44990AudioReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990AudioReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 100000 as audio with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadable = - !_dev.ReadCd(out byte[] lba100000AudioPqBuffer, out byte[] lba100000AudioPqSenseBuffer, 100000, - 2368, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba100000AudioPqData = lba100000AudioPqBuffer; - report.GdRomSwapDiscCapabilities.Lba100000AudioPqSense = lba100000AudioPqSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba100000AudioPqDecodedSense = - Sense.PrettifySense(lba100000AudioPqSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 50000 as audio with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadable = - !_dev.ReadCd(out byte[] lba50000AudioPqBuffer, out byte[] lba50000AudioPqSenseBuffer, 50000, 2368, - cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba50000AudioPqData = lba50000AudioPqBuffer; - report.GdRomSwapDiscCapabilities.Lba50000AudioPqSense = lba50000AudioPqSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba50000AudioPqDecodedSense = - Sense.PrettifySense(lba50000AudioPqSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 450000 as audio with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadable = - !_dev.ReadCd(out byte[] lba450000AudioPqBuffer, out byte[] lba450000AudioPqSenseBuffer, 450000, - 2368, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba450000AudioPqData = lba450000AudioPqBuffer; - report.GdRomSwapDiscCapabilities.Lba450000AudioPqSense = lba450000AudioPqSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba450000AudioPqDecodedSense = - Sense.PrettifySense(lba450000AudioPqSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 400000 as audio with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadable = - !_dev.ReadCd(out byte[] lba400000AudioPqBuffer, out byte[] lba400000AudioPqSenseBuffer, 400000, - 2368, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba400000AudioPqData = lba400000AudioPqBuffer; - report.GdRomSwapDiscCapabilities.Lba400000AudioPqSense = lba400000AudioPqSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba400000AudioPqDecodedSense = - Sense.PrettifySense(lba400000AudioPqSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 45000 as audio with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadable = - !_dev.ReadCd(out byte[] lba45000AudioPqBuffer, out byte[] lba45000AudioPqSenseBuffer, 45000, 2368, - cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba45000AudioPqData = lba45000AudioPqBuffer; - report.GdRomSwapDiscCapabilities.Lba45000AudioPqSense = lba45000AudioPqSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba45000AudioPqDecodedSense = - Sense.PrettifySense(lba45000AudioPqSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 44990 as audio with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba44990AudioPqReadable = - !_dev.ReadCd(out byte[] lba44990AudioPqBuffer, out byte[] lba44990AudioPqSenseBuffer, 44990, 2368, - cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba44990AudioPqData = lba44990AudioPqBuffer; - report.GdRomSwapDiscCapabilities.Lba44990AudioPqSense = lba44990AudioPqSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba44990AudioPqDecodedSense = - Sense.PrettifySense(lba44990AudioPqSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba44990AudioPqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba44990AudioPqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990AudioPqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 100000 as audio with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadable = - !_dev.ReadCd(out byte[] lba100000AudioRwBuffer, out byte[] lba100000AudioRwSenseBuffer, 100000, - 2448, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba100000AudioRwData = lba100000AudioRwBuffer; - report.GdRomSwapDiscCapabilities.Lba100000AudioRwSense = lba100000AudioRwSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba100000AudioRwDecodedSense = - Sense.PrettifySense(lba100000AudioRwSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 50000 as audio with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadable = - !_dev.ReadCd(out byte[] lba50000AudioRwBuffer, out byte[] lba50000AudioRwSenseBuffer, 50000, 2448, - cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba50000AudioRwData = lba50000AudioRwBuffer; - report.GdRomSwapDiscCapabilities.Lba50000AudioRwSense = lba50000AudioRwSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba50000AudioRwDecodedSense = - Sense.PrettifySense(lba50000AudioRwSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 450000 as audio with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadable = - !_dev.ReadCd(out byte[] lba450000AudioRwBuffer, out byte[] lba450000AudioRwSenseBuffer, 450000, - 2448, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba450000AudioRwData = lba450000AudioRwBuffer; - report.GdRomSwapDiscCapabilities.Lba450000AudioRwSense = lba450000AudioRwSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba450000AudioRwDecodedSense = - Sense.PrettifySense(lba450000AudioRwSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 400000 as audio with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadable = - !_dev.ReadCd(out byte[] lba400000AudioRwBuffer, out byte[] lba400000AudioRwSenseBuffer, 400000, - 2448, cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba400000AudioRwData = lba400000AudioRwBuffer; - report.GdRomSwapDiscCapabilities.Lba400000AudioRwSense = lba400000AudioRwSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba400000AudioRwDecodedSense = - Sense.PrettifySense(lba400000AudioRwSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 45000 as audio with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadable = - !_dev.ReadCd(out byte[] lba45000AudioRwBuffer, out byte[] lba45000AudioRwSenseBuffer, 45000, 2448, - cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba45000AudioRwData = lba45000AudioRwBuffer; - report.GdRomSwapDiscCapabilities.Lba45000AudioRwSense = lba45000AudioRwSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba45000AudioRwDecodedSense = - Sense.PrettifySense(lba45000AudioRwSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 44990 as audio with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba44990AudioRwReadable = - !_dev.ReadCd(out byte[] lba44990AudioRwBuffer, out byte[] lba44990AudioRwSenseBuffer, 44990, 2448, - cluster, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba44990AudioRwData = lba44990AudioRwBuffer; - report.GdRomSwapDiscCapabilities.Lba44990AudioRwSense = lba44990AudioRwSenseBuffer; - - report.GdRomSwapDiscCapabilities.Lba44990AudioRwDecodedSense = - Sense.PrettifySense(lba44990AudioRwSenseBuffer); - - report.GdRomSwapDiscCapabilities.Lba44990AudioRwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba44990AudioRwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990AudioRwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 100000... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba100000Readable = !_dev.ReadCd(out byte[] lba100000Buffer, - out byte[] lba100000SenseBuffer, 100000, - 2352, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba100000Data = lba100000Buffer; - report.GdRomSwapDiscCapabilities.Lba100000Sense = lba100000SenseBuffer; - report.GdRomSwapDiscCapabilities.Lba100000DecodedSense = Sense.PrettifySense(lba100000SenseBuffer); - report.GdRomSwapDiscCapabilities.Lba100000ReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba100000Readable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000Readable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 50000... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba50000Readable = !_dev.ReadCd(out byte[] lba50000Buffer, - out byte[] lba50000SenseBuffer, 50000, - 2352, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba50000Data = lba50000Buffer; - report.GdRomSwapDiscCapabilities.Lba50000Sense = lba50000SenseBuffer; - report.GdRomSwapDiscCapabilities.Lba50000DecodedSense = Sense.PrettifySense(lba50000SenseBuffer); - report.GdRomSwapDiscCapabilities.Lba50000ReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba50000Readable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000Readable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 450000... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba450000Readable = !_dev.ReadCd(out byte[] lba450000Buffer, - out byte[] lba450000SenseBuffer, 450000, - 2352, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba450000Data = lba450000Buffer; - report.GdRomSwapDiscCapabilities.Lba450000Sense = lba450000SenseBuffer; - report.GdRomSwapDiscCapabilities.Lba450000DecodedSense = Sense.PrettifySense(lba450000SenseBuffer); - report.GdRomSwapDiscCapabilities.Lba450000ReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba450000Readable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000Readable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 400000... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba400000Readable = !_dev.ReadCd(out byte[] lba400000Buffer, - out byte[] lba400000SenseBuffer, 400000, - 2352, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba400000Data = lba400000Buffer; - report.GdRomSwapDiscCapabilities.Lba400000Sense = lba400000SenseBuffer; - report.GdRomSwapDiscCapabilities.Lba400000DecodedSense = Sense.PrettifySense(lba400000SenseBuffer); - report.GdRomSwapDiscCapabilities.Lba400000ReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba400000Readable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000Readable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 45000... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba45000Readable = !_dev.ReadCd(out byte[] lba45000Buffer, - out byte[] lba45000SenseBuffer, 45000, - 2352, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba45000Data = lba45000Buffer; - report.GdRomSwapDiscCapabilities.Lba45000Sense = lba45000SenseBuffer; - report.GdRomSwapDiscCapabilities.Lba45000DecodedSense = Sense.PrettifySense(lba45000SenseBuffer); - report.GdRomSwapDiscCapabilities.Lba45000ReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba45000Readable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000Readable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 44990... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba44990Readable = !_dev.ReadCd(out byte[] lba44990Buffer, - out byte[] lba44990SenseBuffer, 44990, - 2352, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba44990Data = lba44990Buffer; - report.GdRomSwapDiscCapabilities.Lba44990Sense = lba44990SenseBuffer; - report.GdRomSwapDiscCapabilities.Lba44990DecodedSense = Sense.PrettifySense(lba44990SenseBuffer); - report.GdRomSwapDiscCapabilities.Lba44990ReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba44990Readable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990Readable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 100000 with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba100000PqReadable = !_dev.ReadCd(out byte[] lba100000PqBuffer, - out byte[] lba100000PqSenseBuffer, - 100000, 2368, cluster, - MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, - MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba100000PqData = lba100000PqBuffer; - report.GdRomSwapDiscCapabilities.Lba100000PqSense = lba100000PqSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba100000PqDecodedSense = Sense.PrettifySense(lba100000PqSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba100000PqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba100000PqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000PqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 50000 with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba50000PqReadable = !_dev.ReadCd(out byte[] lba50000PqBuffer, - out byte[] lba50000PqSenseBuffer, 50000, - 2368, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Q16, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba50000PqData = lba50000PqBuffer; - report.GdRomSwapDiscCapabilities.Lba50000PqSense = lba50000PqSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba50000PqDecodedSense = Sense.PrettifySense(lba50000PqSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba50000PqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba50000PqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000PqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 450000 with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba450000PqReadable = !_dev.ReadCd(out byte[] lba450000PqBuffer, - out byte[] lba450000PqSenseBuffer, - 450000, 2368, cluster, - MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, - MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba450000PqData = lba450000PqBuffer; - report.GdRomSwapDiscCapabilities.Lba450000PqSense = lba450000PqSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba450000PqDecodedSense = Sense.PrettifySense(lba450000PqSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba450000PqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba450000PqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000PqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 400000 with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba400000PqReadable = !_dev.ReadCd(out byte[] lba400000PqBuffer, - out byte[] lba400000PqSenseBuffer, - 400000, 2368, cluster, - MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, - MmcSubchannel.Q16, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba400000PqData = lba400000PqBuffer; - report.GdRomSwapDiscCapabilities.Lba400000PqSense = lba400000PqSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba400000PqDecodedSense = Sense.PrettifySense(lba400000PqSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba400000PqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba400000PqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000PqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 45000 with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba45000PqReadable = !_dev.ReadCd(out byte[] lba45000PqBuffer, - out byte[] lba45000PqSenseBuffer, 45000, - 2368, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Q16, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba45000PqData = lba45000PqBuffer; - report.GdRomSwapDiscCapabilities.Lba45000PqSense = lba45000PqSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba45000PqDecodedSense = Sense.PrettifySense(lba45000PqSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba45000PqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba45000PqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000PqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 44990 with PQ subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba44990PqReadable = !_dev.ReadCd(out byte[] lba44990PqBuffer, - out byte[] lba44990PqSenseBuffer, 44990, - 2368, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Q16, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba44990PqData = lba44990PqBuffer; - report.GdRomSwapDiscCapabilities.Lba44990PqSense = lba44990PqSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba44990PqDecodedSense = Sense.PrettifySense(lba44990PqSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba44990PqReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba44990PqReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990PqReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 100000 with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba100000RwReadable = !_dev.ReadCd(out byte[] lba100000RwBuffer, - out byte[] lba100000RwSenseBuffer, - 100000, 2448, cluster, - MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, - MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba100000RwData = lba100000RwBuffer; - report.GdRomSwapDiscCapabilities.Lba100000RwSense = lba100000RwSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba100000RwDecodedSense = Sense.PrettifySense(lba100000RwSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba100000RwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba100000RwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba100000RwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 50000 with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba50000RwReadable = !_dev.ReadCd(out byte[] lba50000RwBuffer, - out byte[] lba50000RwSenseBuffer, 50000, - 2448, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Raw, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba50000RwData = lba50000RwBuffer; - report.GdRomSwapDiscCapabilities.Lba50000RwSense = lba50000RwSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba50000RwDecodedSense = Sense.PrettifySense(lba50000RwSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba50000RwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba50000RwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba50000RwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 450000 with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba450000RwReadable = !_dev.ReadCd(out byte[] lba450000RwBuffer, - out byte[] lba450000RwSenseBuffer, - 450000, 2448, cluster, - MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, - MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba450000RwData = lba450000RwBuffer; - report.GdRomSwapDiscCapabilities.Lba450000RwSense = lba450000RwSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba450000RwDecodedSense = Sense.PrettifySense(lba450000RwSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba450000RwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba450000RwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba450000RwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 400000 with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba400000RwReadable = !_dev.ReadCd(out byte[] lba400000RwBuffer, - out byte[] lba400000RwSenseBuffer, - 400000, 2448, cluster, - MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, - MmcSubchannel.Raw, _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba400000RwData = lba400000RwBuffer; - report.GdRomSwapDiscCapabilities.Lba400000RwSense = lba400000RwSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba400000RwDecodedSense = Sense.PrettifySense(lba400000RwSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba400000RwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba400000RwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba400000RwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 45000 with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba45000RwReadable = !_dev.ReadCd(out byte[] lba45000RwBuffer, - out byte[] lba45000RwSenseBuffer, 45000, - 2448, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Raw, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba45000RwData = lba45000RwBuffer; - report.GdRomSwapDiscCapabilities.Lba45000RwSense = lba45000RwSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba45000RwDecodedSense = Sense.PrettifySense(lba45000RwSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba45000RwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba45000RwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba45000RwReadable ? "Success!" : "FAIL!"); - - AaruConsole.Write("Reading LBA 44990 with RW subchannel... "); - cluster = 16; - - while(true) - { - report.GdRomSwapDiscCapabilities.Lba44990RwReadable = !_dev.ReadCd(out byte[] lba44990RwBuffer, - out byte[] lba44990RwSenseBuffer, 44990, - 2448, cluster, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Raw, - _dev.Timeout, out _); - - report.GdRomSwapDiscCapabilities.Lba44990RwData = lba44990RwBuffer; - report.GdRomSwapDiscCapabilities.Lba44990RwSense = lba44990RwSenseBuffer; - report.GdRomSwapDiscCapabilities.Lba44990RwDecodedSense = Sense.PrettifySense(lba44990RwSenseBuffer); - report.GdRomSwapDiscCapabilities.Lba44990RwReadableCluster = (int)cluster; - - if(report.GdRomSwapDiscCapabilities.Lba44990RwReadable) - break; - - if(cluster == 1) - break; - - cluster /= 2; - } - - AaruConsole.WriteLine(report.GdRomSwapDiscCapabilities.Lba44990RwReadable ? "Success!" : "FAIL!"); - - if(report.GdRomSwapDiscCapabilities.Lba45000Readable == false && - report.GdRomSwapDiscCapabilities.Lba50000Readable == false && - report.GdRomSwapDiscCapabilities.Lba100000Readable == false && - report.GdRomSwapDiscCapabilities.Lba400000Readable == false && - report.GdRomSwapDiscCapabilities.Lba450000Readable == false && - report.GdRomSwapDiscCapabilities.Lba45000AudioReadable == false && - report.GdRomSwapDiscCapabilities.Lba50000AudioReadable == false && - report.GdRomSwapDiscCapabilities.Lba100000AudioReadable == false && - report.GdRomSwapDiscCapabilities.Lba400000AudioReadable == false && - report.GdRomSwapDiscCapabilities.Lba450000AudioReadable == false) - return; - - pressedKey = new ConsoleKeyInfo(); - - while(pressedKey.Key != ConsoleKey.Y && - pressedKey.Key != ConsoleKey.N) - { - AaruConsole. - Write("The next part of the test will read the whole high density area of a GD-ROM from the smallest known readable sector until the first error happens\n" + - "Do you want to proceed? (Y/N): "); - - pressedKey = System.Console.ReadKey(); AaruConsole.WriteLine(); + AaruConsole.WriteLine("Aborted!"); + + break; } - if(pressedKey.Key == ConsoleKey.N) - return; + AaruConsole.Write("\rReading LBA {0} of {1}", lba, sectors); - uint startingSector = 45000; - bool readAsAudio = false; - bool aborted = false; - MmcSubchannel subchannel = MmcSubchannel.None; - uint blockSize = 2352; + sense = readAsAudio + ? _dev.ReadCd(out buffer, out senseBuffer, lba, blockSize, cluster, MmcSectorTypes.Cdda, + false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, + subchannel, _dev.Timeout, out _) : _dev.ReadCd(out buffer, out senseBuffer, + lba, blockSize, cluster, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, subchannel, + _dev.Timeout, out _); - if(report.GdRomSwapDiscCapabilities.Lba45000Readable == false) + if(sense) { - startingSector = 45000; - readAsAudio = false; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba45000ReadableCluster; + if(trackModeChange) + break; - if(report.GdRomSwapDiscCapabilities.Lba45000RwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba45000PqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba50000Readable == false) - { - startingSector = 50000; - readAsAudio = false; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba50000ReadableCluster; + DecodedSense? decoded = Sense.Decode(senseBuffer); - if(report.GdRomSwapDiscCapabilities.Lba50000RwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba50000PqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba100000Readable == false) - { - startingSector = 100000; - readAsAudio = false; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba100000ReadableCluster; + if(decoded?.ASC != 0x64) + break; - if(report.GdRomSwapDiscCapabilities.Lba100000RwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba100000PqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba400000Readable == false) - { - startingSector = 400000; - readAsAudio = false; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba400000ReadableCluster; + if(decoded?.ASCQ != 0x00) + break; - if(report.GdRomSwapDiscCapabilities.Lba400000RwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba400000PqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba450000Readable == false) - { - startingSector = 450000; - readAsAudio = false; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba450000ReadableCluster; + trackModeChange = true; + readAsAudio = !readAsAudio; - if(report.GdRomSwapDiscCapabilities.Lba450000RwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba450000PqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba45000AudioReadable == false) - { - startingSector = 45000; - readAsAudio = true; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba45000AudioReadableCluster; - - if(report.GdRomSwapDiscCapabilities.Lba45000AudioRwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba45000AudioPqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba50000AudioReadable == false) - { - startingSector = 50000; - readAsAudio = true; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba50000AudioReadableCluster; - - if(report.GdRomSwapDiscCapabilities.Lba50000AudioRwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba50000AudioPqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba100000AudioReadable == false) - { - startingSector = 100000; - readAsAudio = true; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba100000AudioReadableCluster; - - if(report.GdRomSwapDiscCapabilities.Lba100000AudioRwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba100000AudioPqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba400000AudioReadable == false) - { - startingSector = 400000; - readAsAudio = true; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba400000AudioReadableCluster; - - if(report.GdRomSwapDiscCapabilities.Lba400000AudioRwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba400000AudioPqReadable) - subchannel = MmcSubchannel.Q16; - } - else if(report.GdRomSwapDiscCapabilities.Lba450000AudioReadable == false) - { - startingSector = 450000; - readAsAudio = true; - cluster = (uint)report.GdRomSwapDiscCapabilities.Lba450000AudioReadableCluster; - - if(report.GdRomSwapDiscCapabilities.Lba450000AudioRwReadable) - subchannel = MmcSubchannel.Raw; - else if(report.GdRomSwapDiscCapabilities.Lba450000AudioPqReadable) - subchannel = MmcSubchannel.Q16; + continue; } - System.Console.CancelKeyPress += (sender, e) => - { - e.Cancel = true; - aborted = true; - }; - - report.GdRomSwapDiscCapabilities.MinimumReadableSectorInHdArea = startingSector; + trackModeChange = false; switch(subchannel) { case MmcSubchannel.Raw: - blockSize += 96; + lastSuccessfulRw = buffer; break; case MmcSubchannel.Q16: - blockSize += 16; + lastSuccessfulPq = buffer; break; } - byte[] lastSuccessfulPq = null; - byte[] lastSuccessfulRw = null; - bool trackModeChange = false; - - AaruConsole.WriteLine(); - - for(uint lba = startingSector; lba < sectors; lba += cluster) - { - if(aborted) - { - AaruConsole.WriteLine(); - AaruConsole.WriteLine("Aborted!"); - - break; - } - - AaruConsole.Write("\rReading LBA {0} of {1}", lba, sectors); - - sense = readAsAudio - ? _dev.ReadCd(out buffer, out senseBuffer, lba, blockSize, cluster, MmcSectorTypes.Cdda, - false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, - subchannel, _dev.Timeout, out _) : _dev.ReadCd(out buffer, out senseBuffer, - lba, blockSize, cluster, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, subchannel, - _dev.Timeout, out _); - - if(sense) - { - if(trackModeChange) - break; - - DecodedSense? decoded = Sense.Decode(senseBuffer); - - if(decoded?.ASC != 0x64) - break; - - if(decoded?.ASCQ != 0x00) - break; - - trackModeChange = true; - readAsAudio = !readAsAudio; - - continue; - } - - trackModeChange = false; - - switch(subchannel) - { - case MmcSubchannel.Raw: - lastSuccessfulRw = buffer; - - break; - case MmcSubchannel.Q16: - lastSuccessfulPq = buffer; - - break; - } - - report.GdRomSwapDiscCapabilities.MaximumReadableSectorInHdArea = lba + cluster - 1; - } - - AaruConsole.WriteLine(); - - report.GdRomSwapDiscCapabilities.MaximumReadablePqInHdArea = lastSuccessfulPq; - report.GdRomSwapDiscCapabilities.MaximumReadableRwInHdArea = lastSuccessfulRw; + report.GdRomSwapDiscCapabilities.MaximumReadableSectorInHdArea = lba + cluster - 1; } + + AaruConsole.WriteLine(); + + report.GdRomSwapDiscCapabilities.MaximumReadablePqInHdArea = lastSuccessfulPq; + report.GdRomSwapDiscCapabilities.MaximumReadableRwInHdArea = lastSuccessfulRw; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/MMC.cs b/Aaru.Core/Devices/Report/MMC.cs index a75e2ba6e..6ec8da17d 100644 --- a/Aaru.Core/Devices/Report/MMC.cs +++ b/Aaru.Core/Devices/Report/MMC.cs @@ -42,687 +42,782 @@ using Aaru.Devices; using Aaru.Helpers; using Spectre.Console; -namespace Aaru.Core.Devices.Report +namespace Aaru.Core.Devices.Report; + +public sealed partial class DeviceReport { - public sealed partial class DeviceReport + static byte[] ClearMmcFeatures(byte[] response) { - static byte[] ClearMmcFeatures(byte[] response) + uint offset = 8; + + while(offset + 4 < response.Length) { - uint offset = 8; + ushort code = (ushort)((response[offset + 0] << 8) + response[offset + 1]); + byte[] data = new byte[response[offset + 3] + 4]; - while(offset + 4 < response.Length) + if(code != 0x0108) { - ushort code = (ushort)((response[offset + 0] << 8) + response[offset + 1]); - byte[] data = new byte[response[offset + 3] + 4]; - - if(code != 0x0108) - { - offset += (uint)data.Length; - - continue; - } - - if(data.Length + offset > response.Length) - data = new byte[response.Length - offset]; - - Array.Copy(data, 4, response, offset + 4, data.Length - 4); offset += (uint)data.Length; + + continue; } - return response; + if(data.Length + offset > response.Length) + data = new byte[response.Length - offset]; + + Array.Copy(data, 4, response, offset + 4, data.Length - 4); + offset += (uint)data.Length; } - /// Creates a report for the GET CONFIGURATION response of an MMC device - /// MMC features report - public MmcFeatures ReportMmcFeatures() + return response; + } + + /// Creates a report for the GET CONFIGURATION response of an MMC device + /// MMC features report + public MmcFeatures ReportMmcFeatures() + { + bool sense = true; + byte[] buffer = Array.Empty(); + + Spectre.ProgressSingleSpinner(ctx => { - bool sense = true; - byte[] buffer = Array.Empty(); + ctx.AddTask("Querying MMC GET CONFIGURATION...").IsIndeterminate(); + sense = _dev.GetConfiguration(out buffer, out _, _dev.Timeout, out _); + }); - Spectre.ProgressSingleSpinner(ctx => + if(sense) + return null; + + Features.SeparatedFeatures ftr = Features.Separate(buffer); + + if(ftr.Descriptors == null || + ftr.Descriptors.Length <= 0) + return null; + + var report = new MmcFeatures + { + BinaryData = ClearMmcFeatures(buffer) + }; + + foreach(Features.FeatureDescriptor desc in ftr.Descriptors) + switch(desc.Code) { - ctx.AddTask("Querying MMC GET CONFIGURATION...").IsIndeterminate(); - sense = _dev.GetConfiguration(out buffer, out _, _dev.Timeout, out _); - }); - - if(sense) - return null; - - Features.SeparatedFeatures ftr = Features.Separate(buffer); - - if(ftr.Descriptors == null || - ftr.Descriptors.Length <= 0) - return null; - - var report = new MmcFeatures - { - BinaryData = ClearMmcFeatures(buffer) - }; - - foreach(Features.FeatureDescriptor desc in ftr.Descriptors) - switch(desc.Code) + case 0x0001: { - case 0x0001: + Feature_0001? ftr0001 = Features.Decode_0001(desc.Data); + + if(ftr0001.HasValue) { - Feature_0001? ftr0001 = Features.Decode_0001(desc.Data); + report.PhysicalInterfaceStandardNumber = (uint)ftr0001.Value.PhysicalInterfaceStandard; - if(ftr0001.HasValue) - { - report.PhysicalInterfaceStandardNumber = (uint)ftr0001.Value.PhysicalInterfaceStandard; - - report.SupportsDeviceBusyEvent = ftr0001.Value.DBE; - } + report.SupportsDeviceBusyEvent = ftr0001.Value.DBE; } - - break; - case 0x0003: - { - Feature_0003? ftr0003 = Features.Decode_0003(desc.Data); - - if(ftr0003.HasValue) - { - report.LoadingMechanismType = ftr0003.Value.LoadingMechanismType; - report.CanLoad = ftr0003.Value.Load; - report.CanEject = ftr0003.Value.Eject; - report.PreventJumper = ftr0003.Value.PreventJumper; - report.DBML = ftr0003.Value.DBML; - report.Locked = ftr0003.Value.Lock; - } - } - - break; - case 0x0004: - { - Feature_0004? ftr0004 = Features.Decode_0004(desc.Data); - - if(ftr0004.HasValue) - { - report.SupportsWriteProtectPAC = ftr0004.Value.DWP; - report.SupportsWriteInhibitDCB = ftr0004.Value.WDCB; - report.SupportsPWP = ftr0004.Value.SPWP; - report.SupportsSWPP = ftr0004.Value.SSWPP; - } - } - - break; - case 0x0010: - { - Feature_0010? ftr0010 = Features.Decode_0010(desc.Data); - - if(ftr0010.HasValue) - { - if(ftr0010.Value.LogicalBlockSize > 0) - report.LogicalBlockSize = ftr0010.Value.LogicalBlockSize; - - if(ftr0010.Value.Blocking > 0) - report.BlocksPerReadableUnit = ftr0010.Value.Blocking; - - report.ErrorRecoveryPage = ftr0010.Value.PP; - } - } - - break; - case 0x001D: - report.MultiRead = true; - - break; - case 0x001E: - { - report.CanReadCD = true; - Feature_001E? ftr001E = Features.Decode_001E(desc.Data); - - if(ftr001E.HasValue) - { - report.SupportsDAP = ftr001E.Value.DAP; - report.SupportsC2 = ftr001E.Value.C2; - report.CanReadLeadInCDText = ftr001E.Value.CDText; - } - } - - break; - case 0x001F: - { - report.CanReadDVD = true; - Feature_001F? ftr001F = Features.Decode_001F(desc.Data); - - if(ftr001F.HasValue) - { - report.DVDMultiRead = ftr001F.Value.MULTI110; - report.CanReadAllDualRW = ftr001F.Value.DualRW; - report.CanReadAllDualR = ftr001F.Value.DualR; - } - } - - break; - case 0x0022: - report.CanEraseSector = true; - - break; - case 0x0023: - { - report.CanFormat = true; - Feature_0023? ftr0023 = Features.Decode_0023(desc.Data); - - if(ftr0023.HasValue) - { - report.CanFormatBDREWithoutSpare = ftr0023.Value.RENoSA; - report.CanExpandBDRESpareArea = ftr0023.Value.Expand; - report.CanFormatQCert = ftr0023.Value.QCert; - report.CanFormatCert = ftr0023.Value.Cert; - report.CanFormatFRF = ftr0023.Value.FRF; - report.CanFormatRRM = ftr0023.Value.RRM; - } - } - - break; - case 0x0024: - report.CanReadSpareAreaInformation = true; - - break; - case 0x0027: - report.CanWriteCDRWCAV = true; - - break; - case 0x0028: - { - report.CanReadCDMRW = true; - Feature_0028? ftr0028 = Features.Decode_0028(desc.Data); - - if(ftr0028.HasValue) - { - report.CanReadDVDPlusMRW = ftr0028.Value.DVDPRead; - report.CanWriteDVDPlusMRW = ftr0028.Value.DVDPWrite; - report.CanWriteCDMRW = ftr0028.Value.Write; - } - } - - break; - case 0x002A: - { - report.CanReadDVDPlusRW = true; - Feature_002A? ftr002A = Features.Decode_002A(desc.Data); - - if(ftr002A.HasValue) - report.CanWriteDVDPlusRW = ftr002A.Value.Write; - } - - break; - case 0x002B: - { - report.CanReadDVDPlusR = true; - Feature_002B? ftr002B = Features.Decode_002B(desc.Data); - - if(ftr002B.HasValue) - report.CanWriteDVDPlusR = ftr002B.Value.Write; - } - - break; - case 0x002D: - { - report.CanWriteCDTAO = true; - Feature_002D? ftr002D = Features.Decode_002D(desc.Data); - - if(ftr002D.HasValue) - { - report.BufferUnderrunFreeInTAO = ftr002D.Value.BUF; - report.CanWriteRawSubchannelInTAO = ftr002D.Value.RWRaw; - report.CanWritePackedSubchannelInTAO = ftr002D.Value.RWPack; - report.CanTestWriteInTAO = ftr002D.Value.TestWrite; - report.CanOverwriteTAOTrack = ftr002D.Value.CDRW; - report.CanWriteRWSubchannelInTAO = ftr002D.Value.RWSubchannel; - } - } - - break; - case 0x002E: - { - report.CanWriteCDSAO = true; - Feature_002E? ftr002E = Features.Decode_002E(desc.Data); - - if(ftr002E.HasValue) - { - report.BufferUnderrunFreeInSAO = ftr002E.Value.BUF; - report.CanWriteRawMultiSession = ftr002E.Value.RAWMS; - report.CanWriteRaw = ftr002E.Value.RAW; - report.CanTestWriteInSAO = ftr002E.Value.TestWrite; - report.CanOverwriteSAOTrack = ftr002E.Value.CDRW; - report.CanWriteRWSubchannelInSAO = ftr002E.Value.RW; - } - } - - break; - case 0x002F: - { - report.CanWriteDVDR = true; - Feature_002F? ftr002F = Features.Decode_002F(desc.Data); - - if(ftr002F.HasValue) - { - report.BufferUnderrunFreeInDVD = ftr002F.Value.BUF; - report.CanWriteDVDRDL = ftr002F.Value.RDL; - report.CanTestWriteDVD = ftr002F.Value.TestWrite; - report.CanWriteDVDRW = ftr002F.Value.DVDRW; - } - } - - break; - case 0x0030: - report.CanReadDDCD = true; - - break; - case 0x0031: - { - report.CanWriteDDCDR = true; - Feature_0031? ftr0031 = Features.Decode_0031(desc.Data); - - if(ftr0031.HasValue) - report.CanTestWriteDDCDR = ftr0031.Value.TestWrite; - } - - break; - case 0x0032: - report.CanWriteDDCDRW = true; - - break; - case 0x0037: - report.CanWriteCDRW = true; - - break; - case 0x0038: - report.CanPseudoOverwriteBDR = true; - - break; - case 0x003A: - { - report.CanReadDVDPlusRWDL = true; - Feature_003A? ftr003A = Features.Decode_003A(desc.Data); - - if(ftr003A.HasValue) - report.CanWriteDVDPlusRWDL = ftr003A.Value.Write; - } - - break; - case 0x003B: - { - report.CanReadDVDPlusRDL = true; - Feature_003B? ftr003B = Features.Decode_003B(desc.Data); - - if(ftr003B.HasValue) - report.CanWriteDVDPlusRDL = ftr003B.Value.Write; - } - - break; - case 0x0040: - { - report.CanReadBD = true; - Feature_0040? ftr0040 = Features.Decode_0040(desc.Data); - - if(ftr0040.HasValue) - { - report.CanReadBluBCA = ftr0040.Value.BCA; - report.CanReadBDRE2 = ftr0040.Value.RE2; - report.CanReadBDRE1 = ftr0040.Value.RE1; - report.CanReadOldBDRE = ftr0040.Value.OldRE; - report.CanReadBDR = ftr0040.Value.R; - report.CanReadOldBDR = ftr0040.Value.OldR; - report.CanReadBDROM = ftr0040.Value.ROM; - report.CanReadOldBDROM = ftr0040.Value.OldROM; - } - } - - break; - case 0x0041: - { - report.CanWriteBD = true; - Feature_0041? ftr0041 = Features.Decode_0041(desc.Data); - - if(ftr0041.HasValue) - { - report.CanWriteBDRE2 = ftr0041.Value.RE2; - report.CanWriteBDRE1 = ftr0041.Value.RE1; - report.CanWriteOldBDRE = ftr0041.Value.OldRE; - report.CanWriteBDR = ftr0041.Value.R; - report.CanWriteOldBDR = ftr0041.Value.OldR; - } - } - - break; - case 0x0050: - { - report.CanReadHDDVD = true; - Feature_0050? ftr0050 = Features.Decode_0050(desc.Data); - - if(ftr0050.HasValue) - { - report.CanReadHDDVDR = ftr0050.Value.HDDVDR; - report.CanReadHDDVDRAM = ftr0050.Value.HDDVDRAM; - } - } - - break; - case 0x0051: - { - // TODO: Write HD DVD-RW - Feature_0051? ftr0051 = Features.Decode_0051(desc.Data); - - if(ftr0051.HasValue) - { - report.CanWriteHDDVDR = ftr0051.Value.HDDVDR; - report.CanWriteHDDVDRAM = ftr0051.Value.HDDVDRAM; - } - } - - break; - case 0x0080: - report.SupportsHybridDiscs = true; - - break; - case 0x0101: - report.SupportsModePage1Ch = true; - - break; - case 0x0102: - { - report.EmbeddedChanger = true; - Feature_0102? ftr0102 = Features.Decode_0102(desc.Data); - - if(ftr0102.HasValue) - { - report.ChangerIsSideChangeCapable = ftr0102.Value.SCC; - report.ChangerSupportsDiscPresent = ftr0102.Value.SDP; - report.ChangerSlots = (byte)(ftr0102.Value.HighestSlotNumber + 1); - } - } - - break; - case 0x0103: - { - report.CanPlayCDAudio = true; - Feature_0103? ftr0103 = Features.Decode_0103(desc.Data); - - if(ftr0103.HasValue) - { - report.CanAudioScan = ftr0103.Value.Scan; - report.CanMuteSeparateChannels = ftr0103.Value.SCM; - report.SupportsSeparateVolume = ftr0103.Value.SV; - - if(ftr0103.Value.VolumeLevels > 0) - report.VolumeLevels = ftr0103.Value.VolumeLevels; - } - } - - break; - case 0x0104: - report.CanUpgradeFirmware = true; - - break; - case 0x0106: - { - report.SupportsCSS = true; - Feature_0106? ftr0106 = Features.Decode_0106(desc.Data); - - if(ftr0106?.CSSVersion > 0) - report.CSSVersion = ftr0106.Value.CSSVersion; - } - - break; - case 0x0108: - report.CanReportDriveSerial = true; - - break; - case 0x0109: - report.CanReportMediaSerial = true; - - break; - case 0x010B: - { - report.SupportsCPRM = true; - Feature_010B? ftr010B = Features.Decode_010B(desc.Data); - - if(ftr010B?.CPRMVersion > 0) - report.CPRMVersion = ftr010B.Value.CPRMVersion; - } - - break; - case 0x010C: - { - Feature_010C? ftr010C = Features.Decode_010C(desc.Data); - - if(ftr010C.HasValue) - { - byte[] temp = new byte[4]; - temp[0] = (byte)((ftr010C.Value.Century & 0xFF00) >> 8); - temp[1] = (byte)(ftr010C.Value.Century & 0xFF); - temp[2] = (byte)((ftr010C.Value.Year & 0xFF00) >> 8); - temp[3] = (byte)(ftr010C.Value.Year & 0xFF); - string syear = Encoding.ASCII.GetString(temp); - temp = new byte[2]; - temp[0] = (byte)((ftr010C.Value.Month & 0xFF00) >> 8); - temp[1] = (byte)(ftr010C.Value.Month & 0xFF); - string smonth = Encoding.ASCII.GetString(temp); - temp = new byte[2]; - temp[0] = (byte)((ftr010C.Value.Day & 0xFF00) >> 8); - temp[1] = (byte)(ftr010C.Value.Day & 0xFF); - string sday = Encoding.ASCII.GetString(temp); - temp = new byte[2]; - temp[0] = (byte)((ftr010C.Value.Hour & 0xFF00) >> 8); - temp[1] = (byte)(ftr010C.Value.Hour & 0xFF); - string shour = Encoding.ASCII.GetString(temp); - temp = new byte[2]; - temp[0] = (byte)((ftr010C.Value.Minute & 0xFF00) >> 8); - temp[1] = (byte)(ftr010C.Value.Minute & 0xFF); - string sminute = Encoding.ASCII.GetString(temp); - temp = new byte[2]; - temp[0] = (byte)((ftr010C.Value.Second & 0xFF00) >> 8); - temp[1] = (byte)(ftr010C.Value.Second & 0xFF); - string ssecond = Encoding.ASCII.GetString(temp); - - try - { - report.FirmwareDate = new DateTime(int.Parse(syear), int.Parse(smonth), int.Parse(sday), - int.Parse(shour), int.Parse(sminute), - int.Parse(ssecond), DateTimeKind.Utc); - } - #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body - catch - { - // ignored - } - #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body - } - } - - break; - case 0x010D: - { - report.SupportsAACS = true; - Feature_010D? ftr010D = Features.Decode_010D(desc.Data); - - if(ftr010D.HasValue) - { - report.CanReadDriveAACSCertificate = ftr010D.Value.RDC; - report.CanReadCPRM_MKB = ftr010D.Value.RMC; - report.CanWriteBusEncryptedBlocks = ftr010D.Value.WBE; - report.SupportsBusEncryption = ftr010D.Value.BEC; - report.CanGenerateBindingNonce = ftr010D.Value.BNG; - - if(ftr010D.Value.BindNonceBlocks > 0) - report.BindingNonceBlocks = ftr010D.Value.BindNonceBlocks; - - if(ftr010D.Value.AGIDs > 0) - report.AGIDs = ftr010D.Value.AGIDs; - - if(ftr010D.Value.AACSVersion > 0) - report.AACSVersion = ftr010D.Value.AACSVersion; - } - } - - break; - case 0x010E: - report.CanWriteCSSManagedDVD = true; - - break; - case 0x0113: - report.SupportsSecurDisc = true; - - break; - case 0x0142: - report.SupportsOSSC = true; - - break; - case 0x0110: - report.SupportsVCPS = true; - - break; } - return report; + break; + case 0x0003: + { + Feature_0003? ftr0003 = Features.Decode_0003(desc.Data); + + if(ftr0003.HasValue) + { + report.LoadingMechanismType = ftr0003.Value.LoadingMechanismType; + report.CanLoad = ftr0003.Value.Load; + report.CanEject = ftr0003.Value.Eject; + report.PreventJumper = ftr0003.Value.PreventJumper; + report.DBML = ftr0003.Value.DBML; + report.Locked = ftr0003.Value.Lock; + } + } + + break; + case 0x0004: + { + Feature_0004? ftr0004 = Features.Decode_0004(desc.Data); + + if(ftr0004.HasValue) + { + report.SupportsWriteProtectPAC = ftr0004.Value.DWP; + report.SupportsWriteInhibitDCB = ftr0004.Value.WDCB; + report.SupportsPWP = ftr0004.Value.SPWP; + report.SupportsSWPP = ftr0004.Value.SSWPP; + } + } + + break; + case 0x0010: + { + Feature_0010? ftr0010 = Features.Decode_0010(desc.Data); + + if(ftr0010.HasValue) + { + if(ftr0010.Value.LogicalBlockSize > 0) + report.LogicalBlockSize = ftr0010.Value.LogicalBlockSize; + + if(ftr0010.Value.Blocking > 0) + report.BlocksPerReadableUnit = ftr0010.Value.Blocking; + + report.ErrorRecoveryPage = ftr0010.Value.PP; + } + } + + break; + case 0x001D: + report.MultiRead = true; + + break; + case 0x001E: + { + report.CanReadCD = true; + Feature_001E? ftr001E = Features.Decode_001E(desc.Data); + + if(ftr001E.HasValue) + { + report.SupportsDAP = ftr001E.Value.DAP; + report.SupportsC2 = ftr001E.Value.C2; + report.CanReadLeadInCDText = ftr001E.Value.CDText; + } + } + + break; + case 0x001F: + { + report.CanReadDVD = true; + Feature_001F? ftr001F = Features.Decode_001F(desc.Data); + + if(ftr001F.HasValue) + { + report.DVDMultiRead = ftr001F.Value.MULTI110; + report.CanReadAllDualRW = ftr001F.Value.DualRW; + report.CanReadAllDualR = ftr001F.Value.DualR; + } + } + + break; + case 0x0022: + report.CanEraseSector = true; + + break; + case 0x0023: + { + report.CanFormat = true; + Feature_0023? ftr0023 = Features.Decode_0023(desc.Data); + + if(ftr0023.HasValue) + { + report.CanFormatBDREWithoutSpare = ftr0023.Value.RENoSA; + report.CanExpandBDRESpareArea = ftr0023.Value.Expand; + report.CanFormatQCert = ftr0023.Value.QCert; + report.CanFormatCert = ftr0023.Value.Cert; + report.CanFormatFRF = ftr0023.Value.FRF; + report.CanFormatRRM = ftr0023.Value.RRM; + } + } + + break; + case 0x0024: + report.CanReadSpareAreaInformation = true; + + break; + case 0x0027: + report.CanWriteCDRWCAV = true; + + break; + case 0x0028: + { + report.CanReadCDMRW = true; + Feature_0028? ftr0028 = Features.Decode_0028(desc.Data); + + if(ftr0028.HasValue) + { + report.CanReadDVDPlusMRW = ftr0028.Value.DVDPRead; + report.CanWriteDVDPlusMRW = ftr0028.Value.DVDPWrite; + report.CanWriteCDMRW = ftr0028.Value.Write; + } + } + + break; + case 0x002A: + { + report.CanReadDVDPlusRW = true; + Feature_002A? ftr002A = Features.Decode_002A(desc.Data); + + if(ftr002A.HasValue) + report.CanWriteDVDPlusRW = ftr002A.Value.Write; + } + + break; + case 0x002B: + { + report.CanReadDVDPlusR = true; + Feature_002B? ftr002B = Features.Decode_002B(desc.Data); + + if(ftr002B.HasValue) + report.CanWriteDVDPlusR = ftr002B.Value.Write; + } + + break; + case 0x002D: + { + report.CanWriteCDTAO = true; + Feature_002D? ftr002D = Features.Decode_002D(desc.Data); + + if(ftr002D.HasValue) + { + report.BufferUnderrunFreeInTAO = ftr002D.Value.BUF; + report.CanWriteRawSubchannelInTAO = ftr002D.Value.RWRaw; + report.CanWritePackedSubchannelInTAO = ftr002D.Value.RWPack; + report.CanTestWriteInTAO = ftr002D.Value.TestWrite; + report.CanOverwriteTAOTrack = ftr002D.Value.CDRW; + report.CanWriteRWSubchannelInTAO = ftr002D.Value.RWSubchannel; + } + } + + break; + case 0x002E: + { + report.CanWriteCDSAO = true; + Feature_002E? ftr002E = Features.Decode_002E(desc.Data); + + if(ftr002E.HasValue) + { + report.BufferUnderrunFreeInSAO = ftr002E.Value.BUF; + report.CanWriteRawMultiSession = ftr002E.Value.RAWMS; + report.CanWriteRaw = ftr002E.Value.RAW; + report.CanTestWriteInSAO = ftr002E.Value.TestWrite; + report.CanOverwriteSAOTrack = ftr002E.Value.CDRW; + report.CanWriteRWSubchannelInSAO = ftr002E.Value.RW; + } + } + + break; + case 0x002F: + { + report.CanWriteDVDR = true; + Feature_002F? ftr002F = Features.Decode_002F(desc.Data); + + if(ftr002F.HasValue) + { + report.BufferUnderrunFreeInDVD = ftr002F.Value.BUF; + report.CanWriteDVDRDL = ftr002F.Value.RDL; + report.CanTestWriteDVD = ftr002F.Value.TestWrite; + report.CanWriteDVDRW = ftr002F.Value.DVDRW; + } + } + + break; + case 0x0030: + report.CanReadDDCD = true; + + break; + case 0x0031: + { + report.CanWriteDDCDR = true; + Feature_0031? ftr0031 = Features.Decode_0031(desc.Data); + + if(ftr0031.HasValue) + report.CanTestWriteDDCDR = ftr0031.Value.TestWrite; + } + + break; + case 0x0032: + report.CanWriteDDCDRW = true; + + break; + case 0x0037: + report.CanWriteCDRW = true; + + break; + case 0x0038: + report.CanPseudoOverwriteBDR = true; + + break; + case 0x003A: + { + report.CanReadDVDPlusRWDL = true; + Feature_003A? ftr003A = Features.Decode_003A(desc.Data); + + if(ftr003A.HasValue) + report.CanWriteDVDPlusRWDL = ftr003A.Value.Write; + } + + break; + case 0x003B: + { + report.CanReadDVDPlusRDL = true; + Feature_003B? ftr003B = Features.Decode_003B(desc.Data); + + if(ftr003B.HasValue) + report.CanWriteDVDPlusRDL = ftr003B.Value.Write; + } + + break; + case 0x0040: + { + report.CanReadBD = true; + Feature_0040? ftr0040 = Features.Decode_0040(desc.Data); + + if(ftr0040.HasValue) + { + report.CanReadBluBCA = ftr0040.Value.BCA; + report.CanReadBDRE2 = ftr0040.Value.RE2; + report.CanReadBDRE1 = ftr0040.Value.RE1; + report.CanReadOldBDRE = ftr0040.Value.OldRE; + report.CanReadBDR = ftr0040.Value.R; + report.CanReadOldBDR = ftr0040.Value.OldR; + report.CanReadBDROM = ftr0040.Value.ROM; + report.CanReadOldBDROM = ftr0040.Value.OldROM; + } + } + + break; + case 0x0041: + { + report.CanWriteBD = true; + Feature_0041? ftr0041 = Features.Decode_0041(desc.Data); + + if(ftr0041.HasValue) + { + report.CanWriteBDRE2 = ftr0041.Value.RE2; + report.CanWriteBDRE1 = ftr0041.Value.RE1; + report.CanWriteOldBDRE = ftr0041.Value.OldRE; + report.CanWriteBDR = ftr0041.Value.R; + report.CanWriteOldBDR = ftr0041.Value.OldR; + } + } + + break; + case 0x0050: + { + report.CanReadHDDVD = true; + Feature_0050? ftr0050 = Features.Decode_0050(desc.Data); + + if(ftr0050.HasValue) + { + report.CanReadHDDVDR = ftr0050.Value.HDDVDR; + report.CanReadHDDVDRAM = ftr0050.Value.HDDVDRAM; + } + } + + break; + case 0x0051: + { + // TODO: Write HD DVD-RW + Feature_0051? ftr0051 = Features.Decode_0051(desc.Data); + + if(ftr0051.HasValue) + { + report.CanWriteHDDVDR = ftr0051.Value.HDDVDR; + report.CanWriteHDDVDRAM = ftr0051.Value.HDDVDRAM; + } + } + + break; + case 0x0080: + report.SupportsHybridDiscs = true; + + break; + case 0x0101: + report.SupportsModePage1Ch = true; + + break; + case 0x0102: + { + report.EmbeddedChanger = true; + Feature_0102? ftr0102 = Features.Decode_0102(desc.Data); + + if(ftr0102.HasValue) + { + report.ChangerIsSideChangeCapable = ftr0102.Value.SCC; + report.ChangerSupportsDiscPresent = ftr0102.Value.SDP; + report.ChangerSlots = (byte)(ftr0102.Value.HighestSlotNumber + 1); + } + } + + break; + case 0x0103: + { + report.CanPlayCDAudio = true; + Feature_0103? ftr0103 = Features.Decode_0103(desc.Data); + + if(ftr0103.HasValue) + { + report.CanAudioScan = ftr0103.Value.Scan; + report.CanMuteSeparateChannels = ftr0103.Value.SCM; + report.SupportsSeparateVolume = ftr0103.Value.SV; + + if(ftr0103.Value.VolumeLevels > 0) + report.VolumeLevels = ftr0103.Value.VolumeLevels; + } + } + + break; + case 0x0104: + report.CanUpgradeFirmware = true; + + break; + case 0x0106: + { + report.SupportsCSS = true; + Feature_0106? ftr0106 = Features.Decode_0106(desc.Data); + + if(ftr0106?.CSSVersion > 0) + report.CSSVersion = ftr0106.Value.CSSVersion; + } + + break; + case 0x0108: + report.CanReportDriveSerial = true; + + break; + case 0x0109: + report.CanReportMediaSerial = true; + + break; + case 0x010B: + { + report.SupportsCPRM = true; + Feature_010B? ftr010B = Features.Decode_010B(desc.Data); + + if(ftr010B?.CPRMVersion > 0) + report.CPRMVersion = ftr010B.Value.CPRMVersion; + } + + break; + case 0x010C: + { + Feature_010C? ftr010C = Features.Decode_010C(desc.Data); + + if(ftr010C.HasValue) + { + byte[] temp = new byte[4]; + temp[0] = (byte)((ftr010C.Value.Century & 0xFF00) >> 8); + temp[1] = (byte)(ftr010C.Value.Century & 0xFF); + temp[2] = (byte)((ftr010C.Value.Year & 0xFF00) >> 8); + temp[3] = (byte)(ftr010C.Value.Year & 0xFF); + string syear = Encoding.ASCII.GetString(temp); + temp = new byte[2]; + temp[0] = (byte)((ftr010C.Value.Month & 0xFF00) >> 8); + temp[1] = (byte)(ftr010C.Value.Month & 0xFF); + string smonth = Encoding.ASCII.GetString(temp); + temp = new byte[2]; + temp[0] = (byte)((ftr010C.Value.Day & 0xFF00) >> 8); + temp[1] = (byte)(ftr010C.Value.Day & 0xFF); + string sday = Encoding.ASCII.GetString(temp); + temp = new byte[2]; + temp[0] = (byte)((ftr010C.Value.Hour & 0xFF00) >> 8); + temp[1] = (byte)(ftr010C.Value.Hour & 0xFF); + string shour = Encoding.ASCII.GetString(temp); + temp = new byte[2]; + temp[0] = (byte)((ftr010C.Value.Minute & 0xFF00) >> 8); + temp[1] = (byte)(ftr010C.Value.Minute & 0xFF); + string sminute = Encoding.ASCII.GetString(temp); + temp = new byte[2]; + temp[0] = (byte)((ftr010C.Value.Second & 0xFF00) >> 8); + temp[1] = (byte)(ftr010C.Value.Second & 0xFF); + string ssecond = Encoding.ASCII.GetString(temp); + + try + { + report.FirmwareDate = new DateTime(int.Parse(syear), int.Parse(smonth), int.Parse(sday), + int.Parse(shour), int.Parse(sminute), + int.Parse(ssecond), DateTimeKind.Utc); + } + #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body + catch + { + // ignored + } + #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body + } + } + + break; + case 0x010D: + { + report.SupportsAACS = true; + Feature_010D? ftr010D = Features.Decode_010D(desc.Data); + + if(ftr010D.HasValue) + { + report.CanReadDriveAACSCertificate = ftr010D.Value.RDC; + report.CanReadCPRM_MKB = ftr010D.Value.RMC; + report.CanWriteBusEncryptedBlocks = ftr010D.Value.WBE; + report.SupportsBusEncryption = ftr010D.Value.BEC; + report.CanGenerateBindingNonce = ftr010D.Value.BNG; + + if(ftr010D.Value.BindNonceBlocks > 0) + report.BindingNonceBlocks = ftr010D.Value.BindNonceBlocks; + + if(ftr010D.Value.AGIDs > 0) + report.AGIDs = ftr010D.Value.AGIDs; + + if(ftr010D.Value.AACSVersion > 0) + report.AACSVersion = ftr010D.Value.AACSVersion; + } + } + + break; + case 0x010E: + report.CanWriteCSSManagedDVD = true; + + break; + case 0x0113: + report.SupportsSecurDisc = true; + + break; + case 0x0142: + report.SupportsOSSC = true; + + break; + case 0x0110: + report.SupportsVCPS = true; + + break; + } + + return report; + } + + /// Creates a report for media inserted into an MMC device + /// Expected media type name + /// Try Plextor vendor commands + /// Try Pioneer vendor commands + /// Try NEC vendor commands + /// Try HL-DT-ST vendor commands + /// Try MediaTek vendor commands + /// + public TestedMedia ReportMmcMedia(string mediaType, bool tryPlextor, bool tryPioneer, bool tryNec, + bool tryHldtst, bool tryMediaTekF106) + { + bool sense = true; + byte[] buffer = Array.Empty(); + byte[] senseBuffer = Array.Empty(); + var mediaTest = new TestedMedia(); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI READ CAPACITY...").IsIndeterminate(); + sense = _dev.ReadCapacity(out buffer, out senseBuffer, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + mediaTest.SupportsReadCapacity = true; + + mediaTest.Blocks = ((ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) & + 0xFFFFFFFF) + 1; + + mediaTest.BlockSize = (uint)((buffer[5] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); } - /// Creates a report for media inserted into an MMC device - /// Expected media type name - /// Try Plextor vendor commands - /// Try Pioneer vendor commands - /// Try NEC vendor commands - /// Try HL-DT-ST vendor commands - /// Try MediaTek vendor commands - /// - public TestedMedia ReportMmcMedia(string mediaType, bool tryPlextor, bool tryPioneer, bool tryNec, - bool tryHldtst, bool tryMediaTekF106) + Spectre.ProgressSingleSpinner(ctx => { - bool sense = true; - byte[] buffer = Array.Empty(); - byte[] senseBuffer = Array.Empty(); - var mediaTest = new TestedMedia(); + ctx.AddTask("Querying SCSI READ CAPACITY (16)...").IsIndeterminate(); + sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + mediaTest.SupportsReadCapacity16 = true; + byte[] temp = new byte[8]; + Array.Copy(buffer, 0, temp, 0, 8); + Array.Reverse(temp); + mediaTest.Blocks = BitConverter.ToUInt64(temp, 0) + 1; + mediaTest.BlockSize = (uint)((buffer[5] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); + } + + Modes.DecodedMode? decMode = null; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate(); + + sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, + 0x3F, 0x00, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); + + mediaTest.ModeSense10Data = buffer; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate(); + sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); + + mediaTest.ModeSense6Data = buffer; + } + + if(decMode != null) + { + mediaTest.MediumType = (byte?)decMode?.Header.MediumType; + + if(decMode?.Header.BlockDescriptors?.Length > 0) + mediaTest.Density = (byte?)decMode?.Header.BlockDescriptors?[0].Density; + } + + if(mediaType.StartsWith("CD-", StringComparison.Ordinal) || + mediaType.StartsWith("DDCD-", StringComparison.Ordinal) || + mediaType == "Audio CD") + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying CD TOC...").IsIndeterminate(); + + mediaTest.CanReadTOC = + !_dev.ReadTocPmaAtip(out buffer, out senseBuffer, false, 0, 0, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadTOC); + mediaTest.TocData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying SCSI READ CAPACITY...").IsIndeterminate(); - sense = _dev.ReadCapacity(out buffer, out senseBuffer, _dev.Timeout, out _); + ctx.AddTask("Querying CD Full TOC...").IsIndeterminate(); + mediaTest.CanReadFullTOC = !_dev.ReadRawToc(out buffer, out senseBuffer, 1, _dev.Timeout, out _); }); - if(!sense && - !_dev.Error) + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadFullTOC); + mediaTest.FullTocData = buffer; + } + + if(mediaType.StartsWith("CD-R", StringComparison.Ordinal) || + mediaType.StartsWith("DDCD-R", StringComparison.Ordinal)) + { + Spectre.ProgressSingleSpinner(ctx => { - mediaTest.SupportsReadCapacity = true; + ctx.AddTask("Querying CD ATIP...").IsIndeterminate(); + mediaTest.CanReadATIP = !_dev.ReadAtip(out buffer, out senseBuffer, _dev.Timeout, out _); + }); - mediaTest.Blocks = ((ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) & - 0xFFFFFFFF) + 1; + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadATIP); - mediaTest.BlockSize = (uint)((buffer[5] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); - } + mediaTest.AtipData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying SCSI READ CAPACITY (16)...").IsIndeterminate(); - sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _); + ctx.AddTask("Querying CD PMA...").IsIndeterminate(); + mediaTest.CanReadPMA = !_dev.ReadPma(out buffer, out senseBuffer, _dev.Timeout, out _); }); - if(!sense && - !_dev.Error) - { - mediaTest.SupportsReadCapacity16 = true; - byte[] temp = new byte[8]; - Array.Copy(buffer, 0, temp, 0, 8); - Array.Reverse(temp); - mediaTest.Blocks = BitConverter.ToUInt64(temp, 0) + 1; - mediaTest.BlockSize = (uint)((buffer[5] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); - } + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPMA); - Modes.DecodedMode? decMode = null; + mediaTest.PmaData = buffer; + } + + if(mediaType.StartsWith("DVD-", StringComparison.Ordinal) || + mediaType.StartsWith("DVD+", StringComparison.Ordinal) || + mediaType.StartsWith("HD DVD-", StringComparison.Ordinal) || + mediaType.StartsWith("PD-", StringComparison.Ordinal)) + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying DVD PFI...").IsIndeterminate(); + + mediaTest.CanReadPFI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, + _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPFI); + + mediaTest.PfiData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate(); + ctx.AddTask("Querying DVD DMI...").IsIndeterminate(); - sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, - 0x3F, 0x00, _dev.Timeout, out _); + mediaTest.CanReadDMI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DiscManufacturingInformation, + 0, _dev.Timeout, out _); }); - if(!sense && - !_dev.Error) - { - decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDMI); - mediaTest.ModeSense10Data = buffer; - } + mediaTest.DmiData = buffer; + } + if(mediaType == "DVD-ROM") + { Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate(); - sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _); + ctx.AddTask("Querying DVD CMI...").IsIndeterminate(); + + mediaTest.CanReadCMI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.CopyrightInformation, 0, + _dev.Timeout, out _); }); - if(!sense && - !_dev.Error) - { - decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadCMI); - mediaTest.ModeSense6Data = buffer; - } + mediaTest.CmiData = buffer; + } - if(decMode != null) - { - mediaTest.MediumType = (byte?)decMode?.Header.MediumType; - - if(decMode?.Header.BlockDescriptors?.Length > 0) - mediaTest.Density = (byte?)decMode?.Header.BlockDescriptors?[0].Density; - } - - if(mediaType.StartsWith("CD-", StringComparison.Ordinal) || - mediaType.StartsWith("DDCD-", StringComparison.Ordinal) || - mediaType == "Audio CD") - { + switch(mediaType) + { + case "DVD-ROM": + case "HD DVD-ROM": Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying CD TOC...").IsIndeterminate(); + ctx.AddTask("Querying DVD BCA...").IsIndeterminate(); - mediaTest.CanReadTOC = - !_dev.ReadTocPmaAtip(out buffer, out senseBuffer, false, 0, 0, _dev.Timeout, out _); + mediaTest.CanReadBCA = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.BurstCuttingArea, 0, + _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadTOC); - mediaTest.TocData = buffer; + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadBCA); + + mediaTest.DvdBcaData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying CD Full TOC...").IsIndeterminate(); - mediaTest.CanReadFullTOC = !_dev.ReadRawToc(out buffer, out senseBuffer, 1, _dev.Timeout, out _); + ctx.AddTask("Querying DVD AACS...").IsIndeterminate(); + + mediaTest.CanReadAACS = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdAacs, 0, _dev.Timeout, + out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadFullTOC); - mediaTest.FullTocData = buffer; - } + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadAACS); - if(mediaType.StartsWith("CD-R", StringComparison.Ordinal) || - mediaType.StartsWith("DDCD-R", StringComparison.Ordinal)) - { + mediaTest.DvdAacsData = buffer; + + break; + case "Nintendo GameCube game": + case "Nintendo Wii game": Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying CD ATIP...").IsIndeterminate(); - mediaTest.CanReadATIP = !_dev.ReadAtip(out buffer, out senseBuffer, _dev.Timeout, out _); + ctx.AddTask("Querying DVD BCA...").IsIndeterminate(); + + mediaTest.CanReadBCA = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.BurstCuttingArea, 0, + _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadATIP); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadBCA); - mediaTest.AtipData = buffer; + mediaTest.DvdBcaData = buffer; - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying CD PMA...").IsIndeterminate(); - mediaTest.CanReadPMA = !_dev.ReadPma(out buffer, out senseBuffer, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPMA); - - mediaTest.PmaData = buffer; - } - - if(mediaType.StartsWith("DVD-", StringComparison.Ordinal) || - mediaType.StartsWith("DVD+", StringComparison.Ordinal) || - mediaType.StartsWith("HD DVD-", StringComparison.Ordinal) || - mediaType.StartsWith("PD-", StringComparison.Ordinal)) - { Spectre.ProgressSingleSpinner(ctx => { ctx.AddTask("Querying DVD PFI...").IsIndeterminate(); @@ -750,1520 +845,1424 @@ namespace Aaru.Core.Devices.Report AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDMI); mediaTest.DmiData = buffer; - } - if(mediaType == "DVD-ROM") - { + break; + case "BD-ROM": + case "Ultra HD Blu-ray movie": + case "PlayStation 3 game": + case "PlayStation 4 game": + case "PlayStation 5 game": + case "Xbox One game": + case "Nintendo Wii U game": Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying DVD CMI...").IsIndeterminate(); + ctx.AddTask("Querying BD BCA...").IsIndeterminate(); - mediaTest.CanReadCMI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.CopyrightInformation, 0, + mediaTest.CanReadBCA = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdBurstCuttingArea, 0, _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadCMI); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadBCA); - mediaTest.CmiData = buffer; - } + mediaTest.BluBcaData = buffer; - switch(mediaType) - { - case "DVD-ROM": - case "HD DVD-ROM": - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD BCA...").IsIndeterminate(); - - mediaTest.CanReadBCA = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.BurstCuttingArea, 0, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadBCA); - - mediaTest.DvdBcaData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD AACS...").IsIndeterminate(); - - mediaTest.CanReadAACS = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdAacs, 0, _dev.Timeout, - out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadAACS); - - mediaTest.DvdAacsData = buffer; - - break; - case "Nintendo GameCube game": - case "Nintendo Wii game": - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD BCA...").IsIndeterminate(); - - mediaTest.CanReadBCA = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.BurstCuttingArea, 0, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadBCA); - - mediaTest.DvdBcaData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD PFI...").IsIndeterminate(); - - mediaTest.CanReadPFI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPFI); - - mediaTest.PfiData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD DMI...").IsIndeterminate(); - - mediaTest.CanReadDMI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DiscManufacturingInformation, - 0, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDMI); - - mediaTest.DmiData = buffer; - - break; - case "BD-ROM": - case "Ultra HD Blu-ray movie": - case "PlayStation 3 game": - case "PlayStation 4 game": - case "PlayStation 5 game": - case "Xbox One game": - case "Nintendo Wii U game": - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying BD BCA...").IsIndeterminate(); - - mediaTest.CanReadBCA = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdBurstCuttingArea, 0, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadBCA); - - mediaTest.BluBcaData = buffer; - - break; - case "DVD-RAM (1st gen, marked 2.6Gb or 5.2Gb)": - case "DVD-RAM (2nd gen, marked 4.7Gb or 9.4Gb)": - case "HD DVD-RAM": - case "PD-650": - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying Disc Definition Structure...").IsIndeterminate(); - - mediaTest.CanReadDDS = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdramDds, 0, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDDS); - - mediaTest.DvdDdsData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying Spare Area Information...").IsIndeterminate(); - - mediaTest.CanReadSpareAreaInformation = - !_dev.ReadDiscStructure(out buffer, out senseBuffer, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdramSpareAreaInformation, 0, _dev.Timeout, - out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadSpareAreaInformation); - - mediaTest.DvdSaiData = buffer; - - break; - } - - if(mediaType.StartsWith("BD-R", StringComparison.Ordinal) && - mediaType != "BD-ROM") - { + break; + case "DVD-RAM (1st gen, marked 2.6Gb or 5.2Gb)": + case "DVD-RAM (2nd gen, marked 4.7Gb or 9.4Gb)": + case "HD DVD-RAM": + case "PD-650": Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying BD DDS...").IsIndeterminate(); + ctx.AddTask("Querying Disc Definition Structure...").IsIndeterminate(); mediaTest.CanReadDDS = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdDds, 0, _dev.Timeout, - out _); + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdramDds, 0, + _dev.Timeout, out _); }); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDDS); - mediaTest.BluDdsData = buffer; + mediaTest.DvdDdsData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying BD SAI...").IsIndeterminate(); + ctx.AddTask("Querying Spare Area Information...").IsIndeterminate(); mediaTest.CanReadSpareAreaInformation = - !_dev.ReadDiscStructure(out buffer, out senseBuffer, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdSpareAreaInformation, 0, _dev.Timeout, out _); + !_dev.ReadDiscStructure(out buffer, out senseBuffer, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdramSpareAreaInformation, 0, _dev.Timeout, + out _); }); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadSpareAreaInformation); - mediaTest.BluSaiData = buffer; - } + mediaTest.DvdSaiData = buffer; - if(mediaType == "DVD-R" || - mediaType == "DVD-RW") + break; + } + + if(mediaType.StartsWith("BD-R", StringComparison.Ordinal) && + mediaType != "BD-ROM") + { + Spectre.ProgressSingleSpinner(ctx => { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD PRI...").IsIndeterminate(); + ctx.AddTask("Querying BD DDS...").IsIndeterminate(); - mediaTest.CanReadPRI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + mediaTest.CanReadDDS = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdDds, 0, _dev.Timeout, + out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDDS); + + mediaTest.BluDdsData = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying BD SAI...").IsIndeterminate(); + + mediaTest.CanReadSpareAreaInformation = + !_dev.ReadDiscStructure(out buffer, out senseBuffer, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdSpareAreaInformation, 0, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadSpareAreaInformation); + + mediaTest.BluSaiData = buffer; + } + + if(mediaType == "DVD-R" || + mediaType == "DVD-RW") + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying DVD PRI...").IsIndeterminate(); + + mediaTest.CanReadPRI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PreRecordedInfo, 0, + _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPRI); + + mediaTest.PriData = buffer; + } + + if(mediaType == "DVD-R" || + mediaType == "DVD-RW" || + mediaType == "HD DVD-R") + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying DVD Media ID...").IsIndeterminate(); + + mediaTest.CanReadMediaID = !_dev.ReadDiscStructure(out buffer, out senseBuffer, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PreRecordedInfo, 0, + MmcDiscStructureFormat.DvdrMediaIdentifier, 0, _dev.Timeout, out _); - }); + }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPRI); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadMediaID); - mediaTest.PriData = buffer; - } + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying DVD Embossed PFI...").IsIndeterminate(); - if(mediaType == "DVD-R" || - mediaType == "DVD-RW" || - mediaType == "HD DVD-R") + mediaTest.CanReadRecordablePFI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat. + DvdrPhysicalInformation, 0, + _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadRecordablePFI); + + mediaTest.EmbossedPfiData = buffer; + } + + if(mediaType.StartsWith("DVD+R", StringComparison.Ordinal) || + mediaType == "DVD+MRW") + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying DVD ADIP...").IsIndeterminate(); + + mediaTest.CanReadADIP = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.Adip, 0, _dev.Timeout, + out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadADIP); + + mediaTest.AdipData = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying DVD DCB...").IsIndeterminate(); + + mediaTest.CanReadDCB = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.Dcb, 0, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDCB); + + mediaTest.DcbData = buffer; + } + + if(mediaType == "HD DVD-ROM") + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying HD DVD CMI...").IsIndeterminate(); + + mediaTest.CanReadHDCMI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.HddvdCopyrightInformation, + 0, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadHDCMI); + + mediaTest.HdCmiData = buffer; + } + + if(mediaType.EndsWith(" DL", StringComparison.Ordinal)) + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying DVD Layer Capacity...").IsIndeterminate(); + + mediaTest.CanReadLayerCapacity = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdrLayerCapacity, + 0, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadLayerCapacity); + + mediaTest.DvdLayerData = buffer; + } + + if(mediaType.StartsWith("BD-R", StringComparison.Ordinal) || + mediaType == "Ultra HD Blu-ray movie" || + mediaType == "PlayStation 3 game" || + mediaType == "PlayStation 4 game" || + mediaType == "PlayStation 5 game" || + mediaType == "Xbox One game" || + mediaType == "Nintendo Wii game") + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying BD Disc Information...").IsIndeterminate(); + + mediaTest.CanReadDiscInformation = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.DiscInformation, + 0, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDiscInformation); + + mediaTest.BluDiData = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying BD PAC...").IsIndeterminate(); + + mediaTest.CanReadPAC = !_dev.ReadDiscStructure(out buffer, out senseBuffer, + MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.Pac, 0, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPAC); + + mediaTest.BluPacData = buffer; + } + + if(mediaType.StartsWith("CD-", StringComparison.Ordinal)) + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ CD scrambled...").IsIndeterminate(); + + mediaTest.CanReadCdScrambled = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2352, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); + }); + + mediaTest.ReadCdScrambledData = buffer; + } + + if(mediaType.StartsWith("PD-", StringComparison.Ordinal)) + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate(); + mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 16, 512, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); + + mediaTest.Read6Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate(); + + mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, true, false, false, + 16, 512, 0, 1, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); + + mediaTest.Read10Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate(); + + mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, true, false, false, + 16, 512, 0, 1, false, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); + + mediaTest.Read12Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate(); + + mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, true, false, 16, 512, + 0, 1, false, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); + + mediaTest.Read16Data = buffer; + } + else + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate(); + mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 16, 2048, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); + + mediaTest.Read6Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate(); + + mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, true, false, false, + 16, 2048, 0, 1, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); + + mediaTest.Read10Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate(); + + mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, true, false, false, + 16, 2048, 0, 1, false, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); + + mediaTest.Read12Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate(); + + mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, true, false, 16, + 2048, 0, 1, false, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); + + mediaTest.Read16Data = buffer; + } + + if(mediaType.StartsWith("CD-", StringComparison.Ordinal) || + mediaType.StartsWith("DDCD-", StringComparison.Ordinal) || + mediaType == "Audio CD") + { + if(mediaType == "Audio CD") { Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying DVD Media ID...").IsIndeterminate(); + ctx.AddTask("Trying SCSI READ CD...").IsIndeterminate(); - mediaTest.CanReadMediaID = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdrMediaIdentifier, 0, - _dev.Timeout, out _); + mediaTest.SupportsReadCd = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2352, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadMediaID); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCd); + + mediaTest.ReadCdFullData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Querying DVD Embossed PFI...").IsIndeterminate(); + ctx.AddTask("Trying SCSI READ CD MSF...").IsIndeterminate(); - mediaTest.CanReadRecordablePFI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat. - DvdrPhysicalInformation, 0, - _dev.Timeout, out _); + mediaTest.SupportsReadCdMsf = !_dev.ReadCdMsf(out buffer, out senseBuffer, 0x00000210, + 0x00000211, 2352, MmcSectorTypes.Cdda, false, + false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadRecordablePFI); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCdMsf); - mediaTest.EmbossedPfiData = buffer; - } - - if(mediaType.StartsWith("DVD+R", StringComparison.Ordinal) || - mediaType == "DVD+MRW") - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD ADIP...").IsIndeterminate(); - - mediaTest.CanReadADIP = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.Adip, 0, _dev.Timeout, - out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadADIP); - - mediaTest.AdipData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD DCB...").IsIndeterminate(); - - mediaTest.CanReadDCB = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.Dcb, 0, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDCB); - - mediaTest.DcbData = buffer; - } - - if(mediaType == "HD DVD-ROM") - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying HD DVD CMI...").IsIndeterminate(); - - mediaTest.CanReadHDCMI = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.HddvdCopyrightInformation, - 0, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadHDCMI); - - mediaTest.HdCmiData = buffer; - } - - if(mediaType.EndsWith(" DL", StringComparison.Ordinal)) - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying DVD Layer Capacity...").IsIndeterminate(); - - mediaTest.CanReadLayerCapacity = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdrLayerCapacity, - 0, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadLayerCapacity); - - mediaTest.DvdLayerData = buffer; - } - - if(mediaType.StartsWith("BD-R", StringComparison.Ordinal) || - mediaType == "Ultra HD Blu-ray movie" || - mediaType == "PlayStation 3 game" || - mediaType == "PlayStation 4 game" || - mediaType == "PlayStation 5 game" || - mediaType == "Xbox One game" || - mediaType == "Nintendo Wii game") - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying BD Disc Information...").IsIndeterminate(); - - mediaTest.CanReadDiscInformation = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.DiscInformation, - 0, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadDiscInformation); - - mediaTest.BluDiData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying BD PAC...").IsIndeterminate(); - - mediaTest.CanReadPAC = !_dev.ReadDiscStructure(out buffer, out senseBuffer, - MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.Pac, 0, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPAC); - - mediaTest.BluPacData = buffer; - } - - if(mediaType.StartsWith("CD-", StringComparison.Ordinal)) - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ CD scrambled...").IsIndeterminate(); - - mediaTest.CanReadCdScrambled = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2352, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); - }); - - mediaTest.ReadCdScrambledData = buffer; - } - - if(mediaType.StartsWith("PD-", StringComparison.Ordinal)) - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate(); - mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 16, 512, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); - - mediaTest.Read6Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate(); - - mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, true, false, false, - 16, 512, 0, 1, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); - - mediaTest.Read10Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate(); - - mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, true, false, false, - 16, 512, 0, 1, false, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); - - mediaTest.Read12Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate(); - - mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, true, false, 16, 512, - 0, 1, false, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); - - mediaTest.Read16Data = buffer; + mediaTest.ReadCdMsfFullData = buffer; } else { Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate(); - mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 16, 2048, _dev.Timeout, out _); + ctx.AddTask("Trying SCSI READ CD...").IsIndeterminate(); + + mediaTest.SupportsReadCd = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2048, 1, + MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCd); - mediaTest.Read6Data = buffer; + mediaTest.ReadCdData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate(); + ctx.AddTask("Trying SCSI READ CD MSF...").IsIndeterminate(); - mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, true, false, false, - 16, 2048, 0, 1, _dev.Timeout, out _); + mediaTest.SupportsReadCdMsf = !_dev.ReadCdMsf(out buffer, out senseBuffer, 0x00000210, + 0x00000211, 2048, MmcSectorTypes.AllTypes, false, + false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCdMsf); - mediaTest.Read10Data = buffer; + mediaTest.ReadCdMsfData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate(); + ctx.AddTask("Trying SCSI READ CD full sector...").IsIndeterminate(); - mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, true, false, false, - 16, 2048, 0, 1, false, _dev.Timeout, out _); + mediaTest.SupportsReadCdRaw = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2352, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, + out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCdRaw); - mediaTest.Read12Data = buffer; + mediaTest.ReadCdFullData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate(); + ctx.AddTask("Trying SCSI READ CD MSF full sector...").IsIndeterminate(); - mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, true, false, 16, - 2048, 0, 1, false, _dev.Timeout, out _); + mediaTest.SupportsReadCdMsfRaw = !_dev.ReadCdMsf(out buffer, out senseBuffer, 0x00000210, + 0x00000211, 2352, MmcSectorTypes.AllTypes, + false, false, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCdMsfRaw); - mediaTest.Read16Data = buffer; + mediaTest.ReadCdMsfFullData = buffer; } - if(mediaType.StartsWith("CD-", StringComparison.Ordinal) || - mediaType.StartsWith("DDCD-", StringComparison.Ordinal) || - mediaType == "Audio CD") + if(mediaTest.SupportsReadCdRaw == true || + mediaType == "Audio CD") { - if(mediaType == "Audio CD") + Spectre.ProgressSingleSpinner(ctx => { - Spectre.ProgressSingleSpinner(ctx => + ctx.AddTask("Trying to read CD Track 1 pre-gap...").IsIndeterminate(); + + for(int i = -10; i < 0; i++) { - ctx.AddTask("Trying SCSI READ CD...").IsIndeterminate(); - - mediaTest.SupportsReadCd = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2352, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCd); - - mediaTest.ReadCdFullData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ CD MSF...").IsIndeterminate(); - - mediaTest.SupportsReadCdMsf = !_dev.ReadCdMsf(out buffer, out senseBuffer, 0x00000210, - 0x00000211, 2352, MmcSectorTypes.Cdda, false, - false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCdMsf); - - mediaTest.ReadCdMsfFullData = buffer; - } - else - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ CD...").IsIndeterminate(); - - mediaTest.SupportsReadCd = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2048, 1, - MmcSectorTypes.AllTypes, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCd); - - mediaTest.ReadCdData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ CD MSF...").IsIndeterminate(); - - mediaTest.SupportsReadCdMsf = !_dev.ReadCdMsf(out buffer, out senseBuffer, 0x00000210, - 0x00000211, 2048, MmcSectorTypes.AllTypes, false, - false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCdMsf); - - mediaTest.ReadCdMsfData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ CD full sector...").IsIndeterminate(); - - mediaTest.SupportsReadCdRaw = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2352, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, - out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCdRaw); - - mediaTest.ReadCdFullData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ CD MSF full sector...").IsIndeterminate(); - - mediaTest.SupportsReadCdMsfRaw = !_dev.ReadCdMsf(out buffer, out senseBuffer, 0x00000210, - 0x00000211, 2352, MmcSectorTypes.AllTypes, - false, false, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsReadCdMsfRaw); - - mediaTest.ReadCdMsfFullData = buffer; - } - - if(mediaTest.SupportsReadCdRaw == true || - mediaType == "Audio CD") - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read CD Track 1 pre-gap...").IsIndeterminate(); - - for(int i = -10; i < 0; i++) - { - // ReSharper disable IntVariableOverflowInUncheckedContext - if(mediaType == "Audio CD") - sense = _dev.ReadCd(out buffer, out senseBuffer, (uint)i, 2352, 1, MmcSectorTypes.Cdda, - false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out _); - else - sense = _dev.ReadCd(out buffer, out senseBuffer, (uint)i, 2352, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); - - // ReSharper restore IntVariableOverflowInUncheckedContext - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", sense); - - mediaTest.Track1PregapData = buffer; - - if(sense) - continue; - - mediaTest.CanReadFirstTrackPreGap = true; - - break; - } - }); - - mediaTest.CanReadFirstTrackPreGap ??= false; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read CD Lead-In...").IsIndeterminate(); - - foreach(int i in new[] - { - -5000, -4000, -3000, -2000, -1000, -500, -250 - }) - { - if(mediaType == "Audio CD") - sense = _dev.ReadCd(out buffer, out senseBuffer, (uint)i, 2352, 1, MmcSectorTypes.Cdda, - false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out _); - else - sense = _dev.ReadCd(out buffer, out senseBuffer, (uint)i, 2352, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", sense); - - mediaTest.LeadInData = buffer; - - if(sense) - continue; - - mediaTest.CanReadLeadIn = true; - - break; - } - }); - - mediaTest.CanReadLeadIn ??= false; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read CD Lead-Out...").IsIndeterminate(); - + // ReSharper disable IntVariableOverflowInUncheckedContext if(mediaType == "Audio CD") - mediaTest.CanReadLeadOut = !_dev.ReadCd(out buffer, out senseBuffer, - (uint)(mediaTest.Blocks + 1), 2352, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); + sense = _dev.ReadCd(out buffer, out senseBuffer, (uint)i, 2352, 1, MmcSectorTypes.Cdda, + false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out _); else - mediaTest.CanReadLeadOut = !_dev.ReadCd(out buffer, out senseBuffer, - (uint)(mediaTest.Blocks + 1), 2352, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); - }); + sense = _dev.ReadCd(out buffer, out senseBuffer, (uint)i, 2352, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadLeadOut); + // ReSharper restore IntVariableOverflowInUncheckedContext - mediaTest.LeadOutData = buffer; - } + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", sense); - if(mediaType == "Audio CD") + mediaTest.Track1PregapData = buffer; + + if(sense) + continue; + + mediaTest.CanReadFirstTrackPreGap = true; + + break; + } + }); + + mediaTest.CanReadFirstTrackPreGap ??= false; + + Spectre.ProgressSingleSpinner(ctx => { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read C2 Pointers...").IsIndeterminate(); - mediaTest.CanReadPMA = !_dev.ReadPma(out buffer, out senseBuffer, _dev.Timeout, out _); + ctx.AddTask("Trying to read CD Lead-In...").IsIndeterminate(); - // They return OK, but then all following commands make the drive fail miserably. - if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) - mediaTest.CanReadC2Pointers = false; + foreach(int i in new[] + { + -5000, -4000, -3000, -2000, -1000, -500, -250 + }) + { + if(mediaType == "Audio CD") + sense = _dev.ReadCd(out buffer, out senseBuffer, (uint)i, 2352, 1, MmcSectorTypes.Cdda, + false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out _); else - { - mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2646, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.C2Pointers, MmcSubchannel.None, - _dev.Timeout, out _); + sense = _dev.ReadCd(out buffer, out senseBuffer, (uint)i, 2352, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); - if(!mediaTest.CanReadC2Pointers == true) - mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2648, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.C2PointersAndBlock, - MmcSubchannel.None, _dev.Timeout, out _); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", sense); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadC2Pointers); + mediaTest.LeadInData = buffer; - mediaTest.C2PointersData = buffer; - } - }); + if(sense) + continue; - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read subchannels...").IsIndeterminate(); + mediaTest.CanReadLeadIn = true; - mediaTest.CanReadPQSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2368, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Q16, - _dev.Timeout, out _); + break; + } + }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPQSubchannel); + mediaTest.CanReadLeadIn ??= false; - mediaTest.PQSubchannelData = buffer; - - mediaTest.CanReadRWSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2448, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Raw, - _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadRWSubchannel); - - mediaTest.RWSubchannelData = buffer; - - mediaTest.CanReadCorrectedSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2448, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Rw, - _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadCorrectedSubchannel); - - mediaTest.CorrectedSubchannelData = buffer; - }); - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read subchannels with C2 Pointers...").IsIndeterminate(); - mediaTest.CanReadPMA = !_dev.ReadPma(out buffer, out senseBuffer, _dev.Timeout, out _); - - // They return OK, but then all following commands make the drive fail miserably. - if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) - { - mediaTest.CanReadPQSubchannelWithC2 = false; - mediaTest.CanReadRWSubchannelWithC2 = false; - mediaTest.CanReadCorrectedSubchannelWithC2 = false; - } - else - { - mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2662, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.C2Pointers, - MmcSubchannel.Q16, _dev.Timeout, out _); - - if(mediaTest.CanReadPQSubchannelWithC2 == false) - mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, - 2664, 1, MmcSectorTypes.Cdda, false, - false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Q16, _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadPQSubchannelWithC2); - - mediaTest.PQSubchannelWithC2Data = buffer; - - mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2712, 1, - MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.C2Pointers, - MmcSubchannel.Raw, _dev.Timeout, out _); - - if(mediaTest.CanReadRWSubchannelWithC2 == false) - mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, - 2714, 1, MmcSectorTypes.Cdda, false, - false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Raw, _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadRWSubchannelWithC2); - - mediaTest.RWSubchannelWithC2Data = buffer; - - mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, - 2712, 1, MmcSectorTypes.Cdda, false, - false, false, MmcHeaderCodes.None, - true, false, MmcErrorField.C2Pointers, - MmcSubchannel.Rw, _dev.Timeout, - out _); - - if(mediaTest.CanReadCorrectedSubchannelWithC2 == false) - mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, - 11, 2714, 1, MmcSectorTypes.Cdda, - false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Rw, _dev.Timeout, - out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadCorrectedSubchannelWithC2); - - mediaTest.CorrectedSubchannelWithC2Data = buffer; - } - }); - } - else if(mediaTest.SupportsReadCdRaw == true) + Spectre.ProgressSingleSpinner(ctx => { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read C2 Pointers...").IsIndeterminate(); + ctx.AddTask("Trying to read CD Lead-Out...").IsIndeterminate(); - // They return OK, but then all following commands make the drive fail miserably. - if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) - mediaTest.CanReadC2Pointers = false; - else - { - mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2646, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.C2Pointers, MmcSubchannel.None, - _dev.Timeout, out _); + if(mediaType == "Audio CD") + mediaTest.CanReadLeadOut = !_dev.ReadCd(out buffer, out senseBuffer, + (uint)(mediaTest.Blocks + 1), 2352, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + else + mediaTest.CanReadLeadOut = !_dev.ReadCd(out buffer, out senseBuffer, + (uint)(mediaTest.Blocks + 1), 2352, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + }); - if(mediaTest.CanReadC2Pointers == false) - mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2648, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.C2PointersAndBlock, - MmcSubchannel.None, _dev.Timeout, out _); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadLeadOut); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadC2Pointers); + mediaTest.LeadOutData = buffer; + } - mediaTest.C2PointersData = buffer; - } - }); - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read subchannels...").IsIndeterminate(); - - mediaTest.CanReadPQSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2368, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Q16, - _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPQSubchannel); - - mediaTest.PQSubchannelData = buffer; - - mediaTest.CanReadRWSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2448, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Raw, - _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadRWSubchannel); - - mediaTest.RWSubchannelData = buffer; - - mediaTest.CanReadCorrectedSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2448, 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.Rw, - _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadCorrectedSubchannel); - - mediaTest.CorrectedSubchannelData = buffer; - }); - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read subchannels with C2 Pointers...").IsIndeterminate(); - - if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) - { - mediaTest.CanReadPQSubchannelWithC2 = false; - mediaTest.CanReadRWSubchannelWithC2 = false; - mediaTest.CanReadCorrectedSubchannelWithC2 = false; - } - else - { - mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2662, 1, - MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.C2Pointers, - MmcSubchannel.Q16, _dev.Timeout, out _); - - if(mediaTest.CanReadPQSubchannelWithC2 == false) - mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, - 2664, 1, MmcSectorTypes.AllTypes, false, - false, true, MmcHeaderCodes.AllHeaders, - true, true, - MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Q16, _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadPQSubchannelWithC2); - - mediaTest.PQSubchannelWithC2Data = buffer; - - mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2712, 1, - MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.C2Pointers, - MmcSubchannel.Raw, _dev.Timeout, out _); - - if(mediaTest.CanReadRWSubchannelWithC2 == false) - mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, - 2714, 1, MmcSectorTypes.AllTypes, false, - false, true, MmcHeaderCodes.AllHeaders, - true, true, - MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Raw, _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadRWSubchannelWithC2); - - mediaTest.RWSubchannelWithC2Data = buffer; - - mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, - 2712, 1, MmcSectorTypes.AllTypes, - false, false, true, - MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.C2Pointers, - MmcSubchannel.Rw, _dev.Timeout, - out _); - - if(mediaTest.CanReadCorrectedSubchannelWithC2 == false) - mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, - 16, 2714, 1, - MmcSectorTypes.AllTypes, false, - false, true, - MmcHeaderCodes.AllHeaders, true, - true, - MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Rw, _dev.Timeout, - out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadCorrectedSubchannelWithC2); - - mediaTest.CorrectedSubchannelWithC2Data = buffer; - } - }); - } - else + if(mediaType == "Audio CD") + { + Spectre.ProgressSingleSpinner(ctx => { + ctx.AddTask("Trying to read C2 Pointers...").IsIndeterminate(); + mediaTest.CanReadPMA = !_dev.ReadPma(out buffer, out senseBuffer, _dev.Timeout, out _); + + // They return OK, but then all following commands make the drive fail miserably. if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) mediaTest.CanReadC2Pointers = false; else { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to read C2 Pointers...").IsIndeterminate(); + mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2646, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.C2Pointers, MmcSubchannel.None, + _dev.Timeout, out _); - mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2342, 1, - MmcSectorTypes.AllTypes, false, false, false, + if(!mediaTest.CanReadC2Pointers == true) + mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2648, 1, + MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.C2Pointers, MmcSubchannel.None, - _dev.Timeout, out _); - - if(mediaTest.CanReadC2Pointers == false) - mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2344, 1, - MmcSectorTypes.AllTypes, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.C2PointersAndBlock, - MmcSubchannel.None, _dev.Timeout, out _); - }); + MmcErrorField.C2PointersAndBlock, + MmcSubchannel.None, _dev.Timeout, out _); AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadC2Pointers); mediaTest.C2PointersData = buffer; } + }); - Spectre.ProgressSingleSpinner(ctx => + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to read subchannels...").IsIndeterminate(); + + mediaTest.CanReadPQSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2368, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Q16, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPQSubchannel); + + mediaTest.PQSubchannelData = buffer; + + mediaTest.CanReadRWSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2448, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Raw, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadRWSubchannel); + + mediaTest.RWSubchannelData = buffer; + + mediaTest.CanReadCorrectedSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2448, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Rw, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadCorrectedSubchannel); + + mediaTest.CorrectedSubchannelData = buffer; + }); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to read subchannels with C2 Pointers...").IsIndeterminate(); + mediaTest.CanReadPMA = !_dev.ReadPma(out buffer, out senseBuffer, _dev.Timeout, out _); + + // They return OK, but then all following commands make the drive fail miserably. + if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) { - ctx.AddTask("Trying to read subchannels...").IsIndeterminate(); - mediaTest.CanReadPMA = !_dev.ReadPma(out buffer, out senseBuffer, _dev.Timeout, out _); - - mediaTest.CanReadPQSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2064, 1, - MmcSectorTypes.AllTypes, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Q16, - _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPQSubchannel); - - mediaTest.PQSubchannelData = buffer; - - mediaTest.CanReadRWSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2144, 1, - MmcSectorTypes.AllTypes, false, false, false, - MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Raw, - _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadRWSubchannel); - - mediaTest.RWSubchannelData = buffer; - - mediaTest.CanReadCorrectedSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2144, 1, - MmcSectorTypes.AllTypes, false, false, - false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.Rw, - _dev.Timeout, out _); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadCorrectedSubchannel); - - mediaTest.CorrectedSubchannelData = buffer; - }); - - Spectre.ProgressSingleSpinner(ctx => + mediaTest.CanReadPQSubchannelWithC2 = false; + mediaTest.CanReadRWSubchannelWithC2 = false; + mediaTest.CanReadCorrectedSubchannelWithC2 = false; + } + else { - ctx.AddTask("Trying to read subchannels with C2 Pointers...").IsIndeterminate(); + mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2662, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.C2Pointers, + MmcSubchannel.Q16, _dev.Timeout, out _); - if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) - { - mediaTest.CanReadPQSubchannelWithC2 = false; - mediaTest.CanReadRWSubchannelWithC2 = false; - mediaTest.CanReadCorrectedSubchannelWithC2 = false; - } - else - { - mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2358, 1, - MmcSectorTypes.AllTypes, false, false, - false, MmcHeaderCodes.None, true, false, - MmcErrorField.C2Pointers, + if(mediaTest.CanReadPQSubchannelWithC2 == false) + mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, + 2664, 1, MmcSectorTypes.Cdda, false, + false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.C2PointersAndBlock, MmcSubchannel.Q16, _dev.Timeout, out _); - if(mediaTest.CanReadPQSubchannelWithC2 == false) - mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, - 2360, 1, MmcSectorTypes.AllTypes, false, - false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Q16, _dev.Timeout, out _); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadPQSubchannelWithC2); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadPQSubchannelWithC2); + mediaTest.PQSubchannelWithC2Data = buffer; - mediaTest.PQSubchannelWithC2Data = buffer; + mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, 2712, 1, + MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.C2Pointers, + MmcSubchannel.Raw, _dev.Timeout, out _); - mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2438, 1, - MmcSectorTypes.AllTypes, false, false, - false, MmcHeaderCodes.None, true, false, - MmcErrorField.C2Pointers, + if(mediaTest.CanReadRWSubchannelWithC2 == false) + mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, + 2714, 1, MmcSectorTypes.Cdda, false, + false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.C2PointersAndBlock, MmcSubchannel.Raw, _dev.Timeout, out _); - if(mediaTest.CanReadRWSubchannelWithC2 == false) - mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, - 2440, 1, MmcSectorTypes.AllTypes, false, - false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Raw, _dev.Timeout, out _); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadRWSubchannelWithC2); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadRWSubchannelWithC2); + mediaTest.RWSubchannelWithC2Data = buffer; - mediaTest.RWSubchannelWithC2Data = buffer; + mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 11, + 2712, 1, MmcSectorTypes.Cdda, false, + false, false, MmcHeaderCodes.None, + true, false, MmcErrorField.C2Pointers, + MmcSubchannel.Rw, _dev.Timeout, + out _); - mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, - 2438, 1, MmcSectorTypes.AllTypes, + if(mediaTest.CanReadCorrectedSubchannelWithC2 == false) + mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, + 11, 2714, 1, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.C2Pointers, + MmcErrorField.C2PointersAndBlock, MmcSubchannel.Rw, _dev.Timeout, out _); - if(mediaTest.CanReadCorrectedSubchannelWithC2 == false) - mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, - 16, 2440, 1, - MmcSectorTypes.AllTypes, false, - false, false, MmcHeaderCodes.None, - true, false, - MmcErrorField.C2PointersAndBlock, - MmcSubchannel.Rw, _dev.Timeout, - out _); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadCorrectedSubchannelWithC2); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadCorrectedSubchannelWithC2); - - mediaTest.CorrectedSubchannelWithC2Data = buffer; - } - }); - } - - if(tryPlextor) - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying Plextor READ CD-DA...").IsIndeterminate(); - - mediaTest.SupportsPlextorReadCDDA = - !_dev.PlextorReadCdDa(out buffer, out senseBuffer, 16, 2352, 1, PlextorSubchannel.None, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsPlextorReadCDDA); - - mediaTest.PlextorReadCddaData = buffer; - } - - if(tryPioneer) - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying Pioneer READ CD-DA...").IsIndeterminate(); - - mediaTest.SupportsPioneerReadCDDA = - !_dev.PioneerReadCdDa(out buffer, out senseBuffer, 16, 2352, 1, PioneerSubchannel.None, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsPioneerReadCDDA); - - mediaTest.PioneerReadCddaData = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying Pioneer READ CD-DA MSF...").IsIndeterminate(); - - mediaTest.SupportsPioneerReadCDDAMSF = - !_dev.PioneerReadCdDaMsf(out buffer, out senseBuffer, 0x00000210, 0x00000211, 2352, - PioneerSubchannel.None, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsPioneerReadCDDAMSF); - - mediaTest.PioneerReadCddaMsfData = buffer; - } - - if(tryNec) - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying NEC READ CD-DA...").IsIndeterminate(); - - mediaTest.SupportsNECReadCDDA = - !_dev.NecReadCdDa(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsNECReadCDDA); - - mediaTest.NecReadCddaData = buffer; - } - } - - mediaTest.LongBlockSize = mediaTest.BlockSize; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ LONG (10)...").IsIndeterminate(); - sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 16, 0xFFFF, _dev.Timeout, out _); - }); - - if(sense && !_dev.Error) - { - DecodedSense? decSense = Sense.Decode(senseBuffer); - - if(decSense?.SenseKey == SenseKeys.IllegalRequest && - decSense.Value.ASC == 0x24 && - decSense.Value.ASCQ == 0x00) - { - mediaTest.SupportsReadLong = true; - - bool valid = decSense?.Fixed?.InformationValid == true; - bool ili = decSense?.Fixed?.ILI == true; - uint information = decSense?.Fixed?.Information ?? 0; - - if(decSense?.Descriptor.HasValue == true && - decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) - { - valid = true; - ili = true; - information = (uint)Sense.DecodeDescriptor00(desc00); + mediaTest.CorrectedSubchannelWithC2Data = buffer; } - - if(valid && ili) - mediaTest.LongBlockSize = 0xFFFF - (information & 0xFFFF); - } + }); } - - if(mediaTest.SupportsReadLong == true && - mediaTest.LongBlockSize == mediaTest.BlockSize) + else if(mediaTest.SupportsReadCdRaw == true) { - // DVDs - sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 16, 37856, _dev.Timeout, out _); - - if(!sense && - !_dev.Error) + Spectre.ProgressSingleSpinner(ctx => { - mediaTest.ReadLong10Data = buffer; - mediaTest.SupportsReadLong = true; - mediaTest.LongBlockSize = 37856; + ctx.AddTask("Trying to read C2 Pointers...").IsIndeterminate(); + + // They return OK, but then all following commands make the drive fail miserably. + if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) + mediaTest.CanReadC2Pointers = false; + else + { + mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2646, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.C2Pointers, MmcSubchannel.None, + _dev.Timeout, out _); + + if(mediaTest.CanReadC2Pointers == false) + mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2648, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.C2PointersAndBlock, + MmcSubchannel.None, _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadC2Pointers); + + mediaTest.C2PointersData = buffer; + } + }); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to read subchannels...").IsIndeterminate(); + + mediaTest.CanReadPQSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2368, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Q16, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPQSubchannel); + + mediaTest.PQSubchannelData = buffer; + + mediaTest.CanReadRWSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2448, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Raw, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadRWSubchannel); + + mediaTest.RWSubchannelData = buffer; + + mediaTest.CanReadCorrectedSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2448, 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.Rw, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadCorrectedSubchannel); + + mediaTest.CorrectedSubchannelData = buffer; + }); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to read subchannels with C2 Pointers...").IsIndeterminate(); + + if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) + { + mediaTest.CanReadPQSubchannelWithC2 = false; + mediaTest.CanReadRWSubchannelWithC2 = false; + mediaTest.CanReadCorrectedSubchannelWithC2 = false; + } + else + { + mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2662, 1, + MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.C2Pointers, + MmcSubchannel.Q16, _dev.Timeout, out _); + + if(mediaTest.CanReadPQSubchannelWithC2 == false) + mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, + 2664, 1, MmcSectorTypes.AllTypes, false, + false, true, MmcHeaderCodes.AllHeaders, + true, true, + MmcErrorField.C2PointersAndBlock, + MmcSubchannel.Q16, _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadPQSubchannelWithC2); + + mediaTest.PQSubchannelWithC2Data = buffer; + + mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2712, 1, + MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.C2Pointers, + MmcSubchannel.Raw, _dev.Timeout, out _); + + if(mediaTest.CanReadRWSubchannelWithC2 == false) + mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, + 2714, 1, MmcSectorTypes.AllTypes, false, + false, true, MmcHeaderCodes.AllHeaders, + true, true, + MmcErrorField.C2PointersAndBlock, + MmcSubchannel.Raw, _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadRWSubchannelWithC2); + + mediaTest.RWSubchannelWithC2Data = buffer; + + mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, + 2712, 1, MmcSectorTypes.AllTypes, + false, false, true, + MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.C2Pointers, + MmcSubchannel.Rw, _dev.Timeout, + out _); + + if(mediaTest.CanReadCorrectedSubchannelWithC2 == false) + mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, + 16, 2714, 1, + MmcSectorTypes.AllTypes, false, + false, true, + MmcHeaderCodes.AllHeaders, true, + true, + MmcErrorField.C2PointersAndBlock, + MmcSubchannel.Rw, _dev.Timeout, + out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadCorrectedSubchannelWithC2); + + mediaTest.CorrectedSubchannelWithC2Data = buffer; + } + }); + } + else + { + if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) + mediaTest.CanReadC2Pointers = false; + else + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to read C2 Pointers...").IsIndeterminate(); + + mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2342, 1, + MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.C2Pointers, MmcSubchannel.None, + _dev.Timeout, out _); + + if(mediaTest.CanReadC2Pointers == false) + mediaTest.CanReadC2Pointers = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2344, 1, + MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.C2PointersAndBlock, + MmcSubchannel.None, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadC2Pointers); + + mediaTest.C2PointersData = buffer; } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to read subchannels...").IsIndeterminate(); + mediaTest.CanReadPMA = !_dev.ReadPma(out buffer, out senseBuffer, _dev.Timeout, out _); + + mediaTest.CanReadPQSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2064, 1, + MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Q16, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadPQSubchannel); + + mediaTest.PQSubchannelData = buffer; + + mediaTest.CanReadRWSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2144, 1, + MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Raw, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadRWSubchannel); + + mediaTest.RWSubchannelData = buffer; + + mediaTest.CanReadCorrectedSubchannel = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2144, 1, + MmcSectorTypes.AllTypes, false, false, + false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.Rw, + _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadCorrectedSubchannel); + + mediaTest.CorrectedSubchannelData = buffer; + }); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to read subchannels with C2 Pointers...").IsIndeterminate(); + + if(_dev.Model.StartsWith("iHOS104", StringComparison.Ordinal)) + { + mediaTest.CanReadPQSubchannelWithC2 = false; + mediaTest.CanReadRWSubchannelWithC2 = false; + mediaTest.CanReadCorrectedSubchannelWithC2 = false; + } + else + { + mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2358, 1, + MmcSectorTypes.AllTypes, false, false, + false, MmcHeaderCodes.None, true, false, + MmcErrorField.C2Pointers, + MmcSubchannel.Q16, _dev.Timeout, out _); + + if(mediaTest.CanReadPQSubchannelWithC2 == false) + mediaTest.CanReadPQSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, + 2360, 1, MmcSectorTypes.AllTypes, false, + false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.C2PointersAndBlock, + MmcSubchannel.Q16, _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadPQSubchannelWithC2); + + mediaTest.PQSubchannelWithC2Data = buffer; + + mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, 2438, 1, + MmcSectorTypes.AllTypes, false, false, + false, MmcHeaderCodes.None, true, false, + MmcErrorField.C2Pointers, + MmcSubchannel.Raw, _dev.Timeout, out _); + + if(mediaTest.CanReadRWSubchannelWithC2 == false) + mediaTest.CanReadRWSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, + 2440, 1, MmcSectorTypes.AllTypes, false, + false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.C2PointersAndBlock, + MmcSubchannel.Raw, _dev.Timeout, out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadRWSubchannelWithC2); + + mediaTest.RWSubchannelWithC2Data = buffer; + + mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, 16, + 2438, 1, MmcSectorTypes.AllTypes, + false, false, false, + MmcHeaderCodes.None, true, false, + MmcErrorField.C2Pointers, + MmcSubchannel.Rw, _dev.Timeout, + out _); + + if(mediaTest.CanReadCorrectedSubchannelWithC2 == false) + mediaTest.CanReadCorrectedSubchannelWithC2 = !_dev.ReadCd(out buffer, out senseBuffer, + 16, 2440, 1, + MmcSectorTypes.AllTypes, false, + false, false, MmcHeaderCodes.None, + true, false, + MmcErrorField.C2PointersAndBlock, + MmcSubchannel.Rw, _dev.Timeout, + out _); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadCorrectedSubchannelWithC2); + + mediaTest.CorrectedSubchannelWithC2Data = buffer; + } + }); } if(tryPlextor) { Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Trying Plextor trick to raw read DVDs...").IsIndeterminate(); + ctx.AddTask("Trying Plextor READ CD-DA...").IsIndeterminate(); - mediaTest.SupportsPlextorReadRawDVD = - !_dev.PlextorReadRawDvd(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _); + mediaTest.SupportsPlextorReadCDDA = + !_dev.PlextorReadCdDa(out buffer, out senseBuffer, 16, 2352, 1, PlextorSubchannel.None, + _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsPlextorReadRawDVD); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsPlextorReadCDDA); - if(mediaTest.SupportsPlextorReadRawDVD == true) - mediaTest.SupportsPlextorReadRawDVD = !ArrayHelpers.ArrayIsNullOrEmpty(buffer); - - if(mediaTest.SupportsPlextorReadRawDVD == true) - mediaTest.PlextorReadRawDVDData = buffer; + mediaTest.PlextorReadCddaData = buffer; } - if(tryHldtst) + if(tryPioneer) { Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Trying HL-DT-ST (aka LG) trick to raw read DVDs...").IsIndeterminate(); + ctx.AddTask("Trying Pioneer READ CD-DA...").IsIndeterminate(); - mediaTest.SupportsHLDTSTReadRawDVD = - !_dev.HlDtStReadRawDvd(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _); + mediaTest.SupportsPioneerReadCDDA = + !_dev.PioneerReadCdDa(out buffer, out senseBuffer, 16, 2352, 1, PioneerSubchannel.None, + _dev.Timeout, out _); }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsHLDTSTReadRawDVD); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsPioneerReadCDDA); - if(mediaTest.SupportsHLDTSTReadRawDVD == true) - mediaTest.SupportsHLDTSTReadRawDVD = !ArrayHelpers.ArrayIsNullOrEmpty(buffer); - - if(mediaTest.SupportsHLDTSTReadRawDVD == true) - mediaTest.HLDTSTReadRawDVDData = buffer; - } - - if(tryMediaTekF106) - { - bool triedLba0 = false; - bool triedLeadOut = false; + mediaTest.PioneerReadCddaData = buffer; Spectre.ProgressSingleSpinner(ctx => { - ctx.AddTask("Trying MediaTek READ DRAM command...").IsIndeterminate(); + ctx.AddTask("Trying Pioneer READ CD-DA MSF...").IsIndeterminate(); + mediaTest.SupportsPioneerReadCDDAMSF = + !_dev.PioneerReadCdDaMsf(out buffer, out senseBuffer, 0x00000210, 0x00000211, 2352, + PioneerSubchannel.None, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsPioneerReadCDDAMSF); + + mediaTest.PioneerReadCddaMsfData = buffer; + } + + if(tryNec) + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying NEC READ CD-DA...").IsIndeterminate(); + + mediaTest.SupportsNECReadCDDA = + !_dev.NecReadCdDa(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsNECReadCDDA); + + mediaTest.NecReadCddaData = buffer; + } + } + + mediaTest.LongBlockSize = mediaTest.BlockSize; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ LONG (10)...").IsIndeterminate(); + sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 16, 0xFFFF, _dev.Timeout, out _); + }); + + if(sense && !_dev.Error) + { + DecodedSense? decSense = Sense.Decode(senseBuffer); + + if(decSense?.SenseKey == SenseKeys.IllegalRequest && + decSense.Value.ASC == 0x24 && + decSense.Value.ASCQ == 0x00) + { + mediaTest.SupportsReadLong = true; + + bool valid = decSense?.Fixed?.InformationValid == true; + bool ili = decSense?.Fixed?.ILI == true; + uint information = decSense?.Fixed?.Information ?? 0; + + if(decSense?.Descriptor.HasValue == true && + decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) + { + valid = true; + ili = true; + information = (uint)Sense.DecodeDescriptor00(desc00); + } + + if(valid && ili) + mediaTest.LongBlockSize = 0xFFFF - (information & 0xFFFF); + } + } + + if(mediaTest.SupportsReadLong == true && + mediaTest.LongBlockSize == mediaTest.BlockSize) + { + // DVDs + sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 16, 37856, _dev.Timeout, out _); + + if(!sense && + !_dev.Error) + { + mediaTest.ReadLong10Data = buffer; + mediaTest.SupportsReadLong = true; + mediaTest.LongBlockSize = 37856; + } + } + + if(tryPlextor) + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying Plextor trick to raw read DVDs...").IsIndeterminate(); + + mediaTest.SupportsPlextorReadRawDVD = + !_dev.PlextorReadRawDvd(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsPlextorReadRawDVD); + + if(mediaTest.SupportsPlextorReadRawDVD == true) + mediaTest.SupportsPlextorReadRawDVD = !ArrayHelpers.ArrayIsNullOrEmpty(buffer); + + if(mediaTest.SupportsPlextorReadRawDVD == true) + mediaTest.PlextorReadRawDVDData = buffer; + } + + if(tryHldtst) + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying HL-DT-ST (aka LG) trick to raw read DVDs...").IsIndeterminate(); + + mediaTest.SupportsHLDTSTReadRawDVD = + !_dev.HlDtStReadRawDvd(out buffer, out senseBuffer, 16, 1, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsHLDTSTReadRawDVD); + + if(mediaTest.SupportsHLDTSTReadRawDVD == true) + mediaTest.SupportsHLDTSTReadRawDVD = !ArrayHelpers.ArrayIsNullOrEmpty(buffer); + + if(mediaTest.SupportsHLDTSTReadRawDVD == true) + mediaTest.HLDTSTReadRawDVDData = buffer; + } + + if(tryMediaTekF106) + { + bool triedLba0 = false; + bool triedLeadOut = false; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying MediaTek READ DRAM command...").IsIndeterminate(); + + if(mediaType == "Audio CD" && + mediaTest.SupportsReadCd == true) + { + _dev.ReadCd(out _, out _, 0, 2352, 1, MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + triedLba0 = true; + } + else if((mediaType.StartsWith("CD", StringComparison.OrdinalIgnoreCase) || + mediaType == "Enhanced CD (aka E-CD, CD-Plus or CD+)") && + mediaTest.SupportsReadCdRaw == true) + { + _dev.ReadCd(out _, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + triedLba0 = true; + } + else if((mediaType.StartsWith("CD", StringComparison.OrdinalIgnoreCase) || + mediaType == "Enhanced CD (aka E-CD, CD-Plus or CD+)") && + mediaTest.SupportsReadCd == true) + { + _dev.ReadCd(out _, out _, 0, 2048, 1, MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + triedLba0 = true; + } + else if(mediaTest.SupportsRead6 == true) + { + _dev.Read6(out _, out _, 0, 2048, _dev.Timeout, out _); + triedLba0 = true; + } + else if(mediaTest.SupportsRead10 == true) + { + _dev.Read10(out _, out _, 0, false, true, false, false, 0, 2048, 0, 1, _dev.Timeout, out _); + triedLba0 = true; + } + else if(mediaTest.SupportsRead12 == true) + { + _dev.Read12(out _, out _, 0, false, true, false, false, 0, 2048, 0, 1, false, _dev.Timeout, + out _); + + triedLba0 = true; + } + else if(mediaTest.SupportsRead16 == true) + { + _dev.Read16(out _, out _, 0, false, true, false, 0, 2048, 0, 1, false, _dev.Timeout, out _); + triedLba0 = true; + } + + if(!triedLba0) + return; + + mediaTest.CanReadF1_06 = + !_dev.MediaTekReadDram(out buffer, out senseBuffer, 0, 0xB00, _dev.Timeout, out _); + + mediaTest.ReadF1_06Data = mediaTest.CanReadF1_06 == true ? buffer : senseBuffer; + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadF1_06); + }); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying MediaTek READ DRAM command for Lead-Out...").IsIndeterminate(); + + if(mediaTest.Blocks > 0) + { if(mediaType == "Audio CD" && mediaTest.SupportsReadCd == true) { - _dev.ReadCd(out _, out _, 0, 2352, 1, MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); + _dev.ReadCd(out _, out _, (uint)(mediaTest.Blocks + 1), 2352, 1, MmcSectorTypes.Cdda, false, + false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); - triedLba0 = true; + triedLeadOut = true; } else if((mediaType.StartsWith("CD", StringComparison.OrdinalIgnoreCase) || mediaType == "Enhanced CD (aka E-CD, CD-Plus or CD+)") && mediaTest.SupportsReadCdRaw == true) { - _dev.ReadCd(out _, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); + _dev.ReadCd(out _, out _, (uint)(mediaTest.Blocks + 1), 2352, 1, MmcSectorTypes.AllTypes, + false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); - triedLba0 = true; + triedLeadOut = true; } else if((mediaType.StartsWith("CD", StringComparison.OrdinalIgnoreCase) || mediaType == "Enhanced CD (aka E-CD, CD-Plus or CD+)") && mediaTest.SupportsReadCd == true) { - _dev.ReadCd(out _, out _, 0, 2048, 1, MmcSectorTypes.AllTypes, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); + _dev.ReadCd(out _, out _, (uint)(mediaTest.Blocks + 1), 2048, 1, MmcSectorTypes.AllTypes, + false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); - triedLba0 = true; + triedLeadOut = true; } else if(mediaTest.SupportsRead6 == true) { - _dev.Read6(out _, out _, 0, 2048, _dev.Timeout, out _); - triedLba0 = true; + _dev.Read6(out _, out _, (uint)(mediaTest.Blocks + 1), 2048, _dev.Timeout, out _); + triedLeadOut = true; } else if(mediaTest.SupportsRead10 == true) { - _dev.Read10(out _, out _, 0, false, true, false, false, 0, 2048, 0, 1, _dev.Timeout, out _); - triedLba0 = true; + _dev.Read10(out _, out _, 0, false, true, false, false, (uint)(mediaTest.Blocks + 1), 2048, + 0, 1, _dev.Timeout, out _); + + triedLeadOut = true; } else if(mediaTest.SupportsRead12 == true) { - _dev.Read12(out _, out _, 0, false, true, false, false, 0, 2048, 0, 1, false, _dev.Timeout, - out _); + _dev.Read12(out _, out _, 0, false, true, false, false, (uint)(mediaTest.Blocks + 1), 2048, + 0, 1, false, _dev.Timeout, out _); - triedLba0 = true; + triedLeadOut = true; } else if(mediaTest.SupportsRead16 == true) { - _dev.Read16(out _, out _, 0, false, true, false, 0, 2048, 0, 1, false, _dev.Timeout, out _); - triedLba0 = true; + _dev.Read16(out _, out _, 0, false, true, false, (ulong)(mediaTest.Blocks + 1), 2048, 0, 1, + false, _dev.Timeout, out _); + + triedLeadOut = true; } - if(!triedLba0) - return; - - mediaTest.CanReadF1_06 = - !_dev.MediaTekReadDram(out buffer, out senseBuffer, 0, 0xB00, _dev.Timeout, out _); - - mediaTest.ReadF1_06Data = mediaTest.CanReadF1_06 == true ? buffer : senseBuffer; - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadF1_06); - }); - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying MediaTek READ DRAM command for Lead-Out...").IsIndeterminate(); - - if(mediaTest.Blocks > 0) + if(triedLeadOut) { - if(mediaType == "Audio CD" && - mediaTest.SupportsReadCd == true) - { - _dev.ReadCd(out _, out _, (uint)(mediaTest.Blocks + 1), 2352, 1, MmcSectorTypes.Cdda, false, - false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); + mediaTest.CanReadF1_06LeadOut = + !_dev.MediaTekReadDram(out buffer, out senseBuffer, 0, 0xB00, _dev.Timeout, out _); - triedLeadOut = true; - } - else if((mediaType.StartsWith("CD", StringComparison.OrdinalIgnoreCase) || - mediaType == "Enhanced CD (aka E-CD, CD-Plus or CD+)") && - mediaTest.SupportsReadCdRaw == true) - { - _dev.ReadCd(out _, out _, (uint)(mediaTest.Blocks + 1), 2352, 1, MmcSectorTypes.AllTypes, - false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); + mediaTest.ReadF1_06LeadOutData = + mediaTest.CanReadF1_06LeadOut == true ? buffer : senseBuffer; - triedLeadOut = true; - } - else if((mediaType.StartsWith("CD", StringComparison.OrdinalIgnoreCase) || - mediaType == "Enhanced CD (aka E-CD, CD-Plus or CD+)") && - mediaTest.SupportsReadCd == true) + // This means it has returned the same as previous read, so not really lead-out. + if(mediaTest.CanReadF1_06 == true && + mediaTest.CanReadF1_06LeadOut == true && + mediaTest.ReadF1_06Data.SequenceEqual(mediaTest.ReadF1_06LeadOutData)) { - _dev.ReadCd(out _, out _, (uint)(mediaTest.Blocks + 1), 2048, 1, MmcSectorTypes.AllTypes, - false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); - - triedLeadOut = true; - } - else if(mediaTest.SupportsRead6 == true) - { - _dev.Read6(out _, out _, (uint)(mediaTest.Blocks + 1), 2048, _dev.Timeout, out _); - triedLeadOut = true; - } - else if(mediaTest.SupportsRead10 == true) - { - _dev.Read10(out _, out _, 0, false, true, false, false, (uint)(mediaTest.Blocks + 1), 2048, - 0, 1, _dev.Timeout, out _); - - triedLeadOut = true; - } - else if(mediaTest.SupportsRead12 == true) - { - _dev.Read12(out _, out _, 0, false, true, false, false, (uint)(mediaTest.Blocks + 1), 2048, - 0, 1, false, _dev.Timeout, out _); - - triedLeadOut = true; - } - else if(mediaTest.SupportsRead16 == true) - { - _dev.Read16(out _, out _, 0, false, true, false, (ulong)(mediaTest.Blocks + 1), 2048, 0, 1, - false, _dev.Timeout, out _); - - triedLeadOut = true; + mediaTest.CanReadF1_06LeadOut = false; + mediaTest.ReadF1_06LeadOutData = senseBuffer; } - if(triedLeadOut) - { - mediaTest.CanReadF1_06LeadOut = - !_dev.MediaTekReadDram(out buffer, out senseBuffer, 0, 0xB00, _dev.Timeout, out _); - - mediaTest.ReadF1_06LeadOutData = - mediaTest.CanReadF1_06LeadOut == true ? buffer : senseBuffer; - - // This means it has returned the same as previous read, so not really lead-out. - if(mediaTest.CanReadF1_06 == true && - mediaTest.CanReadF1_06LeadOut == true && - mediaTest.ReadF1_06Data.SequenceEqual(mediaTest.ReadF1_06LeadOutData)) - { - mediaTest.CanReadF1_06LeadOut = false; - mediaTest.ReadF1_06LeadOutData = senseBuffer; - } - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadF1_06LeadOut); - } + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadF1_06LeadOut); } - }); - } + } + }); + } - // This is for checking multi-session support, and inter-session lead-in/out reading, as Enhanced CD are - if(mediaType == "Enhanced CD (aka E-CD, CD-Plus or CD+)") + // This is for checking multi-session support, and inter-session lead-in/out reading, as Enhanced CD are + if(mediaType == "Enhanced CD (aka E-CD, CD-Plus or CD+)") + { + Spectre.ProgressSingleSpinner(ctx => { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying CD Full TOC...").IsIndeterminate(); - mediaTest.CanReadFullTOC = !_dev.ReadRawToc(out buffer, out senseBuffer, 1, _dev.Timeout, out _); - }); + ctx.AddTask("Querying CD Full TOC...").IsIndeterminate(); + mediaTest.CanReadFullTOC = !_dev.ReadRawToc(out buffer, out senseBuffer, 1, _dev.Timeout, out _); + }); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadFullTOC); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.CanReadFullTOC); - mediaTest.FullTocData = buffer; + mediaTest.FullTocData = buffer; + + if(mediaTest.CanReadFullTOC == true) + { + FullTOC.CDFullTOC? decodedTocNullable = FullTOC.Decode(buffer); + + mediaTest.CanReadFullTOC = decodedTocNullable.HasValue; if(mediaTest.CanReadFullTOC == true) { - FullTOC.CDFullTOC? decodedTocNullable = FullTOC.Decode(buffer); + FullTOC.CDFullTOC decodedToc = decodedTocNullable.Value; - mediaTest.CanReadFullTOC = decodedTocNullable.HasValue; - - if(mediaTest.CanReadFullTOC == true) + if(!decodedToc.TrackDescriptors.Any(t => t.SessionNumber > 1)) { - FullTOC.CDFullTOC decodedToc = decodedTocNullable.Value; + AaruConsole. + ErrorWriteLine("Could not find second session. Have you inserted the correct type of disc?"); - if(!decodedToc.TrackDescriptors.Any(t => t.SessionNumber > 1)) + return null; + } + + FullTOC.TrackDataDescriptor firstSessionLeadOutTrack = + decodedToc.TrackDescriptors.FirstOrDefault(t => t.SessionNumber == 1 && t.POINT == 0xA2); + + FullTOC.TrackDataDescriptor secondSessionFirstTrack = + decodedToc.TrackDescriptors.FirstOrDefault(t => t.SessionNumber > 1 && t.POINT <= 99); + + if(firstSessionLeadOutTrack.SessionNumber == 0 || + secondSessionFirstTrack.SessionNumber == 0) + { + AaruConsole. + ErrorWriteLine("Could not find second session. Have you inserted the correct type of disc?"); + + return null; + } + + AaruConsole.DebugWriteLine("SCSI Report", + "First session Lead-Out starts at {0:D2}:{1:D2}:{2:D2}", + firstSessionLeadOutTrack.PMIN, firstSessionLeadOutTrack.PSEC, + firstSessionLeadOutTrack.PFRAME); + + AaruConsole.DebugWriteLine("SCSI Report", "Second session starts at {0:D2}:{1:D2}:{2:D2}", + secondSessionFirstTrack.PMIN, secondSessionFirstTrack.PSEC, + secondSessionFirstTrack.PFRAME); + + // Skip Lead-Out pre-gap + uint firstSessionLeadOutLba = (uint)((firstSessionLeadOutTrack.PMIN * 60 * 75) + + (firstSessionLeadOutTrack.PSEC * 75) + + firstSessionLeadOutTrack.PFRAME + 150); + + // Skip second session track pre-gap + uint secondSessionLeadInLba = (uint)((secondSessionFirstTrack.PMIN * 60 * 75) + + (secondSessionFirstTrack.PSEC * 75) + + secondSessionFirstTrack.PFRAME - 300); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ CD in first session Lead-Out...").IsIndeterminate(); + + mediaTest.CanReadingIntersessionLeadOut = !_dev.ReadCd(out buffer, out senseBuffer, + firstSessionLeadOutLba, 2448, 1, + MmcSectorTypes.AllTypes, false, false, + false, MmcHeaderCodes.AllHeaders, true, + false, MmcErrorField.None, + MmcSubchannel.Raw, _dev.Timeout, out _); + + if(mediaTest.CanReadingIntersessionLeadOut == false) { - AaruConsole. - ErrorWriteLine("Could not find second session. Have you inserted the correct type of disc?"); - - return null; - } - - FullTOC.TrackDataDescriptor firstSessionLeadOutTrack = - decodedToc.TrackDescriptors.FirstOrDefault(t => t.SessionNumber == 1 && t.POINT == 0xA2); - - FullTOC.TrackDataDescriptor secondSessionFirstTrack = - decodedToc.TrackDescriptors.FirstOrDefault(t => t.SessionNumber > 1 && t.POINT <= 99); - - if(firstSessionLeadOutTrack.SessionNumber == 0 || - secondSessionFirstTrack.SessionNumber == 0) - { - AaruConsole. - ErrorWriteLine("Could not find second session. Have you inserted the correct type of disc?"); - - return null; - } - - AaruConsole.DebugWriteLine("SCSI Report", - "First session Lead-Out starts at {0:D2}:{1:D2}:{2:D2}", - firstSessionLeadOutTrack.PMIN, firstSessionLeadOutTrack.PSEC, - firstSessionLeadOutTrack.PFRAME); - - AaruConsole.DebugWriteLine("SCSI Report", "Second session starts at {0:D2}:{1:D2}:{2:D2}", - secondSessionFirstTrack.PMIN, secondSessionFirstTrack.PSEC, - secondSessionFirstTrack.PFRAME); - - // Skip Lead-Out pre-gap - uint firstSessionLeadOutLba = (uint)((firstSessionLeadOutTrack.PMIN * 60 * 75) + - (firstSessionLeadOutTrack.PSEC * 75) + - firstSessionLeadOutTrack.PFRAME + 150); - - // Skip second session track pre-gap - uint secondSessionLeadInLba = (uint)((secondSessionFirstTrack.PMIN * 60 * 75) + - (secondSessionFirstTrack.PSEC * 75) + - secondSessionFirstTrack.PFRAME - 300); - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ CD in first session Lead-Out...").IsIndeterminate(); - mediaTest.CanReadingIntersessionLeadOut = !_dev.ReadCd(out buffer, out senseBuffer, - firstSessionLeadOutLba, 2448, 1, - MmcSectorTypes.AllTypes, false, false, - false, MmcHeaderCodes.AllHeaders, true, + firstSessionLeadOutLba, 2368, 1, + MmcSectorTypes.AllTypes, false, + false, false, + MmcHeaderCodes.AllHeaders, true, false, MmcErrorField.None, - MmcSubchannel.Raw, _dev.Timeout, out _); + MmcSubchannel.Q16, _dev.Timeout, + out _); if(mediaTest.CanReadingIntersessionLeadOut == false) - { mediaTest.CanReadingIntersessionLeadOut = !_dev.ReadCd(out buffer, out senseBuffer, - firstSessionLeadOutLba, 2368, 1, + firstSessionLeadOutLba, 2352, 1, MmcSectorTypes.AllTypes, false, false, false, MmcHeaderCodes.AllHeaders, true, false, MmcErrorField.None, - MmcSubchannel.Q16, _dev.Timeout, + MmcSubchannel.None, _dev.Timeout, out _); + } + }); - if(mediaTest.CanReadingIntersessionLeadOut == false) - mediaTest.CanReadingIntersessionLeadOut = !_dev.ReadCd(out buffer, out senseBuffer, - firstSessionLeadOutLba, 2352, 1, - MmcSectorTypes.AllTypes, false, - false, false, - MmcHeaderCodes.AllHeaders, true, - false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, - out _); - } - }); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadingIntersessionLeadOut); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadingIntersessionLeadOut); + mediaTest.IntersessionLeadOutData = buffer; - mediaTest.IntersessionLeadOutData = buffer; + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ CD in second session Lead-In...").IsIndeterminate(); - Spectre.ProgressSingleSpinner(ctx => + mediaTest.CanReadingIntersessionLeadIn = !_dev.ReadCd(out buffer, out senseBuffer, + secondSessionLeadInLba, 2448, 1, + MmcSectorTypes.AllTypes, false, false, + false, MmcHeaderCodes.AllHeaders, true, + false, MmcErrorField.None, + MmcSubchannel.Raw, _dev.Timeout, out _); + + if(mediaTest.CanReadingIntersessionLeadIn == false) { - ctx.AddTask("Trying SCSI READ CD in second session Lead-In...").IsIndeterminate(); - mediaTest.CanReadingIntersessionLeadIn = !_dev.ReadCd(out buffer, out senseBuffer, - secondSessionLeadInLba, 2448, 1, + secondSessionLeadInLba, 2368, 1, MmcSectorTypes.AllTypes, false, false, - false, MmcHeaderCodes.AllHeaders, true, - false, MmcErrorField.None, - MmcSubchannel.Raw, _dev.Timeout, out _); + false, MmcHeaderCodes.AllHeaders, + true, false, MmcErrorField.None, + MmcSubchannel.Q16, _dev.Timeout, + out _); if(mediaTest.CanReadingIntersessionLeadIn == false) - { mediaTest.CanReadingIntersessionLeadIn = !_dev.ReadCd(out buffer, out senseBuffer, - secondSessionLeadInLba, 2368, 1, - MmcSectorTypes.AllTypes, false, false, - false, MmcHeaderCodes.AllHeaders, - true, false, MmcErrorField.None, - MmcSubchannel.Q16, _dev.Timeout, + secondSessionLeadInLba, 2352, 1, + MmcSectorTypes.AllTypes, false, + false, false, + MmcHeaderCodes.AllHeaders, true, + false, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); + } + }); - if(mediaTest.CanReadingIntersessionLeadIn == false) - mediaTest.CanReadingIntersessionLeadIn = !_dev.ReadCd(out buffer, out senseBuffer, - secondSessionLeadInLba, 2352, 1, - MmcSectorTypes.AllTypes, false, - false, false, - MmcHeaderCodes.AllHeaders, true, - false, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, - out _); - } - }); + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", + !mediaTest.CanReadingIntersessionLeadIn); - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", - !mediaTest.CanReadingIntersessionLeadIn); - - mediaTest.IntersessionLeadInData = buffer; - } + mediaTest.IntersessionLeadInData = buffer; } } - - mediaTest.Blocks = null; - - return mediaTest; } + + mediaTest.Blocks = null; + + return mediaTest; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/PCMCIA.cs b/Aaru.Core/Devices/Report/PCMCIA.cs index f71db5476..1ee78ef15 100644 --- a/Aaru.Core/Devices/Report/PCMCIA.cs +++ b/Aaru.Core/Devices/Report/PCMCIA.cs @@ -33,52 +33,51 @@ using Aaru.CommonTypes.Metadata; using Aaru.Decoders.PCMCIA; -namespace Aaru.Core.Devices.Report +namespace Aaru.Core.Devices.Report; + +/// Implements creating a report for a PCMCIA device +public sealed partial class DeviceReport { - /// Implements creating a report for a PCMCIA device - public sealed partial class DeviceReport + /// Fills a device report with parameters specific to a PCMCIA device + public Pcmcia PcmciaReport() { - /// Fills a device report with parameters specific to a PCMCIA device - public Pcmcia PcmciaReport() + var pcmciaReport = new Pcmcia { - var pcmciaReport = new Pcmcia - { - CIS = _dev.Cis - }; + CIS = _dev.Cis + }; - Tuple[] tuples = CIS.GetTuples(_dev.Cis); - - if(tuples == null) - return pcmciaReport; - - foreach(Tuple tuple in tuples) - switch(tuple.Code) - { - case TupleCodes.CISTPL_MANFID: - ManufacturerIdentificationTuple manfid = CIS.DecodeManufacturerIdentificationTuple(tuple); - - if(manfid != null) - { - pcmciaReport.ManufacturerCode = manfid.ManufacturerID; - pcmciaReport.CardCode = manfid.CardID; - } - - break; - case TupleCodes.CISTPL_VERS_1: - Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple); - - if(vers != null) - { - pcmciaReport.Manufacturer = vers.Manufacturer; - pcmciaReport.ProductName = vers.Product; - pcmciaReport.Compliance = $"{vers.MajorVersion}.{vers.MinorVersion}"; - pcmciaReport.AdditionalInformation = vers.AdditionalInformation; - } - - break; - } + Tuple[] tuples = CIS.GetTuples(_dev.Cis); + if(tuples == null) return pcmciaReport; - } + + foreach(Tuple tuple in tuples) + switch(tuple.Code) + { + case TupleCodes.CISTPL_MANFID: + ManufacturerIdentificationTuple manfid = CIS.DecodeManufacturerIdentificationTuple(tuple); + + if(manfid != null) + { + pcmciaReport.ManufacturerCode = manfid.ManufacturerID; + pcmciaReport.CardCode = manfid.CardID; + } + + break; + case TupleCodes.CISTPL_VERS_1: + Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple); + + if(vers != null) + { + pcmciaReport.Manufacturer = vers.Manufacturer; + pcmciaReport.ProductName = vers.Product; + pcmciaReport.Compliance = $"{vers.MajorVersion}.{vers.MinorVersion}"; + pcmciaReport.AdditionalInformation = vers.AdditionalInformation; + } + + break; + } + + return pcmciaReport; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/SSC.cs b/Aaru.Core/Devices/Report/SSC.cs index 5e4379a1f..d5e49806e 100644 --- a/Aaru.Core/Devices/Report/SSC.cs +++ b/Aaru.Core/Devices/Report/SSC.cs @@ -38,255 +38,254 @@ using Aaru.Decoders.SCSI.SSC; using Aaru.Devices; using Spectre.Console; -namespace Aaru.Core.Devices.Report +namespace Aaru.Core.Devices.Report; + +public sealed partial class DeviceReport { - public sealed partial class DeviceReport + /// Creates a report from a SCSI Sequential Commands device + /// SSC report + public Ssc ReportScsiSsc() { - /// Creates a report from a SCSI Sequential Commands device - /// SSC report - public Ssc ReportScsiSsc() + var report = new Ssc(); + bool sense = true; + byte[] buffer = Array.Empty(); + + Spectre.ProgressSingleSpinner(ctx => { - var report = new Ssc(); - bool sense = true; - byte[] buffer = Array.Empty(); + ctx.AddTask("Querying SCSI READ BLOCK LIMITS...").IsIndeterminate(); + sense = _dev.ReadBlockLimits(out buffer, out _, _dev.Timeout, out _); + }); - Spectre.ProgressSingleSpinner(ctx => + if(!sense) + { + BlockLimits.BlockLimitsData? decBl = BlockLimits.Decode(buffer); + + if(decBl?.granularity > 0) + report.BlockSizeGranularity = decBl.Value.granularity; + + if(decBl?.maxBlockLen > 0) + report.MaxBlockLength = decBl.Value.maxBlockLen; + + if(decBl?.minBlockLen > 0) + report.MinBlockLength = decBl.Value.minBlockLen; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI REPORT DENSITY SUPPORT...").IsIndeterminate(); + sense = _dev.ReportDensitySupport(out buffer, out _, false, false, _dev.Timeout, out _); + }); + + if(!sense) + { + DensitySupport.DensitySupportHeader? dsh = DensitySupport.DecodeDensity(buffer); + + if(dsh.HasValue) { - ctx.AddTask("Querying SCSI READ BLOCK LIMITS...").IsIndeterminate(); - sense = _dev.ReadBlockLimits(out buffer, out _, _dev.Timeout, out _); - }); + SupportedDensity[] array = new SupportedDensity[dsh.Value.descriptors.Length]; - if(!sense) - { - BlockLimits.BlockLimitsData? decBl = BlockLimits.Decode(buffer); - - if(decBl?.granularity > 0) - report.BlockSizeGranularity = decBl.Value.granularity; - - if(decBl?.maxBlockLen > 0) - report.MaxBlockLength = decBl.Value.maxBlockLen; - - if(decBl?.minBlockLen > 0) - report.MinBlockLength = decBl.Value.minBlockLen; - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI REPORT DENSITY SUPPORT...").IsIndeterminate(); - sense = _dev.ReportDensitySupport(out buffer, out _, false, false, _dev.Timeout, out _); - }); - - if(!sense) - { - DensitySupport.DensitySupportHeader? dsh = DensitySupport.DecodeDensity(buffer); - - if(dsh.HasValue) - { - SupportedDensity[] array = new SupportedDensity[dsh.Value.descriptors.Length]; - - for(int i = 0; i < dsh.Value.descriptors.Length; i++) - array[i] = new SupportedDensity - { - BitsPerMm = dsh.Value.descriptors[i].bpmm, - Capacity = dsh.Value.descriptors[i].capacity, - DefaultDensity = dsh.Value.descriptors[i].defaultDensity, - Description = dsh.Value.descriptors[i].description, - Duplicate = dsh.Value.descriptors[i].duplicate, - Name = dsh.Value.descriptors[i].name, - Organization = dsh.Value.descriptors[i].organization, - PrimaryCode = dsh.Value.descriptors[i].primaryCode, - SecondaryCode = dsh.Value.descriptors[i].secondaryCode, - Tracks = dsh.Value.descriptors[i].tracks, - Width = dsh.Value.descriptors[i].width, - Writable = dsh.Value.descriptors[i].writable - }; - - report.SupportedDensities = array.ToList(); - } - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI REPORT DENSITY SUPPORT for medium types...").IsIndeterminate(); - sense = _dev.ReportDensitySupport(out buffer, out _, true, false, _dev.Timeout, out _); - }); - - if(sense) - return report; - - DensitySupport.MediaTypeSupportHeader? mtsh = DensitySupport.DecodeMediumType(buffer); - - if(!mtsh.HasValue) - return report; - - SscSupportedMedia[] array2 = new SscSupportedMedia[mtsh.Value.descriptors.Length]; - - for(int i = 0; i < mtsh.Value.descriptors.Length; i++) - { - array2[i] = new SscSupportedMedia - { - Description = mtsh.Value.descriptors[i].description, - Length = mtsh.Value.descriptors[i].length, - MediumType = mtsh.Value.descriptors[i].mediumType, - Name = mtsh.Value.descriptors[i].name, - Organization = mtsh.Value.descriptors[i].organization, - Width = mtsh.Value.descriptors[i].width - }; - - if(mtsh.Value.descriptors[i].densityCodes == null) - continue; - - DensityCode[] array3 = new DensityCode[mtsh.Value.descriptors[i].densityCodes.Length]; - - for(int j = 0; j < mtsh.Value.descriptors[i].densityCodes.Length; j++) - array3[j] = new DensityCode + for(int i = 0; i < dsh.Value.descriptors.Length; i++) + array[i] = new SupportedDensity { - Code = mtsh.Value.descriptors[i].densityCodes[j] + BitsPerMm = dsh.Value.descriptors[i].bpmm, + Capacity = dsh.Value.descriptors[i].capacity, + DefaultDensity = dsh.Value.descriptors[i].defaultDensity, + Description = dsh.Value.descriptors[i].description, + Duplicate = dsh.Value.descriptors[i].duplicate, + Name = dsh.Value.descriptors[i].name, + Organization = dsh.Value.descriptors[i].organization, + PrimaryCode = dsh.Value.descriptors[i].primaryCode, + SecondaryCode = dsh.Value.descriptors[i].secondaryCode, + Tracks = dsh.Value.descriptors[i].tracks, + Width = dsh.Value.descriptors[i].width, + Writable = dsh.Value.descriptors[i].writable }; - array2[i].DensityCodes = array3.Distinct().ToList(); + report.SupportedDensities = array.ToList(); } - - report.SupportedMediaTypes = array2.ToList(); - - return report; } - /// Creates a report for media inserted into an SSC device - /// Media report - public TestedSequentialMedia ReportSscMedia() + Spectre.ProgressSingleSpinner(ctx => { - var seqTest = new TestedSequentialMedia(); - bool sense = true; - byte[] buffer = Array.Empty(); + ctx.AddTask("Querying SCSI REPORT DENSITY SUPPORT for medium types...").IsIndeterminate(); + sense = _dev.ReportDensitySupport(out buffer, out _, true, false, _dev.Timeout, out _); + }); - Modes.DecodedMode? decMode = null; + if(sense) + return report; - Spectre.ProgressSingleSpinner(ctx => + DensitySupport.MediaTypeSupportHeader? mtsh = DensitySupport.DecodeMediumType(buffer); + + if(!mtsh.HasValue) + return report; + + SscSupportedMedia[] array2 = new SscSupportedMedia[mtsh.Value.descriptors.Length]; + + for(int i = 0; i < mtsh.Value.descriptors.Length; i++) + { + array2[i] = new SscSupportedMedia { - ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate(); + Description = mtsh.Value.descriptors[i].description, + Length = mtsh.Value.descriptors[i].length, + MediumType = mtsh.Value.descriptors[i].mediumType, + Name = mtsh.Value.descriptors[i].name, + Organization = mtsh.Value.descriptors[i].organization, + Width = mtsh.Value.descriptors[i].width + }; - sense = _dev.ModeSense10(out buffer, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, - _dev.Timeout, out _); - }); + if(mtsh.Value.descriptors[i].densityCodes == null) + continue; - if(!sense && - !_dev.Error) - { - decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); - seqTest.ModeSense10Data = buffer; - } + DensityCode[] array3 = new DensityCode[mtsh.Value.descriptors[i].densityCodes.Length]; - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate(); - sense = _dev.ModeSense(out buffer, out _, _dev.Timeout, out _); - }); - - if(!sense && - !_dev.Error) - { - decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); - - seqTest.ModeSense6Data = buffer; - } - - if(decMode.HasValue) - { - seqTest.MediumType = (byte)decMode.Value.Header.MediumType; - - if(decMode.Value.Header.BlockDescriptors?.Length > 0) - seqTest.Density = (byte)decMode.Value.Header.BlockDescriptors?[0].Density; - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI REPORT DENSITY SUPPORT for current media...").IsIndeterminate(); - sense = _dev.ReportDensitySupport(out buffer, out _, false, true, _dev.Timeout, out _); - }); - - if(!sense) - { - DensitySupport.DensitySupportHeader? dsh = DensitySupport.DecodeDensity(buffer); - - if(dsh.HasValue) + for(int j = 0; j < mtsh.Value.descriptors[i].densityCodes.Length; j++) + array3[j] = new DensityCode { - SupportedDensity[] array = new SupportedDensity[dsh.Value.descriptors.Length]; + Code = mtsh.Value.descriptors[i].densityCodes[j] + }; - for(int i = 0; i < dsh.Value.descriptors.Length; i++) - array[i] = new SupportedDensity - { - BitsPerMm = dsh.Value.descriptors[i].bpmm, - Capacity = dsh.Value.descriptors[i].capacity, - DefaultDensity = dsh.Value.descriptors[i].defaultDensity, - Description = dsh.Value.descriptors[i].description, - Duplicate = dsh.Value.descriptors[i].duplicate, - Name = dsh.Value.descriptors[i].name, - Organization = dsh.Value.descriptors[i].organization, - PrimaryCode = dsh.Value.descriptors[i].primaryCode, - SecondaryCode = dsh.Value.descriptors[i].secondaryCode, - Tracks = dsh.Value.descriptors[i].tracks, - Width = dsh.Value.descriptors[i].width, - Writable = dsh.Value.descriptors[i].writable - }; - - seqTest.SupportedDensities = array.ToList(); - } - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI REPORT DENSITY SUPPORT for medium types for current media..."). - IsIndeterminate(); - - sense = _dev.ReportDensitySupport(out buffer, out _, true, true, _dev.Timeout, out _); - }); - - if(!sense) - { - DensitySupport.MediaTypeSupportHeader? mtsh = DensitySupport.DecodeMediumType(buffer); - - if(mtsh.HasValue) - { - SscSupportedMedia[] array = new SscSupportedMedia[mtsh.Value.descriptors.Length]; - - for(int i = 0; i < mtsh.Value.descriptors.Length; i++) - { - array[i] = new SscSupportedMedia - { - Description = mtsh.Value.descriptors[i].description, - Length = mtsh.Value.descriptors[i].length, - MediumType = mtsh.Value.descriptors[i].mediumType, - Name = mtsh.Value.descriptors[i].name, - Organization = mtsh.Value.descriptors[i].organization, - Width = mtsh.Value.descriptors[i].width - }; - - if(mtsh.Value.descriptors[i].densityCodes == null) - continue; - - DensityCode[] array2 = new DensityCode[mtsh.Value.descriptors[i].densityCodes.Length]; - - for(int j = 0; j < mtsh.Value.descriptors[i].densityCodes.Length; j++) - array2[j] = new DensityCode - { - Code = mtsh.Value.descriptors[i].densityCodes[j] - }; - - array[i].DensityCodes = array2.Distinct().ToList(); - } - - seqTest.SupportedMediaTypes = array.ToList(); - } - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ MEDIA SERIAL NUMBER...").IsIndeterminate(); - seqTest.CanReadMediaSerial = !_dev.ReadMediaSerialNumber(out buffer, out _, _dev.Timeout, out _); - }); - - return seqTest; + array2[i].DensityCodes = array3.Distinct().ToList(); } + + report.SupportedMediaTypes = array2.ToList(); + + return report; + } + + /// Creates a report for media inserted into an SSC device + /// Media report + public TestedSequentialMedia ReportSscMedia() + { + var seqTest = new TestedSequentialMedia(); + bool sense = true; + byte[] buffer = Array.Empty(); + + Modes.DecodedMode? decMode = null; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate(); + + sense = _dev.ModeSense10(out buffer, out _, false, true, ScsiModeSensePageControl.Current, 0x3F, 0x00, + _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); + seqTest.ModeSense10Data = buffer; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate(); + sense = _dev.ModeSense(out buffer, out _, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); + + seqTest.ModeSense6Data = buffer; + } + + if(decMode.HasValue) + { + seqTest.MediumType = (byte)decMode.Value.Header.MediumType; + + if(decMode.Value.Header.BlockDescriptors?.Length > 0) + seqTest.Density = (byte)decMode.Value.Header.BlockDescriptors?[0].Density; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI REPORT DENSITY SUPPORT for current media...").IsIndeterminate(); + sense = _dev.ReportDensitySupport(out buffer, out _, false, true, _dev.Timeout, out _); + }); + + if(!sense) + { + DensitySupport.DensitySupportHeader? dsh = DensitySupport.DecodeDensity(buffer); + + if(dsh.HasValue) + { + SupportedDensity[] array = new SupportedDensity[dsh.Value.descriptors.Length]; + + for(int i = 0; i < dsh.Value.descriptors.Length; i++) + array[i] = new SupportedDensity + { + BitsPerMm = dsh.Value.descriptors[i].bpmm, + Capacity = dsh.Value.descriptors[i].capacity, + DefaultDensity = dsh.Value.descriptors[i].defaultDensity, + Description = dsh.Value.descriptors[i].description, + Duplicate = dsh.Value.descriptors[i].duplicate, + Name = dsh.Value.descriptors[i].name, + Organization = dsh.Value.descriptors[i].organization, + PrimaryCode = dsh.Value.descriptors[i].primaryCode, + SecondaryCode = dsh.Value.descriptors[i].secondaryCode, + Tracks = dsh.Value.descriptors[i].tracks, + Width = dsh.Value.descriptors[i].width, + Writable = dsh.Value.descriptors[i].writable + }; + + seqTest.SupportedDensities = array.ToList(); + } + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI REPORT DENSITY SUPPORT for medium types for current media..."). + IsIndeterminate(); + + sense = _dev.ReportDensitySupport(out buffer, out _, true, true, _dev.Timeout, out _); + }); + + if(!sense) + { + DensitySupport.MediaTypeSupportHeader? mtsh = DensitySupport.DecodeMediumType(buffer); + + if(mtsh.HasValue) + { + SscSupportedMedia[] array = new SscSupportedMedia[mtsh.Value.descriptors.Length]; + + for(int i = 0; i < mtsh.Value.descriptors.Length; i++) + { + array[i] = new SscSupportedMedia + { + Description = mtsh.Value.descriptors[i].description, + Length = mtsh.Value.descriptors[i].length, + MediumType = mtsh.Value.descriptors[i].mediumType, + Name = mtsh.Value.descriptors[i].name, + Organization = mtsh.Value.descriptors[i].organization, + Width = mtsh.Value.descriptors[i].width + }; + + if(mtsh.Value.descriptors[i].densityCodes == null) + continue; + + DensityCode[] array2 = new DensityCode[mtsh.Value.descriptors[i].densityCodes.Length]; + + for(int j = 0; j < mtsh.Value.descriptors[i].densityCodes.Length; j++) + array2[j] = new DensityCode + { + Code = mtsh.Value.descriptors[i].densityCodes[j] + }; + + array[i].DensityCodes = array2.Distinct().ToList(); + } + + seqTest.SupportedMediaTypes = array.ToList(); + } + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ MEDIA SERIAL NUMBER...").IsIndeterminate(); + seqTest.CanReadMediaSerial = !_dev.ReadMediaSerialNumber(out buffer, out _, _dev.Timeout, out _); + }); + + return seqTest; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/Scsi.cs b/Aaru.Core/Devices/Report/Scsi.cs index 660bbac9a..ff89b362b 100644 --- a/Aaru.Core/Devices/Report/Scsi.cs +++ b/Aaru.Core/Devices/Report/Scsi.cs @@ -42,231 +42,224 @@ using Aaru.Helpers; using Spectre.Console; using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry; -namespace Aaru.Core.Devices.Report +namespace Aaru.Core.Devices.Report; + +public sealed partial class DeviceReport { - public sealed partial class DeviceReport + /// Creates a report for the SCSI INQUIRY response + /// SCSI report + public Scsi ReportScsiInquiry() { - /// Creates a report for the SCSI INQUIRY response - /// SCSI report - public Scsi ReportScsiInquiry() + bool sense = true; + byte[] buffer = Array.Empty(); + + Spectre.ProgressSingleSpinner(ctx => { - bool sense = true; - byte[] buffer = Array.Empty(); + ctx.AddTask("Querying SCSI INQUIRY...").IsIndeterminate(); + sense = _dev.ScsiInquiry(out buffer, out _); + }); - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI INQUIRY...").IsIndeterminate(); - sense = _dev.ScsiInquiry(out buffer, out _); - }); + var report = new Scsi(); - var report = new Scsi(); + if(sense) + return null; - if(sense) - return null; + Inquiry? decodedNullable = Inquiry.Decode(buffer); - Inquiry? decodedNullable = Inquiry.Decode(buffer); + if(!decodedNullable.HasValue) + return null; - if(!decodedNullable.HasValue) - return null; + report.InquiryData = ClearInquiry(buffer); - report.InquiryData = ClearInquiry(buffer); + return report; + } - return report; - } - - internal static byte[] ClearInquiry(byte[] inquiry) - { - Inquiry? decodedNullable = Inquiry.Decode(inquiry); - - if(!decodedNullable.HasValue) - return inquiry; - - Inquiry decoded = decodedNullable.Value; - - if(!decoded.SeagatePresent || - StringHandlers.CToString(decoded.VendorIdentification)?.Trim().ToLowerInvariant() != "seagate") - return inquiry; - - // Clear Seagate serial number - for(int i = 36; i <= 43; i++) - inquiry[i] = 0; + internal static byte[] ClearInquiry(byte[] inquiry) + { + Inquiry? decodedNullable = Inquiry.Decode(inquiry); + if(!decodedNullable.HasValue) return inquiry; - } - /// Returns a list of decoded SCSI EVPD pages - /// Decoded SCSI vendor identification - /// List of decoded SCSI EVPD pages - public List ReportEvpdPages(string vendor) + Inquiry decoded = decodedNullable.Value; + + if(!decoded.SeagatePresent || + StringHandlers.CToString(decoded.VendorIdentification)?.Trim().ToLowerInvariant() != "seagate") + return inquiry; + + // Clear Seagate serial number + for(int i = 36; i <= 43; i++) + inquiry[i] = 0; + + return inquiry; + } + + /// Returns a list of decoded SCSI EVPD pages + /// Decoded SCSI vendor identification + /// List of decoded SCSI EVPD pages + public List ReportEvpdPages(string vendor) + { + bool sense = false; + byte[] buffer = Array.Empty(); + + Spectre.ProgressSingleSpinner(ctx => { - bool sense = false; - byte[] buffer = Array.Empty(); + ctx.AddTask("Querying list of SCSI EVPDs...").IsIndeterminate(); + sense = _dev.ScsiInquiry(out buffer, out _, 0x00); + }); - Spectre.ProgressSingleSpinner(ctx => + if(sense) + return null; + + byte[] evpdPages = EVPD.DecodePage00(buffer); + + if(evpdPages == null || + evpdPages.Length <= 0) + return null; + + List evpds = new(); + + Spectre.ProgressSingleSpinner(ctx => + { + ProgressTask task = ctx. + AddTask("Querying SCSI EVPD pages...", + maxValue: evpdPages.Count(page => page != 0x80)).IsIndeterminate(); + + foreach(byte page in evpdPages.Where(page => page != 0x80)) { - ctx.AddTask("Querying list of SCSI EVPDs...").IsIndeterminate(); - sense = _dev.ScsiInquiry(out buffer, out _, 0x00); - }); + task.Description = $"Querying SCSI EVPD {page:X2}h..."; + task.Increment(1); + sense = _dev.ScsiInquiry(out buffer, out _, page); - if(sense) - return null; + if(sense) + continue; - byte[] evpdPages = EVPD.DecodePage00(buffer); + byte[] empty; - if(evpdPages == null || - evpdPages.Length <= 0) - return null; - - List evpds = new(); - - Spectre.ProgressSingleSpinner(ctx => - { - ProgressTask task = ctx. - AddTask("Querying SCSI EVPD pages...", - maxValue: evpdPages.Count(page => page != 0x80)).IsIndeterminate(); - - foreach(byte page in evpdPages.Where(page => page != 0x80)) + switch(page) { - task.Description = $"Querying SCSI EVPD {page:X2}h..."; - task.Increment(1); - sense = _dev.ScsiInquiry(out buffer, out _, page); + case 0x83: + buffer = ClearPage83(buffer); - if(sense) - continue; + break; + case 0x80: + byte[] identify = new byte[512]; + Array.Copy(buffer, 60, identify, 0, 512); + identify = ClearIdentify(identify); + Array.Copy(identify, 0, buffer, 60, 512); - byte[] empty; + break; + case 0xB1: + case 0xB3: + empty = new byte[buffer.Length - 4]; + Array.Copy(empty, 0, buffer, 4, buffer.Length - 4); - switch(page) - { - case 0x83: - buffer = ClearPage83(buffer); + break; + case 0xC1 when vendor == "ibm": + empty = new byte[12]; + Array.Copy(empty, 0, buffer, 4, 12); + Array.Copy(empty, 0, buffer, 16, 12); - break; - case 0x80: - byte[] identify = new byte[512]; - Array.Copy(buffer, 60, identify, 0, 512); - identify = ClearIdentify(identify); - Array.Copy(identify, 0, buffer, 60, 512); + break; + case 0xC2 when vendor == "certance": + case 0xC3 when vendor == "certance": + case 0xC4 when vendor == "certance": + case 0xC5 when vendor == "certance": + case 0xC6 when vendor == "certance": + Array.Copy(new byte[12], 0, buffer, 4, 12); - break; - case 0xB1: - case 0xB3: - empty = new byte[buffer.Length - 4]; - Array.Copy(empty, 0, buffer, 4, buffer.Length - 4); - - break; - case 0xC1 when vendor == "ibm": - empty = new byte[12]; - Array.Copy(empty, 0, buffer, 4, 12); - Array.Copy(empty, 0, buffer, 16, 12); - - break; - case 0xC2 when vendor == "certance": - case 0xC3 when vendor == "certance": - case 0xC4 when vendor == "certance": - case 0xC5 when vendor == "certance": - case 0xC6 when vendor == "certance": - Array.Copy(new byte[12], 0, buffer, 4, 12); - - break; - } - - var evpd = new ScsiPage - { - page = page, - value = buffer - }; - - evpds.Add(evpd); + break; } - }); - return evpds.Count > 0 ? evpds : null; - } + var evpd = new ScsiPage + { + page = page, + value = buffer + }; - byte[] ClearPage83(byte[] pageResponse) - { - if(pageResponse?[1] != 0x83) - return null; - - if(pageResponse[3] + 4 != pageResponse.Length) - return null; - - if(pageResponse.Length < 6) - return null; - - int position = 4; - - while(position < pageResponse.Length) - { - byte length = pageResponse[position + 3]; - - if(length + position + 4 >= pageResponse.Length) - length = (byte)(pageResponse.Length - position - 4); - - byte[] empty = new byte[length]; - Array.Copy(empty, 0, pageResponse, position + 4, length); - - position += 4 + length; + evpds.Add(evpd); } + }); - return pageResponse; + return evpds.Count > 0 ? evpds : null; + } + + byte[] ClearPage83(byte[] pageResponse) + { + if(pageResponse?[1] != 0x83) + return null; + + if(pageResponse[3] + 4 != pageResponse.Length) + return null; + + if(pageResponse.Length < 6) + return null; + + int position = 4; + + while(position < pageResponse.Length) + { + byte length = pageResponse[position + 3]; + + if(length + position + 4 >= pageResponse.Length) + length = (byte)(pageResponse.Length - position - 4); + + byte[] empty = new byte[length]; + Array.Copy(empty, 0, pageResponse, position + 4, length); + + position += 4 + length; } - /// Adds reports for the decoded SCSI MODE SENSE pages to a device report - /// Device report - /// Returns raw MODE SENSE page 2Ah, aka CD-ROM page - /// Returns decoded list of supported media types response - public void ReportScsiModes(ref DeviceReportV2 report, out byte[] cdromMode, out MediumTypes mediumType) + return pageResponse; + } + + /// Adds reports for the decoded SCSI MODE SENSE pages to a device report + /// Device report + /// Returns raw MODE SENSE page 2Ah, aka CD-ROM page + /// Returns decoded list of supported media types response + public void ReportScsiModes(ref DeviceReportV2 report, out byte[] cdromMode, out MediumTypes mediumType) + { + Modes.DecodedMode? decMode = null; + PeripheralDeviceTypes devType = _dev.ScsiType; + byte[] mode10Buffer; + byte[] mode6Buffer; + bool sense; + mediumType = 0; + + DeviceReportV2 v2 = report; + + Spectre.ProgressSingleSpinner(ctx => { - Modes.DecodedMode? decMode = null; - PeripheralDeviceTypes devType = _dev.ScsiType; - byte[] mode10Buffer; - byte[] mode6Buffer; - bool sense; - mediumType = 0; + ctx.AddTask("Querying all mode pages and subpages using SCSI MODE SENSE (10)...").IsIndeterminate(); - DeviceReportV2 v2 = report; - - Spectre.ProgressSingleSpinner(ctx => + foreach(ScsiModeSensePageControl pageControl in new[] + { + ScsiModeSensePageControl.Default, ScsiModeSensePageControl.Current, + ScsiModeSensePageControl.Changeable + }) { - ctx.AddTask("Querying all mode pages and subpages using SCSI MODE SENSE (10)...").IsIndeterminate(); + bool saveBuffer = false; - foreach(ScsiModeSensePageControl pageControl in new[] - { - ScsiModeSensePageControl.Default, ScsiModeSensePageControl.Current, - ScsiModeSensePageControl.Changeable - }) - { - bool saveBuffer = false; + sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, pageControl, 0x3F, 0xFF, + _dev.Timeout, out _); - sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, pageControl, 0x3F, 0xFF, + if(sense || _dev.Error) + { + sense = _dev.ModeSense10(out mode10Buffer, out _, false, false, pageControl, 0x3F, 0xFF, _dev.Timeout, out _); if(sense || _dev.Error) { - sense = _dev.ModeSense10(out mode10Buffer, out _, false, false, pageControl, 0x3F, 0xFF, + sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, pageControl, 0x3F, 0x00, _dev.Timeout, out _); if(sense || _dev.Error) { - sense = _dev.ModeSense10(out mode10Buffer, out _, false, true, pageControl, 0x3F, 0x00, + sense = _dev.ModeSense10(out mode10Buffer, out _, false, false, pageControl, 0x3F, 0x00, _dev.Timeout, out _); - if(sense || _dev.Error) - { - sense = _dev.ModeSense10(out mode10Buffer, out _, false, false, pageControl, 0x3F, 0x00, - _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - { - v2.SCSI.SupportsModeSense10 = true; - decMode ??= Modes.DecodeMode10(mode10Buffer, devType); - saveBuffer = true; - } - } - else + if(!sense && + !_dev.Error) { v2.SCSI.SupportsModeSense10 = true; decMode ??= Modes.DecodeMode10(mode10Buffer, devType); @@ -275,10 +268,9 @@ namespace Aaru.Core.Devices.Report } else { - v2.SCSI.SupportsModeSense10 = true; - v2.SCSI.SupportsModeSubpages = true; - decMode ??= Modes.DecodeMode10(mode10Buffer, devType); - saveBuffer = true; + v2.SCSI.SupportsModeSense10 = true; + decMode ??= Modes.DecodeMode10(mode10Buffer, devType); + saveBuffer = true; } } else @@ -288,75 +280,76 @@ namespace Aaru.Core.Devices.Report decMode ??= Modes.DecodeMode10(mode10Buffer, devType); saveBuffer = true; } - - if(!saveBuffer) - continue; - - switch(pageControl) - { - case ScsiModeSensePageControl.Default: - v2.SCSI.ModeSense10Data = mode10Buffer; - - break; - case ScsiModeSensePageControl.Changeable: - v2.SCSI.ModeSense10ChangeableData = mode10Buffer; - - break; - case ScsiModeSensePageControl.Current: - v2.SCSI.ModeSense10CurrentData = mode10Buffer; - - break; - } } - }); + else + { + v2.SCSI.SupportsModeSense10 = true; + v2.SCSI.SupportsModeSubpages = true; + decMode ??= Modes.DecodeMode10(mode10Buffer, devType); + saveBuffer = true; + } - Spectre.ProgressSingleSpinner(ctx => + if(!saveBuffer) + continue; + + switch(pageControl) + { + case ScsiModeSensePageControl.Default: + v2.SCSI.ModeSense10Data = mode10Buffer; + + break; + case ScsiModeSensePageControl.Changeable: + v2.SCSI.ModeSense10ChangeableData = mode10Buffer; + + break; + case ScsiModeSensePageControl.Current: + v2.SCSI.ModeSense10CurrentData = mode10Buffer; + + break; + } + } + }); + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying all mode pages and subpages using SCSI MODE SENSE (6)...").IsIndeterminate(); + + foreach(ScsiModeSensePageControl pageControl in new[] + { + ScsiModeSensePageControl.Default, ScsiModeSensePageControl.Current, + ScsiModeSensePageControl.Changeable + }) { - ctx.AddTask("Querying all mode pages and subpages using SCSI MODE SENSE (6)...").IsIndeterminate(); + bool saveBuffer = false; + sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x3F, 0xFF, _dev.Timeout, out _); - foreach(ScsiModeSensePageControl pageControl in new[] + if(sense || _dev.Error) { - ScsiModeSensePageControl.Default, ScsiModeSensePageControl.Current, - ScsiModeSensePageControl.Changeable - }) - { - bool saveBuffer = false; - sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x3F, 0xFF, _dev.Timeout, out _); + sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x3F, 0xFF, _dev.Timeout, + out _); if(sense || _dev.Error) { - sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x3F, 0xFF, _dev.Timeout, + sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x3F, 0x00, _dev.Timeout, out _); if(sense || _dev.Error) { - sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x3F, 0x00, _dev.Timeout, - out _); + sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x3F, 0x00, + _dev.Timeout, out _); if(sense || _dev.Error) { - sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x3F, 0x00, + sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x00, 0x00, _dev.Timeout, out _); if(sense || _dev.Error) { - sense = _dev.ModeSense6(out mode6Buffer, out _, true, pageControl, 0x00, 0x00, + sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x00, 0x00, _dev.Timeout, out _); - if(sense || _dev.Error) - { - sense = _dev.ModeSense6(out mode6Buffer, out _, false, pageControl, 0x00, 0x00, - _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - { - v2.SCSI.SupportsModeSense6 = true; - decMode ??= Modes.DecodeMode6(mode6Buffer, devType); - saveBuffer = true; - } - } - else + if(!sense && + !_dev.Error) { v2.SCSI.SupportsModeSense6 = true; decMode ??= Modes.DecodeMode6(mode6Buffer, devType); @@ -379,221 +372,260 @@ namespace Aaru.Core.Devices.Report } else { - v2.SCSI.SupportsModeSense10 = true; - v2.SCSI.SupportsModeSubpages = true; - decMode ??= Modes.DecodeMode6(mode6Buffer, devType); - saveBuffer = true; + v2.SCSI.SupportsModeSense6 = true; + decMode ??= Modes.DecodeMode6(mode6Buffer, devType); + saveBuffer = true; } } else { - v2.SCSI.SupportsModeSense6 = true; + v2.SCSI.SupportsModeSense10 = true; v2.SCSI.SupportsModeSubpages = true; decMode ??= Modes.DecodeMode6(mode6Buffer, devType); saveBuffer = true; } - - if(!saveBuffer) - continue; - - switch(pageControl) - { - case ScsiModeSensePageControl.Default: - v2.SCSI.ModeSense6Data = mode6Buffer; - - break; - case ScsiModeSensePageControl.Changeable: - v2.SCSI.ModeSense6ChangeableData = mode6Buffer; - - break; - case ScsiModeSensePageControl.Current: - v2.SCSI.ModeSense6CurrentData = mode6Buffer; - - break; - } } - }); + else + { + v2.SCSI.SupportsModeSense6 = true; + v2.SCSI.SupportsModeSubpages = true; + decMode ??= Modes.DecodeMode6(mode6Buffer, devType); + saveBuffer = true; + } - report = v2; + if(!saveBuffer) + continue; - cdromMode = null; + switch(pageControl) + { + case ScsiModeSensePageControl.Default: + v2.SCSI.ModeSense6Data = mode6Buffer; - if(!decMode.HasValue) - return; + break; + case ScsiModeSensePageControl.Changeable: + v2.SCSI.ModeSense6ChangeableData = mode6Buffer; - mediumType = decMode.Value.Header.MediumType; + break; + case ScsiModeSensePageControl.Current: + v2.SCSI.ModeSense6CurrentData = mode6Buffer; - report.SCSI.ModeSense = new ScsiMode + break; + } + } + }); + + report = v2; + + cdromMode = null; + + if(!decMode.HasValue) + return; + + mediumType = decMode.Value.Header.MediumType; + + report.SCSI.ModeSense = new ScsiMode + { + BlankCheckEnabled = decMode.Value.Header.EBC, + DPOandFUA = decMode.Value.Header.DPOFUA, + WriteProtected = decMode.Value.Header.WriteProtected + }; + + if(decMode.Value.Header.BufferedMode > 0) + report.SCSI.ModeSense.BufferedMode = decMode.Value.Header.BufferedMode; + + if(decMode.Value.Header.Speed > 0) + report.SCSI.ModeSense.Speed = decMode.Value.Header.Speed; + + if(decMode.Value.Pages == null) + return; + + List modePages = new(); + + foreach(Modes.ModePage page in decMode.Value.Pages) + { + var modePage = new ScsiPage { - BlankCheckEnabled = decMode.Value.Header.EBC, - DPOandFUA = decMode.Value.Header.DPOFUA, - WriteProtected = decMode.Value.Header.WriteProtected + page = page.Page, + subpage = page.Subpage, + value = page.PageResponse }; - if(decMode.Value.Header.BufferedMode > 0) - report.SCSI.ModeSense.BufferedMode = decMode.Value.Header.BufferedMode; + modePages.Add(modePage); - if(decMode.Value.Header.Speed > 0) - report.SCSI.ModeSense.Speed = decMode.Value.Header.Speed; - - if(decMode.Value.Pages == null) - return; - - List modePages = new(); - - foreach(Modes.ModePage page in decMode.Value.Pages) - { - var modePage = new ScsiPage - { - page = page.Page, - subpage = page.Subpage, - value = page.PageResponse - }; - - modePages.Add(modePage); - - if(modePage.page == 0x2A && - modePage.subpage == 0x00) - cdromMode = page.PageResponse; - } - - if(modePages.Count > 0) - report.SCSI.ModeSense.ModePages = modePages; + if(modePage.page == 0x2A && + modePage.subpage == 0x00) + cdromMode = page.PageResponse; } - /// Creates a report for media inserted into a SCSI device - /// Media report - public TestedMedia ReportScsiMedia() + if(modePages.Count > 0) + report.SCSI.ModeSense.ModePages = modePages; + } + + /// Creates a report for media inserted into a SCSI device + /// Media report + public TestedMedia ReportScsiMedia() + { + var mediaTest = new TestedMedia(); + bool sense = true; + byte[] buffer = Array.Empty(); + byte[] senseBuffer = Array.Empty(); + + Spectre.ProgressSingleSpinner(ctx => { - var mediaTest = new TestedMedia(); - bool sense = true; - byte[] buffer = Array.Empty(); - byte[] senseBuffer = Array.Empty(); + ctx.AddTask("Querying SCSI READ CAPACITY...").IsIndeterminate(); + sense = _dev.ReadCapacity(out buffer, out senseBuffer, _dev.Timeout, out _); + }); - Spectre.ProgressSingleSpinner(ctx => + if(!sense && + !_dev.Error) + { + mediaTest.SupportsReadCapacity = true; + + mediaTest.Blocks = ((ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) & + 0xFFFFFFFF) + 1; + + mediaTest.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI READ CAPACITY (16)...").IsIndeterminate(); + sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + mediaTest.SupportsReadCapacity16 = true; + byte[] temp = new byte[8]; + Array.Copy(buffer, 0, temp, 0, 8); + Array.Reverse(temp); + mediaTest.Blocks = BitConverter.ToUInt64(temp, 0) + 1; + mediaTest.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]); + } + + Modes.DecodedMode? decMode = null; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate(); + + sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, + 0x3F, 0x00, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); + mediaTest.ModeSense10Data = buffer; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate(); + sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); + + mediaTest.ModeSense6Data = buffer; + } + + if(decMode.HasValue) + { + mediaTest.MediumType = (byte)decMode.Value.Header.MediumType; + + if(decMode.Value.Header.BlockDescriptors?.Length > 0) + mediaTest.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate(); + + mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, mediaTest.BlockSize ?? 512, + _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); + mediaTest.Read6Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate(); + + mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, 0, + mediaTest.BlockSize ?? 512, 0, 1, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); + mediaTest.Read10Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate(); + + mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, 0, + mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); + mediaTest.Read12Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate(); + + mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0, + mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); + mediaTest.Read16Data = buffer; + + mediaTest.LongBlockSize = mediaTest.BlockSize; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ LONG (10)...").IsIndeterminate(); + sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _); + }); + + if(sense && !_dev.Error) + { + DecodedSense? decSense = Sense.Decode(senseBuffer); + + if(decSense?.SenseKey == SenseKeys.IllegalRequest && + decSense.Value.ASC == 0x24 && + decSense.Value.ASCQ == 0x00) { - ctx.AddTask("Querying SCSI READ CAPACITY...").IsIndeterminate(); - sense = _dev.ReadCapacity(out buffer, out senseBuffer, _dev.Timeout, out _); - }); + mediaTest.SupportsReadLong = true; - if(!sense && - !_dev.Error) - { - mediaTest.SupportsReadCapacity = true; + bool valid = decSense.Value.Fixed?.InformationValid == true; + bool ili = decSense.Value.Fixed?.ILI == true; + uint information = decSense.Value.Fixed?.Information ?? 0; - mediaTest.Blocks = ((ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) & - 0xFFFFFFFF) + 1; + if(decSense.Value.Descriptor.HasValue && + decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) + { + valid = true; + ili = true; + information = (uint)Sense.DecodeDescriptor00(desc00); + } - mediaTest.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); + if(valid && ili) + mediaTest.LongBlockSize = 0xFFFF - (information & 0xFFFF); } + } - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI READ CAPACITY (16)...").IsIndeterminate(); - sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _); - }); - - if(!sense && - !_dev.Error) - { - mediaTest.SupportsReadCapacity16 = true; - byte[] temp = new byte[8]; - Array.Copy(buffer, 0, temp, 0, 8); - Array.Reverse(temp); - mediaTest.Blocks = BitConverter.ToUInt64(temp, 0) + 1; - mediaTest.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]); - } - - Modes.DecodedMode? decMode = null; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate(); - - sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, - 0x3F, 0x00, _dev.Timeout, out _); - }); - - if(!sense && - !_dev.Error) - { - decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); - mediaTest.ModeSense10Data = buffer; - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate(); - sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _); - }); - - if(!sense && - !_dev.Error) - { - decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); - - mediaTest.ModeSense6Data = buffer; - } - - if(decMode.HasValue) - { - mediaTest.MediumType = (byte)decMode.Value.Header.MediumType; - - if(decMode.Value.Header.BlockDescriptors?.Length > 0) - mediaTest.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate(); - - mediaTest.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, mediaTest.BlockSize ?? 512, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead6); - mediaTest.Read6Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate(); - - mediaTest.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, 0, - mediaTest.BlockSize ?? 512, 0, 1, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead10); - mediaTest.Read10Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate(); - - mediaTest.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, 0, - mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead12); - mediaTest.Read12Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate(); - - mediaTest.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0, - mediaTest.BlockSize ?? 512, 0, 1, false, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !mediaTest.SupportsRead16); - mediaTest.Read16Data = buffer; - - mediaTest.LongBlockSize = mediaTest.BlockSize; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ LONG (10)...").IsIndeterminate(); - sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _); - }); + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ LONG (16)...").IsIndeterminate(); + sense = _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 0xFFFF, _dev.Timeout, out _); if(sense && !_dev.Error) { @@ -603,7 +635,7 @@ namespace Aaru.Core.Devices.Report decSense.Value.ASC == 0x24 && decSense.Value.ASCQ == 0x00) { - mediaTest.SupportsReadLong = true; + mediaTest.SupportsReadLong16 = true; bool valid = decSense.Value.Fixed?.InformationValid == true; bool ili = decSense.Value.Fixed?.ILI == true; @@ -622,45 +654,335 @@ namespace Aaru.Core.Devices.Report } } - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ LONG (16)...").IsIndeterminate(); - sense = _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 0xFFFF, _dev.Timeout, out _); - - if(sense && !_dev.Error) + if((mediaTest.SupportsReadLong == true || mediaTest.SupportsReadLong16 == true) && + mediaTest.LongBlockSize == mediaTest.BlockSize) + switch(mediaTest.BlockSize) { - DecodedSense? decSense = Sense.Decode(senseBuffer); - - if(decSense?.SenseKey == SenseKeys.IllegalRequest && - decSense.Value.ASC == 0x24 && - decSense.Value.ASCQ == 0x00) + case 512: { - mediaTest.SupportsReadLong16 = true; + foreach(ushort testSize in new ushort[] + { + // Long sector sizes for floppies + 514, - bool valid = decSense.Value.Fixed?.InformationValid == true; - bool ili = decSense.Value.Fixed?.ILI == true; - uint information = decSense.Value.Fixed?.Information ?? 0; + // Long sector sizes for SuperDisk + 536, 558, - if(decSense.Value.Descriptor.HasValue && - decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) + // Long sector sizes for 512-byte magneto-opticals + 600, 610, 630 + }) { - valid = true; - ili = true; - information = (uint)Sense.DecodeDescriptor00(desc00); + sense = mediaTest.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, + _dev.Timeout, out _) + : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, + _dev.Timeout, out _); + + if(sense || _dev.Error) + continue; + + mediaTest.LongBlockSize = testSize; + + break; } - if(valid && ili) - mediaTest.LongBlockSize = 0xFFFF - (information & 0xFFFF); + break; + } + case 1024: + { + foreach(ushort testSize in new ushort[] + { + // Long sector sizes for floppies + 1026, + + // Long sector sizes for 1024-byte magneto-opticals + 1200 + }) + { + sense = mediaTest.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, + _dev.Timeout, out _) + : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, + _dev.Timeout, out _); + + if(sense || _dev.Error) + continue; + + mediaTest.LongBlockSize = testSize; + + break; + } + + break; + } + case 2048: + { + sense = mediaTest.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 2380, _dev.Timeout, + out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, + false, 0, 2380, _dev.Timeout, out _); + + if(!sense && + !_dev.Error) + mediaTest.LongBlockSize = 2380; + + break; + } + case 4096: + { + sense = mediaTest.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 4760, _dev.Timeout, + out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, + false, 0, 4760, _dev.Timeout, out _); + + if(!sense && + !_dev.Error) + mediaTest.LongBlockSize = 4760; + + break; + } + case 8192: + { + sense = mediaTest.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 9424, _dev.Timeout, + out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, + false, 0, 9424, _dev.Timeout, out _); + + if(!sense && + !_dev.Error) + mediaTest.LongBlockSize = 9424; + + break; } } + }); - if((mediaTest.SupportsReadLong == true || mediaTest.SupportsReadLong16 == true) && - mediaTest.LongBlockSize == mediaTest.BlockSize) - switch(mediaTest.BlockSize) - { - case 512: - { - foreach(ushort testSize in new ushort[] + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ MEDIA SERIAL NUMBER...").IsIndeterminate(); + + mediaTest.CanReadMediaSerial = + !_dev.ReadMediaSerialNumber(out buffer, out senseBuffer, _dev.Timeout, out _); + }); + + return mediaTest; + } + + /// Creates a media report for a non-removable SCSI device + /// Media report + public TestedMedia ReportScsi() + { + bool sense = true; + byte[] buffer = Array.Empty(); + byte[] senseBuffer = Array.Empty(); + + var capabilities = new TestedMedia + { + MediaIsRecognized = true + }; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI READ CAPACITY...").IsIndeterminate(); + sense = _dev.ReadCapacity(out buffer, out senseBuffer, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + capabilities.SupportsReadCapacity = true; + + capabilities.Blocks = ((ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) & + 0xFFFFFFFF) + 1; + + capabilities.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI READ CAPACITY (16)...").IsIndeterminate(); + sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + capabilities.SupportsReadCapacity16 = true; + byte[] temp = new byte[8]; + Array.Copy(buffer, 0, temp, 0, 8); + Array.Reverse(temp); + capabilities.Blocks = BitConverter.ToUInt64(temp, 0) + 1; + capabilities.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]); + } + + Modes.DecodedMode? decMode = null; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate(); + + sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, + 0x3F, 0x00, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); + capabilities.ModeSense10Data = buffer; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate(); + sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _); + }); + + if(!sense && + !_dev.Error) + { + decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); + + capabilities.ModeSense6Data = buffer; + } + + if(decMode.HasValue) + { + capabilities.MediumType = (byte)decMode.Value.Header.MediumType; + + if(decMode.Value.Header.BlockDescriptors?.Length > 0) + capabilities.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate(); + + capabilities.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, capabilities.BlockSize ?? 512, + _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead6); + capabilities.Read6Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate(); + + capabilities.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, + 0, capabilities.BlockSize ?? 512, 0, 1, _dev.Timeout, out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead10); + capabilities.Read10Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate(); + + capabilities.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, + 0, capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, + out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead12); + capabilities.Read12Data = buffer; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate(); + + capabilities.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0, + capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, + out _); + }); + + AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead16); + capabilities.Read16Data = buffer; + + capabilities.LongBlockSize = capabilities.BlockSize; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ LONG (10)...").IsIndeterminate(); + sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _); + }); + + if(sense && !_dev.Error) + { + DecodedSense? decSense = Sense.Decode(senseBuffer); + + if(decSense?.SenseKey == SenseKeys.IllegalRequest && + decSense.Value.ASC == 0x24 && + decSense.Value.ASCQ == 0x00) + { + capabilities.SupportsReadLong = true; + + bool valid = decSense.Value.Fixed?.InformationValid == true; + bool ili = decSense.Value.Fixed?.ILI == true; + uint information = decSense.Value.Fixed?.Information ?? 0; + + if(decSense.Value.Descriptor.HasValue && + decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) + { + valid = true; + ili = true; + information = (uint)Sense.DecodeDescriptor00(desc00); + } + + if(valid && ili) + capabilities.LongBlockSize = 0xFFFF - (information & 0xFFFF); + } + } + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying SCSI READ LONG (16)...").IsIndeterminate(); + sense = _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 0xFFFF, _dev.Timeout, out _); + }); + + if(sense && !_dev.Error) + { + capabilities.SupportsReadLong16 = true; + DecodedSense? decSense = Sense.Decode(senseBuffer); + + if(decSense?.SenseKey == SenseKeys.IllegalRequest && + decSense.Value.ASC == 0x24 && + decSense.Value.ASCQ == 0x00) + { + capabilities.SupportsReadLong16 = true; + + bool valid = decSense.Value.Fixed?.InformationValid == true; + bool ili = decSense.Value.Fixed?.ILI == true; + uint information = decSense.Value.Fixed?.Information ?? 0; + + if(decSense.Value.Descriptor.HasValue && + decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) + { + valid = true; + ili = true; + information = (uint)Sense.DecodeDescriptor00(desc00); + } + + if(valid && ili) + capabilities.LongBlockSize = 0xFFFF - (information & 0xFFFF); + } + } + + if((capabilities.SupportsReadLong != true && capabilities.SupportsReadLong16 != true) || + capabilities.LongBlockSize != capabilities.BlockSize) + return capabilities; + + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask(capabilities.SupportsReadLong16 == true ? "Trying SCSI READ LONG (16)..." + : "Trying SCSI READ LONG (10)...").IsIndeterminate(); + + switch(capabilities.BlockSize) + { + case 512: + { + foreach(ushort testSize in new ushort[] { // Long sector sizes for floppies 514, @@ -671,26 +993,26 @@ namespace Aaru.Core.Devices.Report // Long sector sizes for 512-byte magneto-opticals 600, 610, 630 }) - { - sense = mediaTest.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, - _dev.Timeout, out _) - : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, - _dev.Timeout, out _); + { + sense = capabilities.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, _dev.Timeout, + out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, + false, 0, testSize, _dev.Timeout, out _); - if(sense || _dev.Error) - continue; + if(sense || _dev.Error) + continue; - mediaTest.LongBlockSize = testSize; + capabilities.SupportsReadLong = true; + capabilities.LongBlockSize = testSize; - break; - } + break; + } - break; - } - case 1024: - { - foreach(ushort testSize in new ushort[] + break; + } + case 1024: + { + foreach(ushort testSize in new ushort[] { // Long sector sizes for floppies 1026, @@ -698,394 +1020,71 @@ namespace Aaru.Core.Devices.Report // Long sector sizes for 1024-byte magneto-opticals 1200 }) - { - sense = mediaTest.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, - _dev.Timeout, out _) - : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, testSize, - _dev.Timeout, out _); + { + sense = capabilities.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, _dev.Timeout, + out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, + false, 0, testSize, _dev.Timeout, out _); - if(sense || _dev.Error) - continue; + if(sense || _dev.Error) + continue; - mediaTest.LongBlockSize = testSize; + capabilities.SupportsReadLong = true; + capabilities.LongBlockSize = testSize; - break; - } - - break; - } - case 2048: - { - sense = mediaTest.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 2380, _dev.Timeout, - out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, - false, 0, 2380, _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - mediaTest.LongBlockSize = 2380; - - break; - } - case 4096: - { - sense = mediaTest.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 4760, _dev.Timeout, - out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, - false, 0, 4760, _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - mediaTest.LongBlockSize = 4760; - - break; - } - case 8192: - { - sense = mediaTest.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 9424, _dev.Timeout, - out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, - false, 0, 9424, _dev.Timeout, out _); - - if(!sense && - !_dev.Error) - mediaTest.LongBlockSize = 9424; - - break; - } + break; } - }); - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ MEDIA SERIAL NUMBER...").IsIndeterminate(); - - mediaTest.CanReadMediaSerial = - !_dev.ReadMediaSerialNumber(out buffer, out senseBuffer, _dev.Timeout, out _); - }); - - return mediaTest; - } - - /// Creates a media report for a non-removable SCSI device - /// Media report - public TestedMedia ReportScsi() - { - bool sense = true; - byte[] buffer = Array.Empty(); - byte[] senseBuffer = Array.Empty(); - - var capabilities = new TestedMedia - { - MediaIsRecognized = true - }; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI READ CAPACITY...").IsIndeterminate(); - sense = _dev.ReadCapacity(out buffer, out senseBuffer, _dev.Timeout, out _); - }); - - if(!sense && - !_dev.Error) - { - capabilities.SupportsReadCapacity = true; - - capabilities.Blocks = ((ulong)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3]) & - 0xFFFFFFFF) + 1; - - capabilities.BlockSize = (uint)((buffer[4] << 24) + (buffer[5] << 16) + (buffer[6] << 8) + buffer[7]); - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI READ CAPACITY (16)...").IsIndeterminate(); - sense = _dev.ReadCapacity16(out buffer, out buffer, _dev.Timeout, out _); - }); - - if(!sense && - !_dev.Error) - { - capabilities.SupportsReadCapacity16 = true; - byte[] temp = new byte[8]; - Array.Copy(buffer, 0, temp, 0, 8); - Array.Reverse(temp); - capabilities.Blocks = BitConverter.ToUInt64(temp, 0) + 1; - capabilities.BlockSize = (uint)((buffer[8] << 24) + (buffer[9] << 16) + (buffer[10] << 8) + buffer[11]); - } - - Modes.DecodedMode? decMode = null; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI MODE SENSE (10)...").IsIndeterminate(); - - sense = _dev.ModeSense10(out buffer, out senseBuffer, false, true, ScsiModeSensePageControl.Current, - 0x3F, 0x00, _dev.Timeout, out _); - }); - - if(!sense && - !_dev.Error) - { - decMode = Modes.DecodeMode10(buffer, _dev.ScsiType); - capabilities.ModeSense10Data = buffer; - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Querying SCSI MODE SENSE...").IsIndeterminate(); - sense = _dev.ModeSense(out buffer, out senseBuffer, _dev.Timeout, out _); - }); - - if(!sense && - !_dev.Error) - { - decMode ??= Modes.DecodeMode6(buffer, _dev.ScsiType); - - capabilities.ModeSense6Data = buffer; - } - - if(decMode.HasValue) - { - capabilities.MediumType = (byte)decMode.Value.Header.MediumType; - - if(decMode.Value.Header.BlockDescriptors?.Length > 0) - capabilities.Density = (byte)decMode.Value.Header.BlockDescriptors[0].Density; - } - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (6)...").IsIndeterminate(); - - capabilities.SupportsRead6 = !_dev.Read6(out buffer, out senseBuffer, 0, capabilities.BlockSize ?? 512, - _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead6); - capabilities.Read6Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (10)...").IsIndeterminate(); - - capabilities.SupportsRead10 = !_dev.Read10(out buffer, out senseBuffer, 0, false, false, false, false, - 0, capabilities.BlockSize ?? 512, 0, 1, _dev.Timeout, out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead10); - capabilities.Read10Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (12)...").IsIndeterminate(); - - capabilities.SupportsRead12 = !_dev.Read12(out buffer, out senseBuffer, 0, false, false, false, false, - 0, capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, - out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead12); - capabilities.Read12Data = buffer; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ (16)...").IsIndeterminate(); - - capabilities.SupportsRead16 = !_dev.Read16(out buffer, out senseBuffer, 0, false, false, false, 0, - capabilities.BlockSize ?? 512, 0, 1, false, _dev.Timeout, - out _); - }); - - AaruConsole.DebugWriteLine("SCSI Report", "Sense = {0}", !capabilities.SupportsRead16); - capabilities.Read16Data = buffer; - - capabilities.LongBlockSize = capabilities.BlockSize; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ LONG (10)...").IsIndeterminate(); - sense = _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 0xFFFF, _dev.Timeout, out _); - }); - - if(sense && !_dev.Error) - { - DecodedSense? decSense = Sense.Decode(senseBuffer); - - if(decSense?.SenseKey == SenseKeys.IllegalRequest && - decSense.Value.ASC == 0x24 && - decSense.Value.ASCQ == 0x00) + break; + } + case 2048: { + sense = capabilities.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 2380, _dev.Timeout, out _) + : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 2380, _dev.Timeout, + out _); + + if(sense || _dev.Error) + return; + capabilities.SupportsReadLong = true; + capabilities.LongBlockSize = 2380; - bool valid = decSense.Value.Fixed?.InformationValid == true; - bool ili = decSense.Value.Fixed?.ILI == true; - uint information = decSense.Value.Fixed?.Information ?? 0; + break; + } + case 4096: + { + sense = capabilities.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 4760, _dev.Timeout, out _) + : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 4760, _dev.Timeout, + out _); - if(decSense.Value.Descriptor.HasValue && - decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) - { - valid = true; - ili = true; - information = (uint)Sense.DecodeDescriptor00(desc00); - } + if(sense || _dev.Error) + return; - if(valid && ili) - capabilities.LongBlockSize = 0xFFFF - (information & 0xFFFF); + capabilities.SupportsReadLong = true; + capabilities.LongBlockSize = 4760; + + break; + } + case 8192: + { + sense = capabilities.SupportsReadLong16 == true + ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 9424, _dev.Timeout, out _) + : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 9424, _dev.Timeout, + out _); + + if(sense || _dev.Error) + return; + + capabilities.SupportsReadLong = true; + capabilities.LongBlockSize = 9424; + + break; } } + }); - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying SCSI READ LONG (16)...").IsIndeterminate(); - sense = _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 0xFFFF, _dev.Timeout, out _); - }); - - if(sense && !_dev.Error) - { - capabilities.SupportsReadLong16 = true; - DecodedSense? decSense = Sense.Decode(senseBuffer); - - if(decSense?.SenseKey == SenseKeys.IllegalRequest && - decSense.Value.ASC == 0x24 && - decSense.Value.ASCQ == 0x00) - { - capabilities.SupportsReadLong16 = true; - - bool valid = decSense.Value.Fixed?.InformationValid == true; - bool ili = decSense.Value.Fixed?.ILI == true; - uint information = decSense.Value.Fixed?.Information ?? 0; - - if(decSense.Value.Descriptor.HasValue && - decSense.Value.Descriptor.Value.Descriptors.TryGetValue(0, out byte[] desc00)) - { - valid = true; - ili = true; - information = (uint)Sense.DecodeDescriptor00(desc00); - } - - if(valid && ili) - capabilities.LongBlockSize = 0xFFFF - (information & 0xFFFF); - } - } - - if((capabilities.SupportsReadLong != true && capabilities.SupportsReadLong16 != true) || - capabilities.LongBlockSize != capabilities.BlockSize) - return capabilities; - - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask(capabilities.SupportsReadLong16 == true ? "Trying SCSI READ LONG (16)..." - : "Trying SCSI READ LONG (10)...").IsIndeterminate(); - - switch(capabilities.BlockSize) - { - case 512: - { - foreach(ushort testSize in new ushort[] - { - // Long sector sizes for floppies - 514, - - // Long sector sizes for SuperDisk - 536, 558, - - // Long sector sizes for 512-byte magneto-opticals - 600, 610, 630 - }) - { - sense = capabilities.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, _dev.Timeout, - out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, - false, 0, testSize, _dev.Timeout, out _); - - if(sense || _dev.Error) - continue; - - capabilities.SupportsReadLong = true; - capabilities.LongBlockSize = testSize; - - break; - } - - break; - } - case 1024: - { - foreach(ushort testSize in new ushort[] - { - // Long sector sizes for floppies - 1026, - - // Long sector sizes for 1024-byte magneto-opticals - 1200 - }) - { - sense = capabilities.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, testSize, _dev.Timeout, - out _) : _dev.ReadLong10(out buffer, out senseBuffer, false, - false, 0, testSize, _dev.Timeout, out _); - - if(sense || _dev.Error) - continue; - - capabilities.SupportsReadLong = true; - capabilities.LongBlockSize = testSize; - - break; - } - - break; - } - case 2048: - { - sense = capabilities.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 2380, _dev.Timeout, out _) - : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 2380, _dev.Timeout, - out _); - - if(sense || _dev.Error) - return; - - capabilities.SupportsReadLong = true; - capabilities.LongBlockSize = 2380; - - break; - } - case 4096: - { - sense = capabilities.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 4760, _dev.Timeout, out _) - : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 4760, _dev.Timeout, - out _); - - if(sense || _dev.Error) - return; - - capabilities.SupportsReadLong = true; - capabilities.LongBlockSize = 4760; - - break; - } - case 8192: - { - sense = capabilities.SupportsReadLong16 == true - ? _dev.ReadLong16(out buffer, out senseBuffer, false, 0, 9424, _dev.Timeout, out _) - : _dev.ReadLong10(out buffer, out senseBuffer, false, false, 0, 9424, _dev.Timeout, - out _); - - if(sense || _dev.Error) - return; - - capabilities.SupportsReadLong = true; - capabilities.LongBlockSize = 9424; - - break; - } - } - }); - - return capabilities; - } + return capabilities; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/SecureDigital.cs b/Aaru.Core/Devices/Report/SecureDigital.cs index 0ff1e385f..33630c1da 100644 --- a/Aaru.Core/Devices/Report/SecureDigital.cs +++ b/Aaru.Core/Devices/Report/SecureDigital.cs @@ -36,146 +36,145 @@ using Aaru.CommonTypes.Metadata; using Aaru.Console; using Spectre.Console; -namespace Aaru.Core.Devices.Report +namespace Aaru.Core.Devices.Report; + +/// Implements creating a device report for a SecureDigital or MultiMediaCard flash card +public sealed partial class DeviceReport { - /// Implements creating a device report for a SecureDigital or MultiMediaCard flash card - public sealed partial class DeviceReport + /// Creates a device report for a SecureDigital or MultiMediaCard flash card + public MmcSd MmcSdReport() { - /// Creates a device report for a SecureDigital or MultiMediaCard flash card - public MmcSd MmcSdReport() + var report = new MmcSd(); + bool sense = true; + byte[] cid = Array.Empty(); + byte[] csd = Array.Empty(); + byte[] ecsd = Array.Empty(); + byte[] scr = Array.Empty(); + + Spectre.ProgressSingleSpinner(ctx => { - var report = new MmcSd(); - bool sense = true; - byte[] cid = Array.Empty(); - byte[] csd = Array.Empty(); - byte[] ecsd = Array.Empty(); - byte[] scr = Array.Empty(); + ctx.AddTask("Trying to get CID...").IsIndeterminate(); + sense = _dev.ReadCid(out cid, out _, _dev.Timeout, out _); + }); - Spectre.ProgressSingleSpinner(ctx => + if(!sense) + { + AaruConsole.WriteLine("CID obtained correctly..."); + + switch(_dev.Type) { - ctx.AddTask("Trying to get CID...").IsIndeterminate(); - sense = _dev.ReadCid(out cid, out _, _dev.Timeout, out _); - }); + case DeviceType.SecureDigital: + // Clear serial number and manufacturing date + cid[9] = 0; + cid[10] = 0; + cid[11] = 0; + cid[12] = 0; + cid[13] = 0; + cid[14] = 0; - if(!sense) - { - AaruConsole.WriteLine("CID obtained correctly..."); + break; + case DeviceType.MMC: + // Clear serial number and manufacturing date + cid[10] = 0; + cid[11] = 0; + cid[12] = 0; + cid[13] = 0; + cid[14] = 0; - switch(_dev.Type) - { - case DeviceType.SecureDigital: - // Clear serial number and manufacturing date - cid[9] = 0; - cid[10] = 0; - cid[11] = 0; - cid[12] = 0; - cid[13] = 0; - cid[14] = 0; - - break; - case DeviceType.MMC: - // Clear serial number and manufacturing date - cid[10] = 0; - cid[11] = 0; - cid[12] = 0; - cid[13] = 0; - cid[14] = 0; - - break; - } - - report.CID = cid; + break; } - else - AaruConsole.WriteLine("Could not read CID..."); - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to get CSD...").IsIndeterminate(); - sense = _dev.ReadCsd(out csd, out _, _dev.Timeout, out _); - }); + report.CID = cid; + } + else + AaruConsole.WriteLine("Could not read CID..."); - if(!sense) - { - AaruConsole.WriteLine("CSD obtained correctly..."); - report.CSD = csd; - } - else - AaruConsole.WriteLine("Could not read CSD..."); + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to get CSD...").IsIndeterminate(); + sense = _dev.ReadCsd(out csd, out _, _dev.Timeout, out _); + }); - sense = true; - byte[] ocr = null; + if(!sense) + { + AaruConsole.WriteLine("CSD obtained correctly..."); + report.CSD = csd; + } + else + AaruConsole.WriteLine("Could not read CSD..."); - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to get OCR...").IsIndeterminate(); + sense = true; + byte[] ocr = null; - switch(_dev.Type) - { - case DeviceType.MMC: - { - sense = _dev.ReadOcr(out ocr, out _, _dev.Timeout, out _); - - break; - } - case DeviceType.SecureDigital: - { - sense = _dev.ReadSdocr(out ocr, out _, _dev.Timeout, out _); - - break; - } - } - }); - - if(!sense) - { - AaruConsole.WriteLine("OCR obtained correctly..."); - report.OCR = ocr; - } - else - AaruConsole.WriteLine("Could not read OCR..."); + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to get OCR...").IsIndeterminate(); switch(_dev.Type) { case DeviceType.MMC: { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to get Extended CSD...").IsIndeterminate(); - sense = _dev.ReadExtendedCsd(out ecsd, out _, _dev.Timeout, out _); - }); - - if(!sense) - { - AaruConsole.WriteLine("Extended CSD obtained correctly..."); - report.ExtendedCSD = ecsd; - } - else - AaruConsole.WriteLine("Could not read Extended CSD..."); + sense = _dev.ReadOcr(out ocr, out _, _dev.Timeout, out _); break; } case DeviceType.SecureDigital: { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Trying to get SCR...").IsIndeterminate(); - sense = _dev.ReadScr(out scr, out _, _dev.Timeout, out _); - }); - - if(!sense) - { - AaruConsole.WriteLine("SCR obtained correctly..."); - report.SCR = scr; - } - else - AaruConsole.WriteLine("Could not read SCR..."); + sense = _dev.ReadSdocr(out ocr, out _, _dev.Timeout, out _); break; } } + }); - return report; + if(!sense) + { + AaruConsole.WriteLine("OCR obtained correctly..."); + report.OCR = ocr; } + else + AaruConsole.WriteLine("Could not read OCR..."); + + switch(_dev.Type) + { + case DeviceType.MMC: + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to get Extended CSD...").IsIndeterminate(); + sense = _dev.ReadExtendedCsd(out ecsd, out _, _dev.Timeout, out _); + }); + + if(!sense) + { + AaruConsole.WriteLine("Extended CSD obtained correctly..."); + report.ExtendedCSD = ecsd; + } + else + AaruConsole.WriteLine("Could not read Extended CSD..."); + + break; + } + case DeviceType.SecureDigital: + { + Spectre.ProgressSingleSpinner(ctx => + { + ctx.AddTask("Trying to get SCR...").IsIndeterminate(); + sense = _dev.ReadScr(out scr, out _, _dev.Timeout, out _); + }); + + if(!sense) + { + AaruConsole.WriteLine("SCR obtained correctly..."); + report.SCR = scr; + } + else + AaruConsole.WriteLine("Could not read SCR..."); + + break; + } + } + + return report; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Report/USB.cs b/Aaru.Core/Devices/Report/USB.cs index 48ca6a45e..899cbff81 100644 --- a/Aaru.Core/Devices/Report/USB.cs +++ b/Aaru.Core/Devices/Report/USB.cs @@ -32,24 +32,23 @@ using Aaru.CommonTypes.Metadata; -namespace Aaru.Core.Devices.Report -{ - /// Implements creating a report for a USB device - public sealed partial class DeviceReport - { - /// Fills a device report with parameters specific to a USB device - public Usb UsbReport() - { - var usbReport = new Usb - { - Manufacturer = _dev.UsbManufacturerString, - Product = _dev.UsbProductString, - ProductID = _dev.UsbProductId, - VendorID = _dev.UsbVendorId, - Descriptors = _dev.UsbDescriptors - }; +namespace Aaru.Core.Devices.Report; - return usbReport; - } +/// Implements creating a report for a USB device +public sealed partial class DeviceReport +{ + /// Fills a device report with parameters specific to a USB device + public Usb UsbReport() + { + var usbReport = new Usb + { + Manufacturer = _dev.UsbManufacturerString, + Product = _dev.UsbProductString, + ProductID = _dev.UsbProductId, + VendorID = _dev.UsbVendorId, + Descriptors = _dev.UsbDescriptors + }; + + return usbReport; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Scanning/ATA.cs b/Aaru.Core/Devices/Scanning/ATA.cs index b94918f99..fbafddfc5 100644 --- a/Aaru.Core/Devices/Scanning/ATA.cs +++ b/Aaru.Core/Devices/Scanning/ATA.cs @@ -35,337 +35,336 @@ using System.Collections.Generic; using Aaru.CommonTypes.Structs.Devices.ATA; using Aaru.Core.Logging; -namespace Aaru.Core.Devices.Scanning +namespace Aaru.Core.Devices.Scanning; + +/// Implements scanning the media from an ATA device +public sealed partial class MediaScan { - /// Implements scanning the media from an ATA device - public sealed partial class MediaScan + /// Scans the media from an ATA device + /// Scanning results + ScanResults Ata() { - /// Scans the media from an ATA device - /// Scanning results - ScanResults Ata() + var results = new ScanResults { - var results = new ScanResults + Blocks = 0 + }; + + const ushort ataProfile = 0x0001; + const uint timeout = 5; + + bool sense = _dev.AtaIdentify(out byte[] cmdBuf, out _); + + if(!sense && + Identify.Decode(cmdBuf).HasValue) + { + // Initialize reader + var ataReader = new Reader(_dev, timeout, cmdBuf, null); + + // Fill reader blocks + results.Blocks = ataReader.GetDeviceBlocks(); + + if(ataReader.FindReadCommand()) { - Blocks = 0 - }; - - const ushort ataProfile = 0x0001; - const uint timeout = 5; - - bool sense = _dev.AtaIdentify(out byte[] cmdBuf, out _); - - if(!sense && - Identify.Decode(cmdBuf).HasValue) - { - // Initialize reader - var ataReader = new Reader(_dev, timeout, cmdBuf, null); - - // Fill reader blocks - results.Blocks = ataReader.GetDeviceBlocks(); - - if(ataReader.FindReadCommand()) - { - StoppingErrorMessage?.Invoke(ataReader.ErrorMessage); - - return results; - } - - // Check block sizes - if(ataReader.GetBlockSize()) - { - StoppingErrorMessage?.Invoke(ataReader.ErrorMessage); - - return results; - } - - uint blockSize = ataReader.LogicalBlockSize; - - // Check how many blocks to read, if error show and return - if(ataReader.GetBlocksToRead()) - { - StoppingErrorMessage?.Invoke(ataReader.ErrorMessage); - - return results; - } - - uint blocksToRead = ataReader.BlocksToRead; - ushort cylinders = ataReader.Cylinders; - byte heads = ataReader.Heads; - byte sectors = ataReader.Sectors; - - results.A = 0; // <3ms - results.B = 0; // >=3ms, <10ms - results.C = 0; // >=10ms, <50ms - results.D = 0; // >=50ms, <150ms - results.E = 0; // >=150ms, <500ms - results.F = 0; // >=500ms - results.Errored = 0; - DateTime start; - DateTime end; - results.ProcessingTime = 0; - double currentSpeed = 0; - results.MaxSpeed = double.MinValue; - results.MinSpeed = double.MaxValue; - results.UnreadableSectors = new List(); - results.SeekMax = double.MinValue; - results.SeekMin = double.MaxValue; - results.SeekTotal = 0; - const int seekTimes = 1000; - - double seekCur; - - var rnd = new Random(); - - MhddLog mhddLog; - IbgLog ibgLog; - double duration; - - if(ataReader.IsLba) - { - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); - - InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ataProfile); - mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); - ibgLog = new IbgLog(_ibgLogPath, ataProfile); - - start = DateTime.UtcNow; - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; - InitProgress?.Invoke(); - - for(ulong i = 0; i < results.Blocks; i += blocksToRead) - { - if(_aborted) - break; - - if(results.Blocks - i < blocksToRead) - blocksToRead = (byte)(results.Blocks - i); - - if(currentSpeed > results.MaxSpeed && - currentSpeed > 0) - results.MaxSpeed = currentSpeed; - - if(currentSpeed < results.MinSpeed && - currentSpeed > 0) - results.MinSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", - (long)i, (long)results.Blocks); - - bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration, out _, out _); - - if(!error) - { - if(duration >= 500) - results.F += blocksToRead; - else if(duration >= 150) - results.E += blocksToRead; - else if(duration >= 50) - results.D += blocksToRead; - else if(duration >= 10) - results.C += blocksToRead; - else if(duration >= 3) - results.B += blocksToRead; - else - results.A += blocksToRead; - - ScanTime?.Invoke(i, duration); - mhddLog.Write(i, duration); - ibgLog.Write(i, currentSpeed * 1024); - } - else - { - ScanUnreadable?.Invoke(i); - results.Errored += blocksToRead; - - for(ulong b = i; b < i + blocksToRead; b++) - results.UnreadableSectors.Add(b); - - mhddLog.Write(i, duration < 500 ? 65535 : duration); - - ibgLog.Write(i, 0); - } - - sectorSpeedStart += blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - ScanSpeed?.Invoke(i, currentSpeed * 1024); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - end = DateTime.UtcNow; - EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), - _devicePath); - - InitProgress?.Invoke(); - - if(ataReader.CanSeekLba && _seekTest) - for(int i = 0; i < seekTimes; i++) - { - if(_aborted) - break; - - uint seekPos = (uint)rnd.Next((int)results.Blocks); - - PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t"); - - ataReader.Seek(seekPos, out seekCur); - - if(seekCur > results.SeekMax && - seekCur > 0) - results.SeekMax = seekCur; - - if(seekCur < results.SeekMin && - seekCur > 0) - results.SeekMin = seekCur; - - results.SeekTotal += seekCur; - GC.Collect(); - } - - EndProgress?.Invoke(); - } - else - { - InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ataProfile); - mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); - ibgLog = new IbgLog(_ibgLogPath, ataProfile); - - ulong currentBlock = 0; - results.Blocks = (ulong)(cylinders * heads * sectors); - start = DateTime.UtcNow; - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; - InitProgress?.Invoke(); - - for(ushort cy = 0; cy < cylinders; cy++) - { - for(byte hd = 0; hd < heads; hd++) - { - for(byte sc = 1; sc < sectors; sc++) - { - if(_aborted) - break; - - if(currentSpeed > results.MaxSpeed && - currentSpeed > 0) - results.MaxSpeed = currentSpeed; - - if(currentSpeed < results.MinSpeed && - currentSpeed > 0) - results.MinSpeed = currentSpeed; - - PulseProgress?. - Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)"); - - bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration, out _); - - if(!error) - { - if(duration >= 500) - results.F += blocksToRead; - else if(duration >= 150) - results.E += blocksToRead; - else if(duration >= 50) - results.D += blocksToRead; - else if(duration >= 10) - results.C += blocksToRead; - else if(duration >= 3) - results.B += blocksToRead; - else - results.A += blocksToRead; - - ScanTime?.Invoke(currentBlock, duration); - mhddLog.Write(currentBlock, duration); - ibgLog.Write(currentBlock, currentSpeed * 1024); - } - else - { - ScanUnreadable?.Invoke(currentBlock); - results.Errored += blocksToRead; - results.UnreadableSectors.Add(currentBlock); - mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration); - - ibgLog.Write(currentBlock, 0); - } - - sectorSpeedStart++; - currentBlock++; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - ScanSpeed?.Invoke(currentBlock, currentSpeed * 1024); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - } - } - - end = DateTime.UtcNow; - EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), - _devicePath); - - InitProgress?.Invoke(); - - if(ataReader.CanSeek) - for(int i = 0; i < seekTimes; i++) - { - if(_aborted) - break; - - ushort seekCy = (ushort)rnd.Next(cylinders); - byte seekHd = (byte)rnd.Next(heads); - byte seekSc = (byte)rnd.Next(sectors); - - PulseProgress?. - Invoke($"\rSeeking to cylinder {seekCy}, head {seekHd}, sector {seekSc}...\t\t"); - - ataReader.SeekChs(seekCy, seekHd, seekSc, out seekCur); - - if(seekCur > results.SeekMax && - seekCur > 0) - results.SeekMax = seekCur; - - if(seekCur < results.SeekMin && - seekCur > 0) - results.SeekMin = seekCur; - - results.SeekTotal += seekCur; - GC.Collect(); - } - - EndProgress?.Invoke(); - } - - results.ProcessingTime /= 1000; - results.TotalTime = (end - start).TotalSeconds; - results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; - results.SeekTimes = seekTimes; + StoppingErrorMessage?.Invoke(ataReader.ErrorMessage); return results; } - StoppingErrorMessage?.Invoke("Unable to communicate with ATA device."); + // Check block sizes + if(ataReader.GetBlockSize()) + { + StoppingErrorMessage?.Invoke(ataReader.ErrorMessage); + + return results; + } + + uint blockSize = ataReader.LogicalBlockSize; + + // Check how many blocks to read, if error show and return + if(ataReader.GetBlocksToRead()) + { + StoppingErrorMessage?.Invoke(ataReader.ErrorMessage); + + return results; + } + + uint blocksToRead = ataReader.BlocksToRead; + ushort cylinders = ataReader.Cylinders; + byte heads = ataReader.Heads; + byte sectors = ataReader.Sectors; + + results.A = 0; // <3ms + results.B = 0; // >=3ms, <10ms + results.C = 0; // >=10ms, <50ms + results.D = 0; // >=50ms, <150ms + results.E = 0; // >=150ms, <500ms + results.F = 0; // >=500ms + results.Errored = 0; + DateTime start; + DateTime end; + results.ProcessingTime = 0; + double currentSpeed = 0; + results.MaxSpeed = double.MinValue; + results.MinSpeed = double.MaxValue; + results.UnreadableSectors = new List(); + results.SeekMax = double.MinValue; + results.SeekMin = double.MaxValue; + results.SeekTotal = 0; + const int seekTimes = 1000; + + double seekCur; + + var rnd = new Random(); + + MhddLog mhddLog; + IbgLog ibgLog; + double duration; + + if(ataReader.IsLba) + { + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + + InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ataProfile); + mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); + ibgLog = new IbgLog(_ibgLogPath, ataProfile); + + start = DateTime.UtcNow; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(ulong i = 0; i < results.Blocks; i += blocksToRead) + { + if(_aborted) + break; + + if(results.Blocks - i < blocksToRead) + blocksToRead = (byte)(results.Blocks - i); + + if(currentSpeed > results.MaxSpeed && + currentSpeed > 0) + results.MaxSpeed = currentSpeed; + + if(currentSpeed < results.MinSpeed && + currentSpeed > 0) + results.MinSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", + (long)i, (long)results.Blocks); + + bool error = ataReader.ReadBlocks(out cmdBuf, i, blocksToRead, out duration, out _, out _); + + if(!error) + { + if(duration >= 500) + results.F += blocksToRead; + else if(duration >= 150) + results.E += blocksToRead; + else if(duration >= 50) + results.D += blocksToRead; + else if(duration >= 10) + results.C += blocksToRead; + else if(duration >= 3) + results.B += blocksToRead; + else + results.A += blocksToRead; + + ScanTime?.Invoke(i, duration); + mhddLog.Write(i, duration); + ibgLog.Write(i, currentSpeed * 1024); + } + else + { + ScanUnreadable?.Invoke(i); + results.Errored += blocksToRead; + + for(ulong b = i; b < i + blocksToRead; b++) + results.UnreadableSectors.Add(b); + + mhddLog.Write(i, duration < 500 ? 65535 : duration); + + ibgLog.Write(i, 0); + } + + sectorSpeedStart += blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + ScanSpeed?.Invoke(i, currentSpeed * 1024); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + + end = DateTime.UtcNow; + EndProgress?.Invoke(); + mhddLog.Close(); + + ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), + _devicePath); + + InitProgress?.Invoke(); + + if(ataReader.CanSeekLba && _seekTest) + for(int i = 0; i < seekTimes; i++) + { + if(_aborted) + break; + + uint seekPos = (uint)rnd.Next((int)results.Blocks); + + PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t"); + + ataReader.Seek(seekPos, out seekCur); + + if(seekCur > results.SeekMax && + seekCur > 0) + results.SeekMax = seekCur; + + if(seekCur < results.SeekMin && + seekCur > 0) + results.SeekMin = seekCur; + + results.SeekTotal += seekCur; + GC.Collect(); + } + + EndProgress?.Invoke(); + } + else + { + InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, ataProfile); + mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); + ibgLog = new IbgLog(_ibgLogPath, ataProfile); + + ulong currentBlock = 0; + results.Blocks = (ulong)(cylinders * heads * sectors); + start = DateTime.UtcNow; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(ushort cy = 0; cy < cylinders; cy++) + { + for(byte hd = 0; hd < heads; hd++) + { + for(byte sc = 1; sc < sectors; sc++) + { + if(_aborted) + break; + + if(currentSpeed > results.MaxSpeed && + currentSpeed > 0) + results.MaxSpeed = currentSpeed; + + if(currentSpeed < results.MinSpeed && + currentSpeed > 0) + results.MinSpeed = currentSpeed; + + PulseProgress?. + Invoke($"Reading cylinder {cy} head {hd} sector {sc} ({currentSpeed:F3} MiB/sec.)"); + + bool error = ataReader.ReadChs(out cmdBuf, cy, hd, sc, out duration, out _); + + if(!error) + { + if(duration >= 500) + results.F += blocksToRead; + else if(duration >= 150) + results.E += blocksToRead; + else if(duration >= 50) + results.D += blocksToRead; + else if(duration >= 10) + results.C += blocksToRead; + else if(duration >= 3) + results.B += blocksToRead; + else + results.A += blocksToRead; + + ScanTime?.Invoke(currentBlock, duration); + mhddLog.Write(currentBlock, duration); + ibgLog.Write(currentBlock, currentSpeed * 1024); + } + else + { + ScanUnreadable?.Invoke(currentBlock); + results.Errored += blocksToRead; + results.UnreadableSectors.Add(currentBlock); + mhddLog.Write(currentBlock, duration < 500 ? 65535 : duration); + + ibgLog.Write(currentBlock, 0); + } + + sectorSpeedStart++; + currentBlock++; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + ScanSpeed?.Invoke(currentBlock, currentSpeed * 1024); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + } + } + + end = DateTime.UtcNow; + EndProgress?.Invoke(); + mhddLog.Close(); + + ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), + _devicePath); + + InitProgress?.Invoke(); + + if(ataReader.CanSeek) + for(int i = 0; i < seekTimes; i++) + { + if(_aborted) + break; + + ushort seekCy = (ushort)rnd.Next(cylinders); + byte seekHd = (byte)rnd.Next(heads); + byte seekSc = (byte)rnd.Next(sectors); + + PulseProgress?. + Invoke($"\rSeeking to cylinder {seekCy}, head {seekHd}, sector {seekSc}...\t\t"); + + ataReader.SeekChs(seekCy, seekHd, seekSc, out seekCur); + + if(seekCur > results.SeekMax && + seekCur > 0) + results.SeekMax = seekCur; + + if(seekCur < results.SeekMin && + seekCur > 0) + results.SeekMin = seekCur; + + results.SeekTotal += seekCur; + GC.Collect(); + } + + EndProgress?.Invoke(); + } + + results.ProcessingTime /= 1000; + results.TotalTime = (end - start).TotalSeconds; + results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; + results.SeekTimes = seekTimes; return results; } + + StoppingErrorMessage?.Invoke("Unable to communicate with ATA device."); + + return results; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Scanning/MediaScan.cs b/Aaru.Core/Devices/Scanning/MediaScan.cs index b7f2f7cc2..168ce0fc7 100644 --- a/Aaru.Core/Devices/Scanning/MediaScan.cs +++ b/Aaru.Core/Devices/Scanning/MediaScan.cs @@ -35,78 +35,77 @@ using Aaru.CommonTypes; using Aaru.CommonTypes.Enums; using Aaru.Devices; -namespace Aaru.Core.Devices.Scanning +namespace Aaru.Core.Devices.Scanning; + +public sealed partial class MediaScan { - public sealed partial class MediaScan + readonly Device _dev; + readonly string _devicePath; + readonly string _ibgLogPath; + readonly string _mhddLogPath; + readonly bool _seekTest; + readonly bool _useBufferedReads; + bool _aborted; + + /// Path to a MHDD log file + /// Path to a IMGBurn log file + /// Device path + /// Device + /// Enable seek test + /// + /// If MMC/SD does not support CMD23, use OS buffered reads instead of multiple single block + /// commands + /// + public MediaScan(string mhddLogPath, string ibgLogPath, string devicePath, Device dev, bool useBufferedReads, + bool seekTest = true) { - readonly Device _dev; - readonly string _devicePath; - readonly string _ibgLogPath; - readonly string _mhddLogPath; - readonly bool _seekTest; - readonly bool _useBufferedReads; - bool _aborted; - - /// Path to a MHDD log file - /// Path to a IMGBurn log file - /// Device path - /// Device - /// Enable seek test - /// - /// If MMC/SD does not support CMD23, use OS buffered reads instead of multiple single block - /// commands - /// - public MediaScan(string mhddLogPath, string ibgLogPath, string devicePath, Device dev, bool useBufferedReads, - bool seekTest = true) - { - _mhddLogPath = mhddLogPath; - _ibgLogPath = ibgLogPath; - _devicePath = devicePath; - _dev = dev; - _aborted = false; - _seekTest = seekTest; - _useBufferedReads = useBufferedReads; - } - - /// Starts a media scan - /// Media scan results - /// Unknown device type - public ScanResults Scan() - { - switch(_dev.Type) - { - case DeviceType.ATA: return Ata(); - case DeviceType.MMC: - case DeviceType.SecureDigital: return SecureDigital(); - case DeviceType.NVMe: return Nvme(); - case DeviceType.ATAPI: - case DeviceType.SCSI: return Scsi(); - default: throw new NotSupportedException("Unknown device type."); - } - } - - /// Aborts the running media scan - public void Abort() => _aborted = true; - - /// Event raised when the progress bar is not longer needed - public event EndProgressHandler EndProgress; - /// Event raised when a progress bar is needed - public event InitProgressHandler InitProgress; - /// Event raised to report status updates - public event UpdateStatusHandler UpdateStatus; - /// Event raised to report a fatal error that stops the dumping operation and should call user's attention - public event ErrorMessageHandler StoppingErrorMessage; - /// Event raised to update the values of a determinate progress bar - public event UpdateProgressHandler UpdateProgress; - /// Event raised to update the status of an indeterminate progress bar - public event PulseProgressHandler PulseProgress; - /// Updates lists of time taken on scanning from the specified sector - public event ScanTimeHandler ScanTime; - /// Specified a number of blocks could not be read on scan - public event ScanUnreadableHandler ScanUnreadable; - /// Initializes a block map that's going to be filled with a media scan - public event InitBlockMapHandler InitBlockMap; - /// Sends the speed of scanning a specific sector - public event ScanSpeedHandler ScanSpeed; + _mhddLogPath = mhddLogPath; + _ibgLogPath = ibgLogPath; + _devicePath = devicePath; + _dev = dev; + _aborted = false; + _seekTest = seekTest; + _useBufferedReads = useBufferedReads; } + + /// Starts a media scan + /// Media scan results + /// Unknown device type + public ScanResults Scan() + { + switch(_dev.Type) + { + case DeviceType.ATA: return Ata(); + case DeviceType.MMC: + case DeviceType.SecureDigital: return SecureDigital(); + case DeviceType.NVMe: return Nvme(); + case DeviceType.ATAPI: + case DeviceType.SCSI: return Scsi(); + default: throw new NotSupportedException("Unknown device type."); + } + } + + /// Aborts the running media scan + public void Abort() => _aborted = true; + + /// Event raised when the progress bar is not longer needed + public event EndProgressHandler EndProgress; + /// Event raised when a progress bar is needed + public event InitProgressHandler InitProgress; + /// Event raised to report status updates + public event UpdateStatusHandler UpdateStatus; + /// Event raised to report a fatal error that stops the dumping operation and should call user's attention + public event ErrorMessageHandler StoppingErrorMessage; + /// Event raised to update the values of a determinate progress bar + public event UpdateProgressHandler UpdateProgress; + /// Event raised to update the status of an indeterminate progress bar + public event PulseProgressHandler PulseProgress; + /// Updates lists of time taken on scanning from the specified sector + public event ScanTimeHandler ScanTime; + /// Specified a number of blocks could not be read on scan + public event ScanUnreadableHandler ScanUnreadable; + /// Initializes a block map that's going to be filled with a media scan + public event InitBlockMapHandler InitBlockMap; + /// Sends the speed of scanning a specific sector + public event ScanSpeedHandler ScanSpeed; } \ No newline at end of file diff --git a/Aaru.Core/Devices/Scanning/NVMe.cs b/Aaru.Core/Devices/Scanning/NVMe.cs index 388275eeb..7888249c3 100644 --- a/Aaru.Core/Devices/Scanning/NVMe.cs +++ b/Aaru.Core/Devices/Scanning/NVMe.cs @@ -30,15 +30,14 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Core.Devices.Scanning -{ - public sealed partial class MediaScan - { - ScanResults Nvme() - { - StoppingErrorMessage?.Invoke("NVMe devices not yet supported."); +namespace Aaru.Core.Devices.Scanning; - return default; - } +public sealed partial class MediaScan +{ + ScanResults Nvme() + { + StoppingErrorMessage?.Invoke("NVMe devices not yet supported."); + + return default; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Scanning/SCSI.cs b/Aaru.Core/Devices/Scanning/SCSI.cs index f9fb67abf..179e92838 100644 --- a/Aaru.Core/Devices/Scanning/SCSI.cs +++ b/Aaru.Core/Devices/Scanning/SCSI.cs @@ -41,513 +41,408 @@ using Aaru.Decoders.SCSI; using Aaru.Decoders.SCSI.MMC; using Aaru.Devices; -namespace Aaru.Core.Devices.Scanning +namespace Aaru.Core.Devices.Scanning; + +/// Implements scanning the media from an SCSI device +public sealed partial class MediaScan { - /// Implements scanning the media from an SCSI device - public sealed partial class MediaScan + ScanResults Scsi() { - ScanResults Scsi() + var results = new ScanResults(); + MhddLog mhddLog; + IbgLog ibgLog; + byte[] senseBuf; + bool sense; + uint blockSize = 0; + ushort currentProfile = 0x0001; + bool foundReadCommand = false; + bool readcd = false; + + results.Blocks = 0; + + if(_dev.IsRemovable) { - var results = new ScanResults(); - MhddLog mhddLog; - IbgLog ibgLog; - byte[] senseBuf; - bool sense; - uint blockSize = 0; - ushort currentProfile = 0x0001; - bool foundReadCommand = false; - bool readcd = false; + sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); - results.Blocks = 0; - - if(_dev.IsRemovable) + if(sense) { - sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); + InitProgress?.Invoke(); + DecodedSense? decSense = Sense.Decode(senseBuf); - if(sense) - { - InitProgress?.Invoke(); - DecodedSense? decSense = Sense.Decode(senseBuf); - - if(decSense.HasValue) - switch(decSense.Value.ASC) + if(decSense.HasValue) + switch(decSense.Value.ASC) + { + case 0x3A: { - case 0x3A: + int leftRetries = 5; + + while(leftRetries > 0) { - int leftRetries = 5; + PulseProgress?.Invoke("Waiting for drive to become ready"); + Thread.Sleep(2000); + sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); - while(leftRetries > 0) - { - PulseProgress?.Invoke("Waiting for drive to become ready"); - Thread.Sleep(2000); - sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); + if(!sense) + break; - if(!sense) - break; - - leftRetries--; - } - - if(sense) - { - StoppingErrorMessage?.Invoke("Please insert media in drive"); - - return results; - } - - break; - } - case 0x04 when decSense.Value.ASCQ == 0x01: - { - int leftRetries = 10; - - while(leftRetries > 0) - { - PulseProgress?.Invoke("Waiting for drive to become ready"); - Thread.Sleep(2000); - sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); - - if(!sense) - break; - - leftRetries--; - } - - if(sense) - { - StoppingErrorMessage?. - Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); - - return results; - } - - break; + leftRetries--; } - // These should be trapped by the OS but seems in some cases they're not - case 0x28: + if(sense) { - int leftRetries = 10; + StoppingErrorMessage?.Invoke("Please insert media in drive"); - while(leftRetries > 0) - { - PulseProgress?.Invoke("Waiting for drive to become ready"); - Thread.Sleep(2000); - sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); - - if(!sense) - break; - - leftRetries--; - } - - if(sense) - { - StoppingErrorMessage?. - Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); - - return results; - } - - break; + return results; } - default: + + break; + } + case 0x04 when decSense.Value.ASCQ == 0x01: + { + int leftRetries = 10; + + while(leftRetries > 0) + { + PulseProgress?.Invoke("Waiting for drive to become ready"); + Thread.Sleep(2000); + sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); + + if(!sense) + break; + + leftRetries--; + } + + if(sense) + { StoppingErrorMessage?. Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); return results; - } - else - { - StoppingErrorMessage?.Invoke("Unknown testing unit was ready."); - - return results; - } - - EndProgress?.Invoke(); - } - } - - Reader scsiReader = null; - - switch(_dev.ScsiType) - { - case PeripheralDeviceTypes.DirectAccess: - case PeripheralDeviceTypes.MultiMediaDevice: - case PeripheralDeviceTypes.OCRWDevice: - case PeripheralDeviceTypes.OpticalDevice: - case PeripheralDeviceTypes.SimplifiedDevice: - case PeripheralDeviceTypes.WriteOnceDevice: - scsiReader = new Reader(_dev, _dev.Timeout, null, null); - results.Blocks = scsiReader.GetDeviceBlocks(); - foundReadCommand = !scsiReader.FindReadCommand(); - - if(!foundReadCommand && - _dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) - { - StoppingErrorMessage?.Invoke("Unable to read medium."); - - return results; - } - - blockSize = scsiReader.LogicalBlockSize; - - if(results.Blocks != 0 && - blockSize != 0) - { - results.Blocks++; - - ulong totalSize = results.Blocks * blockSize; - - if(totalSize > 1099511627776) - UpdateStatus?. - Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); - else if(totalSize > 1073741824) - UpdateStatus?. - Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); - else if(totalSize > 1048576) - UpdateStatus?. - Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); - else if(totalSize > 1024) - UpdateStatus?. - Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); - else - UpdateStatus?. - Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); - } - - break; - case PeripheralDeviceTypes.SequentialAccess: - StoppingErrorMessage?.Invoke("Scanning will never be supported on SCSI Streaming Devices." + - Environment.NewLine + - "It has no sense to do it, and it will put too much strain on the tape."); - - return results; - } - - if(results.Blocks == 0) - { - StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); - - return results; - } - - bool compactDisc = true; - FullTOC.CDFullTOC? toc = null; - - if(_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) - { - sense = _dev.GetConfiguration(out byte[] cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current, - _dev.Timeout, out _); - - if(!sense) - { - Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); - - currentProfile = ftr.CurrentProfile; - - switch(ftr.CurrentProfile) - { - case 0x0005: - case 0x0008: - case 0x0009: - case 0x000A: - case 0x0020: - case 0x0021: - case 0x0022: break; - default: - compactDisc = false; + } break; + } + + // These should be trapped by the OS but seems in some cases they're not + case 0x28: + { + int leftRetries = 10; + + while(leftRetries > 0) + { + PulseProgress?.Invoke("Waiting for drive to become ready"); + Thread.Sleep(2000); + sense = _dev.ScsiTestUnitReady(out senseBuf, _dev.Timeout, out _); + + if(!sense) + break; + + leftRetries--; + } + + if(sense) + { + StoppingErrorMessage?. + Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); + + return results; + } + + break; + } + default: + StoppingErrorMessage?. + Invoke($"Error testing unit was ready:\n{Sense.PrettifySense(senseBuf)}"); + + return results; } - } - - if(compactDisc) + else { - currentProfile = 0x0008; - - // We discarded all discs that falsify a TOC before requesting a real TOC - // No TOC, no CD (or an empty one) - bool tocSense = _dev.ReadRawToc(out cmdBuf, out senseBuf, 1, _dev.Timeout, out _); - - if(!tocSense) - toc = FullTOC.Decode(cmdBuf); - } - } - else - compactDisc = false; - - scsiReader.GetBlocksToRead(); - - uint blocksToRead; - results.A = 0; // <3ms - results.B = 0; // >=3ms, <10ms - results.C = 0; // >=10ms, <50ms - results.D = 0; // >=50ms, <150ms - results.E = 0; // >=150ms, <500ms - results.F = 0; // >=500ms - results.Errored = 0; - DateTime start; - DateTime end; - results.ProcessingTime = 0; - results.TotalTime = 0; - double currentSpeed = 0; - results.MaxSpeed = double.MinValue; - results.MinSpeed = double.MaxValue; - results.UnreadableSectors = new List(); - - if(compactDisc) - { - blocksToRead = 64; - - if(toc == null) - { - StoppingErrorMessage?.Invoke("Error trying to decode TOC..."); + StoppingErrorMessage?.Invoke("Unknown testing unit was ready."); return results; } - readcd = !_dev.ReadCd(out _, out senseBuf, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out _); + EndProgress?.Invoke(); + } + } - if(readcd) - UpdateStatus?.Invoke("Using MMC READ CD command."); - else if(!foundReadCommand) + Reader scsiReader = null; + + switch(_dev.ScsiType) + { + case PeripheralDeviceTypes.DirectAccess: + case PeripheralDeviceTypes.MultiMediaDevice: + case PeripheralDeviceTypes.OCRWDevice: + case PeripheralDeviceTypes.OpticalDevice: + case PeripheralDeviceTypes.SimplifiedDevice: + case PeripheralDeviceTypes.WriteOnceDevice: + scsiReader = new Reader(_dev, _dev.Timeout, null, null); + results.Blocks = scsiReader.GetDeviceBlocks(); + foundReadCommand = !scsiReader.FindReadCommand(); + + if(!foundReadCommand && + _dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) { StoppingErrorMessage?.Invoke("Unable to read medium."); return results; } - start = DateTime.UtcNow; + blockSize = scsiReader.LogicalBlockSize; - if(readcd) - while(true) - { - sense = _dev.ReadCd(out _, out senseBuf, 0, 2352, blocksToRead, MmcSectorTypes.AllTypes, false, - false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - MmcSubchannel.None, _dev.Timeout, out _); - - if(_dev.Error || sense) - blocksToRead /= 2; - - if(!_dev.Error || - blocksToRead == 1) - break; - } - - if(_dev.Error) + if(results.Blocks != 0 && + blockSize != 0) { - StoppingErrorMessage?. - Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + results.Blocks++; - return results; + ulong totalSize = results.Blocks * blockSize; + + if(totalSize > 1099511627776) + UpdateStatus?. + Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"); + else if(totalSize > 1073741824) + UpdateStatus?. + Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"); + else if(totalSize > 1048576) + UpdateStatus?. + Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"); + else if(totalSize > 1024) + UpdateStatus?. + Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"); + else + UpdateStatus?. + Invoke($"Media has {results.Blocks} blocks of {blockSize} bytes/each. (for a total of {totalSize} bytes)"); } - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + break; + case PeripheralDeviceTypes.SequentialAccess: + StoppingErrorMessage?.Invoke("Scanning will never be supported on SCSI Streaming Devices." + + Environment.NewLine + + "It has no sense to do it, and it will put too much strain on the tape."); - InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile); - mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); - ibgLog = new IbgLog(_ibgLogPath, currentProfile); - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; + return results; + } - InitProgress?.Invoke(); + if(results.Blocks == 0) + { + StoppingErrorMessage?.Invoke("Unable to read medium or empty medium present..."); - for(ulong i = 0; i < results.Blocks; i += blocksToRead) + return results; + } + + bool compactDisc = true; + FullTOC.CDFullTOC? toc = null; + + if(_dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) + { + sense = _dev.GetConfiguration(out byte[] cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current, + _dev.Timeout, out _); + + if(!sense) + { + Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); + + currentProfile = ftr.CurrentProfile; + + switch(ftr.CurrentProfile) { - if(_aborted) + case 0x0005: + case 0x0008: + case 0x0009: + case 0x000A: + case 0x0020: + case 0x0021: + case 0x0022: break; + default: + compactDisc = false; + break; + } + } - double cmdDuration; + if(compactDisc) + { + currentProfile = 0x0008; - if(results.Blocks - i < blocksToRead) - blocksToRead = (uint)(results.Blocks - i); + // We discarded all discs that falsify a TOC before requesting a real TOC + // No TOC, no CD (or an empty one) + bool tocSense = _dev.ReadRawToc(out cmdBuf, out senseBuf, 1, _dev.Timeout, out _); - if(currentSpeed > results.MaxSpeed && - currentSpeed > 0) - results.MaxSpeed = currentSpeed; + if(!tocSense) + toc = FullTOC.Decode(cmdBuf); + } + } + else + compactDisc = false; - if(currentSpeed < results.MinSpeed && - currentSpeed > 0) - results.MinSpeed = currentSpeed; + scsiReader.GetBlocksToRead(); - UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", - (long)i, (long)results.Blocks); + uint blocksToRead; + results.A = 0; // <3ms + results.B = 0; // >=3ms, <10ms + results.C = 0; // >=10ms, <50ms + results.D = 0; // >=50ms, <150ms + results.E = 0; // >=150ms, <500ms + results.F = 0; // >=500ms + results.Errored = 0; + DateTime start; + DateTime end; + results.ProcessingTime = 0; + results.TotalTime = 0; + double currentSpeed = 0; + results.MaxSpeed = double.MinValue; + results.MinSpeed = double.MaxValue; + results.UnreadableSectors = new List(); + + if(compactDisc) + { + blocksToRead = 64; + + if(toc == null) + { + StoppingErrorMessage?.Invoke("Error trying to decode TOC..."); + + return results; + } + + readcd = !_dev.ReadCd(out _, out senseBuf, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out _); + + if(readcd) + UpdateStatus?.Invoke("Using MMC READ CD command."); + else if(!foundReadCommand) + { + StoppingErrorMessage?.Invoke("Unable to read medium."); + + return results; + } + + start = DateTime.UtcNow; + + if(readcd) + while(true) + { + sense = _dev.ReadCd(out _, out senseBuf, 0, 2352, blocksToRead, MmcSectorTypes.AllTypes, false, + false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, _dev.Timeout, out _); + + if(_dev.Error || sense) + blocksToRead /= 2; + + if(!_dev.Error || + blocksToRead == 1) + break; + } + + if(_dev.Error) + { + StoppingErrorMessage?. + Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + + return results; + } + + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + + InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, currentProfile); + mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); + ibgLog = new IbgLog(_ibgLogPath, currentProfile); + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + + InitProgress?.Invoke(); + + for(ulong i = 0; i < results.Blocks; i += blocksToRead) + { + if(_aborted) + break; + + double cmdDuration; + + if(results.Blocks - i < blocksToRead) + blocksToRead = (uint)(results.Blocks - i); + + if(currentSpeed > results.MaxSpeed && + currentSpeed > 0) + results.MaxSpeed = currentSpeed; + + if(currentSpeed < results.MinSpeed && + currentSpeed > 0) + results.MinSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", + (long)i, (long)results.Blocks); + + if(readcd) + { + sense = _dev.ReadCd(out _, out senseBuf, (uint)i, 2352, blocksToRead, MmcSectorTypes.AllTypes, + false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out cmdDuration); + } + else + { + sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out cmdDuration, out _, out _); + } + + results.ProcessingTime += cmdDuration; + + if(!sense) + { + if(cmdDuration >= 500) + results.F += blocksToRead; + else if(cmdDuration >= 150) + results.E += blocksToRead; + else if(cmdDuration >= 50) + results.D += blocksToRead; + else if(cmdDuration >= 10) + results.C += blocksToRead; + else if(cmdDuration >= 3) + results.B += blocksToRead; + else + results.A += blocksToRead; + + ScanTime?.Invoke(i, cmdDuration); + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + } + else + { + DecodedSense? senseDecoded = null; if(readcd) { - sense = _dev.ReadCd(out _, out senseBuf, (uint)i, 2352, blocksToRead, MmcSectorTypes.AllTypes, - false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, _dev.Timeout, out cmdDuration); - } - else - { - sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out cmdDuration, out _, out _); - } + AaruConsole.DebugWriteLine("Media-Scan", "READ CD error:\n{0}", + Sense.PrettifySense(senseBuf)); - results.ProcessingTime += cmdDuration; + senseDecoded = Sense.Decode(senseBuf); - if(!sense) - { - if(cmdDuration >= 500) - results.F += blocksToRead; - else if(cmdDuration >= 150) - results.E += blocksToRead; - else if(cmdDuration >= 50) - results.D += blocksToRead; - else if(cmdDuration >= 10) - results.C += blocksToRead; - else if(cmdDuration >= 3) - results.B += blocksToRead; - else - results.A += blocksToRead; - - ScanTime?.Invoke(i, cmdDuration); - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - } - else - { - DecodedSense? senseDecoded = null; - - if(readcd) + if(senseDecoded.HasValue) { - AaruConsole.DebugWriteLine("Media-Scan", "READ CD error:\n{0}", - Sense.PrettifySense(senseBuf)); + // TODO: This error happens when changing from track type afaik. Need to solve that more cleanly + // LOGICAL BLOCK ADDRESS OUT OF RANGE + if((senseDecoded.Value.ASC != 0x21 || senseDecoded.Value.ASCQ != 0x00) && - senseDecoded = Sense.Decode(senseBuf); - - if(senseDecoded.HasValue) + // ILLEGAL MODE FOR THIS TRACK (requesting sectors as-is, this is a firmware misconception when audio sectors + // are in a track where subchannel indicates data) + (senseDecoded.Value.ASC != 0x64 || senseDecoded.Value.ASCQ != 0x00)) { - // TODO: This error happens when changing from track type afaik. Need to solve that more cleanly - // LOGICAL BLOCK ADDRESS OUT OF RANGE - if((senseDecoded.Value.ASC != 0x21 || senseDecoded.Value.ASCQ != 0x00) && + results.Errored += blocksToRead; - // ILLEGAL MODE FOR THIS TRACK (requesting sectors as-is, this is a firmware misconception when audio sectors - // are in a track where subchannel indicates data) - (senseDecoded.Value.ASC != 0x64 || senseDecoded.Value.ASCQ != 0x00)) - { - results.Errored += blocksToRead; + for(ulong b = i; b < i + blocksToRead; b++) + results.UnreadableSectors.Add(b); - for(ulong b = i; b < i + blocksToRead; b++) - results.UnreadableSectors.Add(b); + ScanUnreadable?.Invoke(i); + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - ScanUnreadable?.Invoke(i); - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - } + ibgLog.Write(i, 0); } } - - if(!senseDecoded.HasValue) - { - ScanUnreadable?.Invoke(i); - results.Errored += blocksToRead; - - for(ulong b = i; b < i + blocksToRead; b++) - results.UnreadableSectors.Add(b); - - mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); - - ibgLog.Write(i, 0); - } } - sectorSpeedStart += blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - ScanSpeed?.Invoke(i, currentSpeed * 1024); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - end = DateTime.UtcNow; - EndProgress?.Invoke(); - mhddLog.Close(); - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * (end - timeSpeedStart).TotalSeconds); - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if(results.MaxSpeed == double.MinValue) - results.MaxSpeed = currentSpeed; - - // ReSharper disable once CompareOfFloatsByEqualityOperator - if(results.MinSpeed == double.MaxValue) - results.MinSpeed = currentSpeed; - - ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), - _devicePath); - } - else - { - start = DateTime.UtcNow; - - UpdateStatus?.Invoke($"Reading {scsiReader.BlocksToRead} sectors at a time."); - - InitBlockMap?.Invoke(results.Blocks, blockSize, scsiReader.BlocksToRead, currentProfile); - mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, scsiReader.BlocksToRead, false); - ibgLog = new IbgLog(_ibgLogPath, currentProfile); - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; - - InitProgress?.Invoke(); - - for(ulong i = 0; i < results.Blocks; i += scsiReader.BlocksToRead) - { - if(_aborted) - break; - - blocksToRead = scsiReader.BlocksToRead; - - if(results.Blocks - i < blocksToRead) - blocksToRead = (uint)(results.Blocks - i); - - if(currentSpeed > results.MaxSpeed && - currentSpeed > 0) - results.MaxSpeed = currentSpeed; - - if(currentSpeed < results.MinSpeed && - currentSpeed > 0) - results.MinSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", - (long)i, (long)results.Blocks); - - sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration, out _, out _); - results.ProcessingTime += cmdDuration; - - if(!sense && - !_dev.Error) - { - if(cmdDuration >= 500) - results.F += blocksToRead; - else if(cmdDuration >= 150) - results.E += blocksToRead; - else if(cmdDuration >= 50) - results.D += blocksToRead; - else if(cmdDuration >= 10) - results.C += blocksToRead; - else if(cmdDuration >= 3) - results.B += blocksToRead; - else - results.A += blocksToRead; - - ScanTime?.Invoke(i, cmdDuration); - mhddLog.Write(i, cmdDuration); - ibgLog.Write(i, currentSpeed * 1024); - } - - // TODO: Separate errors on kind of errors. - else + if(!senseDecoded.HasValue) { ScanUnreadable?.Invoke(i); results.Errored += blocksToRead; @@ -556,80 +451,184 @@ namespace Aaru.Core.Devices.Scanning results.UnreadableSectors.Add(b); mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + ibgLog.Write(i, 0); } - - sectorSpeedStart += blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - ScanSpeed?.Invoke(i, currentSpeed * 1024); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; } - end = DateTime.UtcNow; - EndProgress?.Invoke(); - mhddLog.Close(); + sectorSpeedStart += blocksToRead; - ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), - _devicePath); + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + ScanSpeed?.Invoke(i, currentSpeed * 1024); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; } - results.SeekMax = double.MinValue; - results.SeekMin = double.MaxValue; - results.SeekTotal = 0; - const int seekTimes = 1000; + end = DateTime.UtcNow; + EndProgress?.Invoke(); + mhddLog.Close(); - var rnd = new Random(); + currentSpeed = sectorSpeedStart * blockSize / (1048576 * (end - timeSpeedStart).TotalSeconds); + + // ReSharper disable once CompareOfFloatsByEqualityOperator + if(results.MaxSpeed == double.MinValue) + results.MaxSpeed = currentSpeed; + + // ReSharper disable once CompareOfFloatsByEqualityOperator + if(results.MinSpeed == double.MaxValue) + results.MinSpeed = currentSpeed; + + ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), + _devicePath); + } + else + { + start = DateTime.UtcNow; + + UpdateStatus?.Invoke($"Reading {scsiReader.BlocksToRead} sectors at a time."); + + InitBlockMap?.Invoke(results.Blocks, blockSize, scsiReader.BlocksToRead, currentProfile); + mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, scsiReader.BlocksToRead, false); + ibgLog = new IbgLog(_ibgLogPath, currentProfile); + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; InitProgress?.Invoke(); - for(int i = 0; i < seekTimes; i++) + for(ulong i = 0; i < results.Blocks; i += scsiReader.BlocksToRead) { - if(_aborted || !_seekTest) + if(_aborted) break; - uint seekPos = (uint)rnd.Next((int)results.Blocks); + blocksToRead = scsiReader.BlocksToRead; - PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t"); + if(results.Blocks - i < blocksToRead) + blocksToRead = (uint)(results.Blocks - i); - double seekCur; + if(currentSpeed > results.MaxSpeed && + currentSpeed > 0) + results.MaxSpeed = currentSpeed; - if(scsiReader.CanSeek) - scsiReader.Seek(seekPos, out seekCur); - else if(readcd) - _dev.ReadCd(out _, out _, seekPos, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, - _dev.Timeout, out seekCur); + if(currentSpeed < results.MinSpeed && + currentSpeed > 0) + results.MinSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", + (long)i, (long)results.Blocks); + + sense = scsiReader.ReadBlocks(out _, i, blocksToRead, out double cmdDuration, out _, out _); + results.ProcessingTime += cmdDuration; + + if(!sense && + !_dev.Error) + { + if(cmdDuration >= 500) + results.F += blocksToRead; + else if(cmdDuration >= 150) + results.E += blocksToRead; + else if(cmdDuration >= 50) + results.D += blocksToRead; + else if(cmdDuration >= 10) + results.C += blocksToRead; + else if(cmdDuration >= 3) + results.B += blocksToRead; + else + results.A += blocksToRead; + + ScanTime?.Invoke(i, cmdDuration); + mhddLog.Write(i, cmdDuration); + ibgLog.Write(i, currentSpeed * 1024); + } + + // TODO: Separate errors on kind of errors. else - scsiReader.ReadBlock(out _, seekPos, out seekCur, out _, out _); + { + ScanUnreadable?.Invoke(i); + results.Errored += blocksToRead; - if(seekCur > results.SeekMax && - seekCur > 0) - results.SeekMax = seekCur; + for(ulong b = i; b < i + blocksToRead; b++) + results.UnreadableSectors.Add(b); - if(seekCur < results.SeekMin && - seekCur > 0) - results.SeekMin = seekCur; + mhddLog.Write(i, cmdDuration < 500 ? 65535 : cmdDuration); + ibgLog.Write(i, 0); + } - results.SeekTotal += seekCur; - GC.Collect(); + sectorSpeedStart += blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + ScanSpeed?.Invoke(i, currentSpeed * 1024); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; } + end = DateTime.UtcNow; EndProgress?.Invoke(); + mhddLog.Close(); - results.ProcessingTime /= 1000; - results.TotalTime = (end - start).TotalSeconds; - results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; - results.SeekTimes = seekTimes; - - return results; + ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), + _devicePath); } + + results.SeekMax = double.MinValue; + results.SeekMin = double.MaxValue; + results.SeekTotal = 0; + const int seekTimes = 1000; + + var rnd = new Random(); + + InitProgress?.Invoke(); + + for(int i = 0; i < seekTimes; i++) + { + if(_aborted || !_seekTest) + break; + + uint seekPos = (uint)rnd.Next((int)results.Blocks); + + PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t"); + + double seekCur; + + if(scsiReader.CanSeek) + scsiReader.Seek(seekPos, out seekCur); + else if(readcd) + _dev.ReadCd(out _, out _, seekPos, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + _dev.Timeout, out seekCur); + else + scsiReader.ReadBlock(out _, seekPos, out seekCur, out _, out _); + + if(seekCur > results.SeekMax && + seekCur > 0) + results.SeekMax = seekCur; + + if(seekCur < results.SeekMin && + seekCur > 0) + results.SeekMin = seekCur; + + results.SeekTotal += seekCur; + GC.Collect(); + } + + EndProgress?.Invoke(); + + results.ProcessingTime /= 1000; + results.TotalTime = (end - start).TotalSeconds; + results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; + results.SeekTimes = seekTimes; + + return results; } } \ No newline at end of file diff --git a/Aaru.Core/Devices/Scanning/ScanResults.cs b/Aaru.Core/Devices/Scanning/ScanResults.cs index 767819753..4093c8671 100644 --- a/Aaru.Core/Devices/Scanning/ScanResults.cs +++ b/Aaru.Core/Devices/Scanning/ScanResults.cs @@ -32,46 +32,45 @@ using System.Collections.Generic; -namespace Aaru.Core.Devices.Scanning +namespace Aaru.Core.Devices.Scanning; + +/// Contains the results of a media scan +public struct ScanResults { - /// Contains the results of a media scan - public struct ScanResults - { - /// Total time spent scanning - public double TotalTime; - /// Total time spent by the device processing commands - public double ProcessingTime; - /// Average scan speed - public double AvgSpeed; - /// Maximum scan speed burst - public double MaxSpeed; - /// Minimum scan speed - public double MinSpeed; - /// Sectors that took less than 3 milliseconds to be processed - public ulong A; - /// Sectors that took less than 10 milliseconds but more than 3 milliseconds to be processed - public ulong B; - /// Sectors that took less than 50 milliseconds but more than 10 milliseconds to be processed - public ulong C; - /// Sectors that took less than 150 milliseconds but more than 50 milliseconds to be processed - public ulong D; - /// Sectors that took less than 500 milliseconds but more than 150 milliseconds to be processed - public ulong E; - /// Sectors that took more than 500 milliseconds to be processed - public ulong F; - /// List of sectors that could not be read - public List UnreadableSectors; - /// Slowest seek - public double SeekMax; - /// Fastest seek - public double SeekMin; - /// Total time spent seeking - public double SeekTotal; - /// How many seeks have been done - public int SeekTimes; - /// How many blocks were scanned - public ulong Blocks; - /// How many blocks could not be read - public ulong Errored; - } + /// Total time spent scanning + public double TotalTime; + /// Total time spent by the device processing commands + public double ProcessingTime; + /// Average scan speed + public double AvgSpeed; + /// Maximum scan speed burst + public double MaxSpeed; + /// Minimum scan speed + public double MinSpeed; + /// Sectors that took less than 3 milliseconds to be processed + public ulong A; + /// Sectors that took less than 10 milliseconds but more than 3 milliseconds to be processed + public ulong B; + /// Sectors that took less than 50 milliseconds but more than 10 milliseconds to be processed + public ulong C; + /// Sectors that took less than 150 milliseconds but more than 50 milliseconds to be processed + public ulong D; + /// Sectors that took less than 500 milliseconds but more than 150 milliseconds to be processed + public ulong E; + /// Sectors that took more than 500 milliseconds to be processed + public ulong F; + /// List of sectors that could not be read + public List UnreadableSectors; + /// Slowest seek + public double SeekMax; + /// Fastest seek + public double SeekMin; + /// Total time spent seeking + public double SeekTotal; + /// How many seeks have been done + public int SeekTimes; + /// How many blocks were scanned + public ulong Blocks; + /// How many blocks could not be read + public ulong Errored; } \ No newline at end of file diff --git a/Aaru.Core/Devices/Scanning/SecureDigital.cs b/Aaru.Core/Devices/Scanning/SecureDigital.cs index 90e4812f0..eff52900b 100644 --- a/Aaru.Core/Devices/Scanning/SecureDigital.cs +++ b/Aaru.Core/Devices/Scanning/SecureDigital.cs @@ -40,311 +40,310 @@ using DeviceType = Aaru.CommonTypes.Enums.DeviceType; // ReSharper disable JoinDeclarationAndInitializer -namespace Aaru.Core.Devices.Scanning +namespace Aaru.Core.Devices.Scanning; + +/// Implements scanning a SecureDigital or MultiMediaCard flash card +public sealed partial class MediaScan { - /// Implements scanning a SecureDigital or MultiMediaCard flash card - public sealed partial class MediaScan + ScanResults SecureDigital() { - ScanResults SecureDigital() + var results = new ScanResults(); + byte[] cmdBuf; + bool sense; + results.Blocks = 0; + const uint timeout = 5; + double duration; + const ushort sdProfile = 0x0001; + ushort blocksToRead = 128; + uint blockSize = 512; + bool byteAddressed = true; + bool supportsCmd23 = false; + + switch(_dev.Type) { - var results = new ScanResults(); - byte[] cmdBuf; - bool sense; - results.Blocks = 0; - const uint timeout = 5; - double duration; - const ushort sdProfile = 0x0001; - ushort blocksToRead = 128; - uint blockSize = 512; - bool byteAddressed = true; - bool supportsCmd23 = false; - - switch(_dev.Type) + case DeviceType.MMC: { - case DeviceType.MMC: + sense = _dev.ReadCsd(out cmdBuf, out _, timeout, out _); + + if(!sense) { - sense = _dev.ReadCsd(out cmdBuf, out _, timeout, out _); + CSD csd = Decoders.MMC.Decoders.DecodeCSD(cmdBuf); + results.Blocks = (ulong)((csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2)); + blockSize = (uint)Math.Pow(2, csd.ReadBlockLength); - if(!sense) + // Found at least since MMC System Specification 3.31 + supportsCmd23 = csd.Version >= 3; + + if(csd.Size == 0xFFF) { - CSD csd = Decoders.MMC.Decoders.DecodeCSD(cmdBuf); - results.Blocks = (ulong)((csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2)); - blockSize = (uint)Math.Pow(2, csd.ReadBlockLength); - - // Found at least since MMC System Specification 3.31 - supportsCmd23 = csd.Version >= 3; - - if(csd.Size == 0xFFF) - { - sense = _dev.ReadExtendedCsd(out cmdBuf, out _, timeout, out _); - - if(!sense) - { - ExtendedCSD ecsd = Decoders.MMC.Decoders.DecodeExtendedCSD(cmdBuf); - results.Blocks = ecsd.SectorCount; - blockSize = (uint)(ecsd.SectorSize == 1 ? 4096 : 512); - - blocksToRead = (ushort)(ecsd.OptimalReadSize * 4096 / blockSize); - - if(blocksToRead == 0) - blocksToRead = 128; - - // Supposing it's high-capacity MMC if it has Extended CSD... - byteAddressed = false; - } - } - } - - break; - } - - case DeviceType.SecureDigital: - { - sense = _dev.ReadCsd(out cmdBuf, out _, timeout, out _); - - if(!sense) - { - Decoders.SecureDigital.CSD csd = Decoders.SecureDigital.Decoders.DecodeCSD(cmdBuf); - - results.Blocks = (ulong)(csd.Structure == 0 - ? (csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2) - : (csd.Size + 1) * 1024); - - blockSize = (uint)Math.Pow(2, csd.ReadBlockLength); - - // Structure >=1 for SDHC/SDXC, so that's block addressed - byteAddressed = csd.Structure == 0; - - if(blockSize != 512) - { - uint ratio = blockSize / 512; - results.Blocks *= ratio; - blockSize = 512; - } - - sense = _dev.ReadScr(out cmdBuf, out _, timeout, out _); + sense = _dev.ReadExtendedCsd(out cmdBuf, out _, timeout, out _); if(!sense) - supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(cmdBuf)?.CommandSupport. - HasFlag(CommandSupport.SetBlockCount) ?? false; + { + ExtendedCSD ecsd = Decoders.MMC.Decoders.DecodeExtendedCSD(cmdBuf); + results.Blocks = ecsd.SectorCount; + blockSize = (uint)(ecsd.SectorSize == 1 ? 4096 : 512); + + blocksToRead = (ushort)(ecsd.OptimalReadSize * 4096 / blockSize); + + if(blocksToRead == 0) + blocksToRead = 128; + + // Supposing it's high-capacity MMC if it has Extended CSD... + byteAddressed = false; + } + } + } + + break; + } + + case DeviceType.SecureDigital: + { + sense = _dev.ReadCsd(out cmdBuf, out _, timeout, out _); + + if(!sense) + { + Decoders.SecureDigital.CSD csd = Decoders.SecureDigital.Decoders.DecodeCSD(cmdBuf); + + results.Blocks = (ulong)(csd.Structure == 0 + ? (csd.Size + 1) * Math.Pow(2, csd.SizeMultiplier + 2) + : (csd.Size + 1) * 1024); + + blockSize = (uint)Math.Pow(2, csd.ReadBlockLength); + + // Structure >=1 for SDHC/SDXC, so that's block addressed + byteAddressed = csd.Structure == 0; + + if(blockSize != 512) + { + uint ratio = blockSize / 512; + results.Blocks *= ratio; + blockSize = 512; } - break; + sense = _dev.ReadScr(out cmdBuf, out _, timeout, out _); + + if(!sense) + supportsCmd23 = Decoders.SecureDigital.Decoders.DecodeSCR(cmdBuf)?.CommandSupport. + HasFlag(CommandSupport.SetBlockCount) ?? false; } + + break; } + } - if(results.Blocks == 0) - { - StoppingErrorMessage?.Invoke("Unable to get device size."); - - return results; - } - - if(supportsCmd23) - { - sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, 1, byteAddressed, timeout, - out duration); - - if(sense || _dev.Error) - { - UpdateStatus?. - Invoke("Environment does not support setting block count, downgrading to OS reading."); - - supportsCmd23 = false; - } - - // Need to restart device, otherwise is it just busy streaming data with no one listening - sense = _dev.ReOpen(); - - if(sense) - { - StoppingErrorMessage?.Invoke($"Error {_dev.LastError} reopening device."); - - return results; - } - } - - if(supportsCmd23) - { - while(true) - { - sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, - timeout, out duration); - - if(sense) - blocksToRead /= 2; - - if(!sense || - blocksToRead == 1) - break; - } - - if(sense) - { - StoppingErrorMessage?. - Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); - - return results; - } - } - - results.A = 0; // <3ms - results.B = 0; // >=3ms, <10ms - results.C = 0; // >=10ms, <50ms - results.D = 0; // >=50ms, <150ms - results.E = 0; // >=150ms, <500ms - results.F = 0; // >=500ms - results.Errored = 0; - DateTime start; - DateTime end; - results.ProcessingTime = 0; - double currentSpeed = 0; - results.MaxSpeed = double.MinValue; - results.MinSpeed = double.MaxValue; - results.UnreadableSectors = new List(); - results.SeekMax = double.MinValue; - results.SeekMin = double.MaxValue; - results.SeekTotal = 0; - const int seekTimes = 100; - - var rnd = new Random(); - - if(supportsCmd23 || blocksToRead == 1) - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); - else if(_useBufferedReads) - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time using OS buffered reads."); - else - UpdateStatus?.Invoke($"Reading {blocksToRead} sectors using sequential single commands."); - - InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, sdProfile); - var mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); - var ibgLog = new IbgLog(_ibgLogPath, sdProfile); - - start = DateTime.UtcNow; - DateTime timeSpeedStart = DateTime.UtcNow; - ulong sectorSpeedStart = 0; - InitProgress?.Invoke(); - - for(ulong i = 0; i < results.Blocks; i += blocksToRead) - { - if(_aborted) - break; - - if(results.Blocks - i < blocksToRead) - blocksToRead = (byte)(results.Blocks - i); - - if(currentSpeed > results.MaxSpeed && - currentSpeed > 0) - results.MaxSpeed = currentSpeed; - - if(currentSpeed < results.MinSpeed && - currentSpeed > 0) - results.MinSpeed = currentSpeed; - - UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, - (long)results.Blocks); - - bool error; - - if(blocksToRead == 1) - error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout, - out duration); - else if(supportsCmd23) - error = _dev.ReadWithBlockCount(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, - timeout, out duration); - else if(_useBufferedReads) - error = _dev.BufferedOsRead(out cmdBuf, (long)(i * blockSize), blockSize * blocksToRead, - out duration); - else - error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, - byteAddressed, timeout, out duration); - - if(!error) - { - if(duration >= 500) - results.F += blocksToRead; - else if(duration >= 150) - results.E += blocksToRead; - else if(duration >= 50) - results.D += blocksToRead; - else if(duration >= 10) - results.C += blocksToRead; - else if(duration >= 3) - results.B += blocksToRead; - else - results.A += blocksToRead; - - ScanTime?.Invoke(i, duration); - mhddLog.Write(i, duration); - ibgLog.Write(i, currentSpeed * 1024); - } - else - { - ScanUnreadable?.Invoke(i); - results.Errored += blocksToRead; - - for(ulong b = i; b < i + blocksToRead; b++) - results.UnreadableSectors.Add(b); - - mhddLog.Write(i, duration < 500 ? 65535 : duration); - - ibgLog.Write(i, 0); - } - - sectorSpeedStart += blocksToRead; - - double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; - - if(elapsed <= 0) - continue; - - currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); - ScanSpeed?.Invoke(i, currentSpeed * 1024); - sectorSpeedStart = 0; - timeSpeedStart = DateTime.UtcNow; - } - - end = DateTime.UtcNow; - EndProgress?.Invoke(); - mhddLog.Close(); - - ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, - blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), - _devicePath); - - InitProgress?.Invoke(); - - for(int i = 0; i < seekTimes; i++) - { - if(_aborted || !_seekTest) - break; - - uint seekPos = (uint)rnd.Next((int)results.Blocks); - - PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t"); - - _dev.ReadSingleBlock(out cmdBuf, out _, seekPos, blockSize, byteAddressed, timeout, out double seekCur); - - if(seekCur > results.SeekMax && - seekCur > 0) - results.SeekMax = seekCur; - - if(seekCur < results.SeekMin && - seekCur > 0) - results.SeekMin = seekCur; - - results.SeekTotal += seekCur; - GC.Collect(); - } - - EndProgress?.Invoke(); - - results.ProcessingTime /= 1000; - results.TotalTime = (end - start).TotalSeconds; - results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; - results.SeekTimes = seekTimes; + if(results.Blocks == 0) + { + StoppingErrorMessage?.Invoke("Unable to get device size."); return results; } + + if(supportsCmd23) + { + sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, 1, byteAddressed, timeout, + out duration); + + if(sense || _dev.Error) + { + UpdateStatus?. + Invoke("Environment does not support setting block count, downgrading to OS reading."); + + supportsCmd23 = false; + } + + // Need to restart device, otherwise is it just busy streaming data with no one listening + sense = _dev.ReOpen(); + + if(sense) + { + StoppingErrorMessage?.Invoke($"Error {_dev.LastError} reopening device."); + + return results; + } + } + + if(supportsCmd23) + { + while(true) + { + sense = _dev.ReadWithBlockCount(out cmdBuf, out _, 0, blockSize, blocksToRead, byteAddressed, + timeout, out duration); + + if(sense) + blocksToRead /= 2; + + if(!sense || + blocksToRead == 1) + break; + } + + if(sense) + { + StoppingErrorMessage?. + Invoke($"Device error {_dev.LastError} trying to guess ideal transfer length."); + + return results; + } + } + + results.A = 0; // <3ms + results.B = 0; // >=3ms, <10ms + results.C = 0; // >=10ms, <50ms + results.D = 0; // >=50ms, <150ms + results.E = 0; // >=150ms, <500ms + results.F = 0; // >=500ms + results.Errored = 0; + DateTime start; + DateTime end; + results.ProcessingTime = 0; + double currentSpeed = 0; + results.MaxSpeed = double.MinValue; + results.MinSpeed = double.MaxValue; + results.UnreadableSectors = new List(); + results.SeekMax = double.MinValue; + results.SeekMin = double.MaxValue; + results.SeekTotal = 0; + const int seekTimes = 100; + + var rnd = new Random(); + + if(supportsCmd23 || blocksToRead == 1) + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time."); + else if(_useBufferedReads) + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors at a time using OS buffered reads."); + else + UpdateStatus?.Invoke($"Reading {blocksToRead} sectors using sequential single commands."); + + InitBlockMap?.Invoke(results.Blocks, blockSize, blocksToRead, sdProfile); + var mhddLog = new MhddLog(_mhddLogPath, _dev, results.Blocks, blockSize, blocksToRead, false); + var ibgLog = new IbgLog(_ibgLogPath, sdProfile); + + start = DateTime.UtcNow; + DateTime timeSpeedStart = DateTime.UtcNow; + ulong sectorSpeedStart = 0; + InitProgress?.Invoke(); + + for(ulong i = 0; i < results.Blocks; i += blocksToRead) + { + if(_aborted) + break; + + if(results.Blocks - i < blocksToRead) + blocksToRead = (byte)(results.Blocks - i); + + if(currentSpeed > results.MaxSpeed && + currentSpeed > 0) + results.MaxSpeed = currentSpeed; + + if(currentSpeed < results.MinSpeed && + currentSpeed > 0) + results.MinSpeed = currentSpeed; + + UpdateProgress?.Invoke($"Reading sector {i} of {results.Blocks} ({currentSpeed:F3} MiB/sec.)", (long)i, + (long)results.Blocks); + + bool error; + + if(blocksToRead == 1) + error = _dev.ReadSingleBlock(out cmdBuf, out _, (uint)i, blockSize, byteAddressed, timeout, + out duration); + else if(supportsCmd23) + error = _dev.ReadWithBlockCount(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, byteAddressed, + timeout, out duration); + else if(_useBufferedReads) + error = _dev.BufferedOsRead(out cmdBuf, (long)(i * blockSize), blockSize * blocksToRead, + out duration); + else + error = _dev.ReadMultipleUsingSingle(out cmdBuf, out _, (uint)i, blockSize, blocksToRead, + byteAddressed, timeout, out duration); + + if(!error) + { + if(duration >= 500) + results.F += blocksToRead; + else if(duration >= 150) + results.E += blocksToRead; + else if(duration >= 50) + results.D += blocksToRead; + else if(duration >= 10) + results.C += blocksToRead; + else if(duration >= 3) + results.B += blocksToRead; + else + results.A += blocksToRead; + + ScanTime?.Invoke(i, duration); + mhddLog.Write(i, duration); + ibgLog.Write(i, currentSpeed * 1024); + } + else + { + ScanUnreadable?.Invoke(i); + results.Errored += blocksToRead; + + for(ulong b = i; b < i + blocksToRead; b++) + results.UnreadableSectors.Add(b); + + mhddLog.Write(i, duration < 500 ? 65535 : duration); + + ibgLog.Write(i, 0); + } + + sectorSpeedStart += blocksToRead; + + double elapsed = (DateTime.UtcNow - timeSpeedStart).TotalSeconds; + + if(elapsed <= 0) + continue; + + currentSpeed = sectorSpeedStart * blockSize / (1048576 * elapsed); + ScanSpeed?.Invoke(i, currentSpeed * 1024); + sectorSpeedStart = 0; + timeSpeedStart = DateTime.UtcNow; + } + + end = DateTime.UtcNow; + EndProgress?.Invoke(); + mhddLog.Close(); + + ibgLog.Close(_dev, results.Blocks, blockSize, (end - start).TotalSeconds, currentSpeed * 1024, + blockSize * (double)(results.Blocks + 1) / 1024 / (results.ProcessingTime / 1000), + _devicePath); + + InitProgress?.Invoke(); + + for(int i = 0; i < seekTimes; i++) + { + if(_aborted || !_seekTest) + break; + + uint seekPos = (uint)rnd.Next((int)results.Blocks); + + PulseProgress?.Invoke($"Seeking to sector {seekPos}...\t\t"); + + _dev.ReadSingleBlock(out cmdBuf, out _, seekPos, blockSize, byteAddressed, timeout, out double seekCur); + + if(seekCur > results.SeekMax && + seekCur > 0) + results.SeekMax = seekCur; + + if(seekCur < results.SeekMin && + seekCur > 0) + results.SeekMin = seekCur; + + results.SeekTotal += seekCur; + GC.Collect(); + } + + EndProgress?.Invoke(); + + results.ProcessingTime /= 1000; + results.TotalTime = (end - start).TotalSeconds; + results.AvgSpeed = blockSize * (double)(results.Blocks + 1) / 1048576 / results.ProcessingTime; + results.SeekTimes = seekTimes; + + return results; } } \ No newline at end of file diff --git a/Aaru.Core/Error.cs b/Aaru.Core/Error.cs index a4a2d9583..d451cbb1e 100644 --- a/Aaru.Core/Error.cs +++ b/Aaru.Core/Error.cs @@ -32,96 +32,95 @@ using Aaru.CommonTypes.Interop; -namespace Aaru.Core +namespace Aaru.Core; + +/// Prints the description of a system error number. +public static class Error { /// Prints the description of a system error number. - public static class Error + /// System error number. + /// Error description. + public static string Print(int errno) { - /// Prints the description of a system error number. - /// System error number. - /// Error description. - public static string Print(int errno) + switch(DetectOS.GetRealPlatformID()) { - switch(DetectOS.GetRealPlatformID()) - { - case PlatformID.Win32S: - case PlatformID.Win32Windows: - case PlatformID.Win32NT: - case PlatformID.WinCE: - case PlatformID.WindowsPhone: - case PlatformID.Xbox: return PrintWin32Error(errno); - case PlatformID.Unix: - case PlatformID.MacOSX: - case PlatformID.iOS: - case PlatformID.Linux: - case PlatformID.Solaris: - case PlatformID.NetBSD: - case PlatformID.OpenBSD: - case PlatformID.FreeBSD: - case PlatformID.DragonFly: - case PlatformID.Android: - case PlatformID.Tizen: - case PlatformID.Hurd: - case PlatformID.Haiku: - case PlatformID.HPUX: - case PlatformID.AIX: - case PlatformID.OS400: - case PlatformID.IRIX: - case PlatformID.Minix: - case PlatformID.QNX: - case PlatformID.SINIX: - case PlatformID.Tru64: - case PlatformID.Ultrix: - case PlatformID.OpenServer: - case PlatformID.UnixWare: - case PlatformID.zOS: return PrintUnixError(errno); - case PlatformID.Wii: return $"Unknown error code {errno}"; - case PlatformID.WiiU: return $"Unknown error code {errno}"; - case PlatformID.PlayStation3: return $"Unknown error code {errno}"; - case PlatformID.PlayStation4: return $"Unknown error code {errno}"; - case PlatformID.NonStop: return $"Unknown error code {errno}"; - case PlatformID.Unknown: return $"Unknown error code {errno}"; - default: return $"Unknown error code {errno}"; - } + case PlatformID.Win32S: + case PlatformID.Win32Windows: + case PlatformID.Win32NT: + case PlatformID.WinCE: + case PlatformID.WindowsPhone: + case PlatformID.Xbox: return PrintWin32Error(errno); + case PlatformID.Unix: + case PlatformID.MacOSX: + case PlatformID.iOS: + case PlatformID.Linux: + case PlatformID.Solaris: + case PlatformID.NetBSD: + case PlatformID.OpenBSD: + case PlatformID.FreeBSD: + case PlatformID.DragonFly: + case PlatformID.Android: + case PlatformID.Tizen: + case PlatformID.Hurd: + case PlatformID.Haiku: + case PlatformID.HPUX: + case PlatformID.AIX: + case PlatformID.OS400: + case PlatformID.IRIX: + case PlatformID.Minix: + case PlatformID.QNX: + case PlatformID.SINIX: + case PlatformID.Tru64: + case PlatformID.Ultrix: + case PlatformID.OpenServer: + case PlatformID.UnixWare: + case PlatformID.zOS: return PrintUnixError(errno); + case PlatformID.Wii: return $"Unknown error code {errno}"; + case PlatformID.WiiU: return $"Unknown error code {errno}"; + case PlatformID.PlayStation3: return $"Unknown error code {errno}"; + case PlatformID.PlayStation4: return $"Unknown error code {errno}"; + case PlatformID.NonStop: return $"Unknown error code {errno}"; + case PlatformID.Unknown: return $"Unknown error code {errno}"; + default: return $"Unknown error code {errno}"; } + } - static string PrintUnixError(int errno) + static string PrintUnixError(int errno) + { + switch(errno) { - switch(errno) - { - case 2: // ENOENT - case 19: // ENODEV - return "The specified device cannot be found."; - case 13: // EACCESS - return "Not enough permissions to open the device."; - case 16: // EBUSY - return "The specified device is in use by another process."; - case 30: // EROFS - return "Cannot open the device in writable mode, as needed by some commands."; - default: return $"Unknown error code {errno}"; - } + case 2: // ENOENT + case 19: // ENODEV + return "The specified device cannot be found."; + case 13: // EACCESS + return "Not enough permissions to open the device."; + case 16: // EBUSY + return "The specified device is in use by another process."; + case 30: // EROFS + return "Cannot open the device in writable mode, as needed by some commands."; + default: return $"Unknown error code {errno}"; } + } - static string PrintWin32Error(int errno) + static string PrintWin32Error(int errno) + { + switch(errno) { - switch(errno) - { - case 2: // ERROR_FILE_NOT_FOUND - case 3: // ERROR_PATH_NOT_FOUND - return "The specified device cannot be found."; - case 5: // ERROR_ACCESS_DENIED - return "Not enough permissions to open the device."; - case 19: // ERROR_WRITE_PROTECT - return "Cannot open the device in writable mode, as needed by some commands."; - case 32: // ERROR_SHARING_VIOLATION - case 33: // ERROR_LOCK_VIOLATION - case 108: // ERROR_DRIVE_LOCKED - case 170: // ERROR_BUSY - return "The specified device is in use by another process."; - case 130: // ERROR_DIRECT_ACCESS_HANDLE - return "Tried to open a file instead of a device."; - default: return $"Unknown error code {errno}"; - } + case 2: // ERROR_FILE_NOT_FOUND + case 3: // ERROR_PATH_NOT_FOUND + return "The specified device cannot be found."; + case 5: // ERROR_ACCESS_DENIED + return "Not enough permissions to open the device."; + case 19: // ERROR_WRITE_PROTECT + return "Cannot open the device in writable mode, as needed by some commands."; + case 32: // ERROR_SHARING_VIOLATION + case 33: // ERROR_LOCK_VIOLATION + case 108: // ERROR_DRIVE_LOCKED + case 170: // ERROR_BUSY + return "The specified device is in use by another process."; + case 130: // ERROR_DIRECT_ACCESS_HANDLE + return "Tried to open a file instead of a device."; + default: return $"Unknown error code {errno}"; } } } \ No newline at end of file diff --git a/Aaru.Core/Filesystems.cs b/Aaru.Core/Filesystems.cs index fa92c84ff..6287fa171 100644 --- a/Aaru.Core/Filesystems.cs +++ b/Aaru.Core/Filesystems.cs @@ -35,26 +35,25 @@ using System.Linq; using Aaru.CommonTypes; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Core -{ - /// Core filesystem operations - public static class Filesystems - { - /// - /// Traverses all known filesystems and outputs a list of all that recognized what is in the specified image and - /// partition - /// - /// Media image - /// List of plugins recognizing the filesystem - /// Partition - /// Gets plugin GUID - public static void Identify(IMediaImage imagePlugin, out List idPlugins, Partition partition, - bool getGuid = false) - { - PluginBase plugins = GetPluginBase.Instance; +namespace Aaru.Core; - idPlugins = (from plugin in plugins.PluginsList.Values where plugin.Identify(imagePlugin, partition) - select getGuid ? plugin.Id.ToString() : plugin.Name.ToLower()).ToList(); - } +/// Core filesystem operations +public static class Filesystems +{ + /// + /// Traverses all known filesystems and outputs a list of all that recognized what is in the specified image and + /// partition + /// + /// Media image + /// List of plugins recognizing the filesystem + /// Partition + /// Gets plugin GUID + public static void Identify(IMediaImage imagePlugin, out List idPlugins, Partition partition, + bool getGuid = false) + { + PluginBase plugins = GetPluginBase.Instance; + + idPlugins = (from plugin in plugins.PluginsList.Values where plugin.Identify(imagePlugin, partition) + select getGuid ? plugin.Id.ToString() : plugin.Name.ToLower()).ToList(); } } \ No newline at end of file diff --git a/Aaru.Core/GetPluginBase.cs b/Aaru.Core/GetPluginBase.cs index 69d2f2f27..391ddc82d 100644 --- a/Aaru.Core/GetPluginBase.cs +++ b/Aaru.Core/GetPluginBase.cs @@ -34,34 +34,33 @@ using Aaru.Checksums; using Aaru.CommonTypes; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Core +namespace Aaru.Core; + +/// Plugin base operations +public static class GetPluginBase { - /// Plugin base operations - public static class GetPluginBase + /// Gets an instance with all the known plugins + public static PluginBase Instance { - /// Gets an instance with all the known plugins - public static PluginBase Instance + get { - get - { - var instance = new PluginBase(); + var instance = new PluginBase(); - IPluginRegister checksumRegister = new Register(); - IPluginRegister imagesRegister = new DiscImages.Register(); - IPluginRegister filesystemsRegister = new Aaru.Filesystems.Register(); - IPluginRegister filtersRegister = new Filters.Register(); - IPluginRegister partitionsRegister = new Aaru.Partitions.Register(); - IPluginRegister archiveRegister = new Archives.Register(); + IPluginRegister checksumRegister = new Register(); + IPluginRegister imagesRegister = new DiscImages.Register(); + IPluginRegister filesystemsRegister = new Aaru.Filesystems.Register(); + IPluginRegister filtersRegister = new Filters.Register(); + IPluginRegister partitionsRegister = new Aaru.Partitions.Register(); + IPluginRegister archiveRegister = new Archives.Register(); - instance.AddPlugins(checksumRegister); - instance.AddPlugins(imagesRegister); - instance.AddPlugins(filesystemsRegister); - instance.AddPlugins(filtersRegister); - instance.AddPlugins(partitionsRegister); - instance.AddPlugins(archiveRegister); + instance.AddPlugins(checksumRegister); + instance.AddPlugins(imagesRegister); + instance.AddPlugins(filesystemsRegister); + instance.AddPlugins(filtersRegister); + instance.AddPlugins(partitionsRegister); + instance.AddPlugins(archiveRegister); - return instance; - } + return instance; } } } \ No newline at end of file diff --git a/Aaru.Core/Logging/DumpLog.cs b/Aaru.Core/Logging/DumpLog.cs index 9613084b9..27ca8c17a 100644 --- a/Aaru.Core/Logging/DumpLog.cs +++ b/Aaru.Core/Logging/DumpLog.cs @@ -39,162 +39,161 @@ using Aaru.Devices; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; using Version = Aaru.CommonTypes.Interop.Version; -namespace Aaru.Core.Logging +namespace Aaru.Core.Logging; + +/// Creates a dump log +public sealed class DumpLog { - /// Creates a dump log - public sealed class DumpLog + readonly StreamWriter _logSw; + + /// Initializes the dump log + /// Output log file + /// Device + /// Disable saving paths or serial numbers in log + public DumpLog(string outputFile, Device dev, bool @private) { - readonly StreamWriter _logSw; + if(string.IsNullOrEmpty(outputFile)) + return; - /// Initializes the dump log - /// Output log file - /// Device - /// Disable saving paths or serial numbers in log - public DumpLog(string outputFile, Device dev, bool @private) + _logSw = new StreamWriter(outputFile, true); + + _logSw.WriteLine("Start logging at {0}", DateTime.Now); + + PlatformID platId = DetectOS.GetRealPlatformID(); + string platVer = DetectOS.GetVersion(); + + var assemblyVersion = + Attribute.GetCustomAttribute(typeof(DumpLog).Assembly, typeof(AssemblyInformationalVersionAttribute)) as + AssemblyInformationalVersionAttribute; + + _logSw.WriteLine("################# System information #################"); + + _logSw.WriteLine("{0} {1} ({2}-bit)", DetectOS.GetPlatformName(platId, platVer), platVer, + Environment.Is64BitOperatingSystem ? 64 : 32); + + if(DetectOS.IsMono) + _logSw.WriteLine("Mono {0}", Version.GetMonoVersion()); + else if(DetectOS.IsNetCore) + _logSw.WriteLine(".NET Core {0}", Version.GetNetCoreVersion()); + else + _logSw.WriteLine(RuntimeInformation.FrameworkDescription); + + _logSw.WriteLine(); + + _logSw.WriteLine("################# Program information ################"); + _logSw.WriteLine("Aaru {0}", assemblyVersion?.InformationalVersion); + _logSw.WriteLine("Running in {0}-bit", Environment.Is64BitProcess ? 64 : 32); + _logSw.WriteLine("Running as superuser: {0}", DetectOS.IsAdmin ? "Yes" : "No"); + #if DEBUG + _logSw.WriteLine("DEBUG version"); + #endif + if(@private) { - if(string.IsNullOrEmpty(outputFile)) - return; + string[] args = Environment.GetCommandLineArgs(); - _logSw = new StreamWriter(outputFile, true); - - _logSw.WriteLine("Start logging at {0}", DateTime.Now); - - PlatformID platId = DetectOS.GetRealPlatformID(); - string platVer = DetectOS.GetVersion(); - - var assemblyVersion = - Attribute.GetCustomAttribute(typeof(DumpLog).Assembly, typeof(AssemblyInformationalVersionAttribute)) as - AssemblyInformationalVersionAttribute; - - _logSw.WriteLine("################# System information #################"); - - _logSw.WriteLine("{0} {1} ({2}-bit)", DetectOS.GetPlatformName(platId, platVer), platVer, - Environment.Is64BitOperatingSystem ? 64 : 32); - - if(DetectOS.IsMono) - _logSw.WriteLine("Mono {0}", Version.GetMonoVersion()); - else if(DetectOS.IsNetCore) - _logSw.WriteLine(".NET Core {0}", Version.GetNetCoreVersion()); - else - _logSw.WriteLine(RuntimeInformation.FrameworkDescription); - - _logSw.WriteLine(); - - _logSw.WriteLine("################# Program information ################"); - _logSw.WriteLine("Aaru {0}", assemblyVersion?.InformationalVersion); - _logSw.WriteLine("Running in {0}-bit", Environment.Is64BitProcess ? 64 : 32); - _logSw.WriteLine("Running as superuser: {0}", DetectOS.IsAdmin ? "Yes" : "No"); - #if DEBUG - _logSw.WriteLine("DEBUG version"); - #endif - if(@private) + for(int i = 0; i < args.Length; i++) { - string[] args = Environment.GetCommandLineArgs(); + if(args[i].StartsWith("/dev", StringComparison.OrdinalIgnoreCase) || + args[i].StartsWith("aaru://", StringComparison.OrdinalIgnoreCase)) + continue; - for(int i = 0; i < args.Length; i++) + try { - if(args[i].StartsWith("/dev", StringComparison.OrdinalIgnoreCase) || - args[i].StartsWith("aaru://", StringComparison.OrdinalIgnoreCase)) - continue; - - try - { - args[i] = Path.GetFileName(args[i]); - } - catch - { - // Do nothing - } + args[i] = Path.GetFileName(args[i]); + } + catch + { + // Do nothing } - - _logSw.WriteLine("Command line: {0}", string.Join(" ", args)); - } - else - _logSw.WriteLine("Command line: {0}", Environment.CommandLine); - - _logSw.WriteLine(); - - if(dev.IsRemote) - { - _logSw.WriteLine("################# Remote information #################"); - _logSw.WriteLine("Server: {0}", dev.RemoteApplication); - _logSw.WriteLine("Version: {0}", dev.RemoteVersion); - - _logSw.WriteLine("Operating system: {0} {1}", dev.RemoteOperatingSystem, - dev.RemoteOperatingSystemVersion); - - _logSw.WriteLine("Architecture: {0}", dev.RemoteArchitecture); - _logSw.WriteLine("Protocol version: {0}", dev.RemoteProtocolVersion); - _logSw.WriteLine("Running as superuser: {0}", dev.IsRemoteAdmin ? "Yes" : "No"); - _logSw.WriteLine("######################################################"); } - _logSw.WriteLine("################# Device information #################"); - _logSw.WriteLine("Manufacturer: {0}", dev.Manufacturer); - _logSw.WriteLine("Model: {0}", dev.Model); - _logSw.WriteLine("Firmware revision: {0}", dev.FirmwareRevision); + _logSw.WriteLine("Command line: {0}", string.Join(" ", args)); + } + else + _logSw.WriteLine("Command line: {0}", Environment.CommandLine); + + _logSw.WriteLine(); + + if(dev.IsRemote) + { + _logSw.WriteLine("################# Remote information #################"); + _logSw.WriteLine("Server: {0}", dev.RemoteApplication); + _logSw.WriteLine("Version: {0}", dev.RemoteVersion); + + _logSw.WriteLine("Operating system: {0} {1}", dev.RemoteOperatingSystem, + dev.RemoteOperatingSystemVersion); + + _logSw.WriteLine("Architecture: {0}", dev.RemoteArchitecture); + _logSw.WriteLine("Protocol version: {0}", dev.RemoteProtocolVersion); + _logSw.WriteLine("Running as superuser: {0}", dev.IsRemoteAdmin ? "Yes" : "No"); + _logSw.WriteLine("######################################################"); + } + + _logSw.WriteLine("################# Device information #################"); + _logSw.WriteLine("Manufacturer: {0}", dev.Manufacturer); + _logSw.WriteLine("Model: {0}", dev.Model); + _logSw.WriteLine("Firmware revision: {0}", dev.FirmwareRevision); + + if(!@private) + _logSw.WriteLine("Serial number: {0}", dev.Serial); + + _logSw.WriteLine("Removable device: {0}", dev.IsRemovable); + _logSw.WriteLine("Device type: {0}", dev.Type); + _logSw.WriteLine("CompactFlash device: {0}", dev.IsCompactFlash); + _logSw.WriteLine("PCMCIA device: {0}", dev.IsPcmcia); + _logSw.WriteLine("USB device: {0}", dev.IsUsb); + + if(dev.IsUsb) + { + _logSw.WriteLine("USB manufacturer: {0}", dev.UsbManufacturerString); + _logSw.WriteLine("USB product: {0}", dev.UsbProductString); if(!@private) - _logSw.WriteLine("Serial number: {0}", dev.Serial); + _logSw.WriteLine("USB serial: {0}", dev.UsbSerialString); - _logSw.WriteLine("Removable device: {0}", dev.IsRemovable); - _logSw.WriteLine("Device type: {0}", dev.Type); - _logSw.WriteLine("CompactFlash device: {0}", dev.IsCompactFlash); - _logSw.WriteLine("PCMCIA device: {0}", dev.IsPcmcia); - _logSw.WriteLine("USB device: {0}", dev.IsUsb); - - if(dev.IsUsb) - { - _logSw.WriteLine("USB manufacturer: {0}", dev.UsbManufacturerString); - _logSw.WriteLine("USB product: {0}", dev.UsbProductString); - - if(!@private) - _logSw.WriteLine("USB serial: {0}", dev.UsbSerialString); - - _logSw.WriteLine("USB vendor ID: {0:X4}h", dev.UsbVendorId); - _logSw.WriteLine("USB product ID: {0:X4}h", dev.UsbProductId); - } - - _logSw.WriteLine("FireWire device: {0}", dev.IsFireWire); - - if(dev.IsFireWire) - { - _logSw.WriteLine("FireWire vendor: {0}", dev.FireWireVendorName); - _logSw.WriteLine("FireWire model: {0}", dev.FireWireModelName); - - if(!@private) - _logSw.WriteLine("FireWire GUID: 0x{0:X16}", dev.FireWireGuid); - - _logSw.WriteLine("FireWire vendor ID: 0x{0:X8}", dev.FireWireVendor); - _logSw.WriteLine("FireWire product ID: 0x{0:X8}", dev.FireWireModel); - } - - _logSw.WriteLine("######################################################"); - - _logSw.WriteLine(); - _logSw.WriteLine("################ Dumping progress log ################"); - _logSw.Flush(); + _logSw.WriteLine("USB vendor ID: {0:X4}h", dev.UsbVendorId); + _logSw.WriteLine("USB product ID: {0:X4}h", dev.UsbProductId); } - /// Adds a new line to the dump log - /// Format string - /// Arguments - public void WriteLine(string format, params object[] args) + _logSw.WriteLine("FireWire device: {0}", dev.IsFireWire); + + if(dev.IsFireWire) { - if(_logSw == null) - return; + _logSw.WriteLine("FireWire vendor: {0}", dev.FireWireVendorName); + _logSw.WriteLine("FireWire model: {0}", dev.FireWireModelName); - string text = string.Format(format, args); - _logSw.WriteLine("{0:s} {1}", DateTime.Now, text); - _logSw.Flush(); + if(!@private) + _logSw.WriteLine("FireWire GUID: 0x{0:X16}", dev.FireWireGuid); + + _logSw.WriteLine("FireWire vendor ID: 0x{0:X8}", dev.FireWireVendor); + _logSw.WriteLine("FireWire product ID: 0x{0:X8}", dev.FireWireModel); } - /// Finishes and closes the dump log - public void Close() - { - _logSw?.WriteLine("######################################################"); - _logSw?.WriteLine("End logging at {0}", DateTime.Now); - _logSw?.Close(); - } + _logSw.WriteLine("######################################################"); + + _logSw.WriteLine(); + _logSw.WriteLine("################ Dumping progress log ################"); + _logSw.Flush(); + } + + /// Adds a new line to the dump log + /// Format string + /// Arguments + public void WriteLine(string format, params object[] args) + { + if(_logSw == null) + return; + + string text = string.Format(format, args); + _logSw.WriteLine("{0:s} {1}", DateTime.Now, text); + _logSw.Flush(); + } + + /// Finishes and closes the dump log + public void Close() + { + _logSw?.WriteLine("######################################################"); + _logSw?.WriteLine("End logging at {0}", DateTime.Now); + _logSw?.Close(); } } \ No newline at end of file diff --git a/Aaru.Core/Logging/ErrorLog.cs b/Aaru.Core/Logging/ErrorLog.cs index 4b1e904c3..14272e81b 100644 --- a/Aaru.Core/Logging/ErrorLog.cs +++ b/Aaru.Core/Logging/ErrorLog.cs @@ -33,506 +33,505 @@ using System.Linq; using Aaru.Decoders.ATA; using Aaru.Decoders.SCSI; -namespace Aaru.Core.Logging +namespace Aaru.Core.Logging; + +/// Logs errors +public sealed class ErrorLog { - /// Logs errors - public sealed class ErrorLog + readonly StreamWriter _logSw; + + /// Initializes the error log + /// Output log file + public ErrorLog(string outputFile) { - readonly StreamWriter _logSw; + if(string.IsNullOrEmpty(outputFile)) + return; - /// Initializes the error log - /// Output log file - public ErrorLog(string outputFile) + _logSw = new StreamWriter(outputFile, true); + + _logSw.WriteLine("Start error logging at {0}", DateTime.Now); + _logSw.WriteLine("######################################################"); + _logSw.Flush(); + } + + /// Finishes and closes the error log + public void Close() + { + _logSw.WriteLine("######################################################"); + _logSw.WriteLine("End logging at {0}", DateTime.Now); + _logSw.Close(); + } + + /// Register an ATA error after sending a CHS command + /// Command + /// true if operating system returned an error status instead of the device + /// Operating system error number + /// Error registers + public void WriteLine(string command, bool osError, int errno, AtaErrorRegistersChs registers) + { + if(osError) { - if(string.IsNullOrEmpty(outputFile)) - return; - - _logSw = new StreamWriter(outputFile, true); - - _logSw.WriteLine("Start error logging at {0}", DateTime.Now); - _logSw.WriteLine("######################################################"); + _logSw.WriteLine("ATA command {0} operating system error: {1}.", command, errno); _logSw.Flush(); } - - /// Finishes and closes the error log - public void Close() + else { - _logSw.WriteLine("######################################################"); - _logSw.WriteLine("End logging at {0}", DateTime.Now); - _logSw.Close(); - } + List error = new List(); + List status = new List(); - /// Register an ATA error after sending a CHS command - /// Command - /// true if operating system returned an error status instead of the device - /// Operating system error number - /// Error registers - public void WriteLine(string command, bool osError, int errno, AtaErrorRegistersChs registers) - { - if(osError) - { - _logSw.WriteLine("ATA command {0} operating system error: {1}.", command, errno); - _logSw.Flush(); - } - else - { - List error = new List(); - List status = new List(); + if((registers.Status & 0x01) == 0x01) + status.Add("ERR"); - if((registers.Status & 0x01) == 0x01) - status.Add("ERR"); + if((registers.Status & 0x02) == 0x02) + status.Add("IDX"); - if((registers.Status & 0x02) == 0x02) - status.Add("IDX"); + if((registers.Status & 0x04) == 0x04) + status.Add("CORR"); - if((registers.Status & 0x04) == 0x04) - status.Add("CORR"); + if((registers.Status & 0x08) == 0x08) + status.Add("DRQ"); - if((registers.Status & 0x08) == 0x08) - status.Add("DRQ"); + if((registers.Status & 0x10) == 0x10) + status.Add("SRV"); - if((registers.Status & 0x10) == 0x10) - status.Add("SRV"); + if((registers.Status & 0x20) == 0x20) + status.Add("DF"); - if((registers.Status & 0x20) == 0x20) - status.Add("DF"); + if((registers.Status & 0x40) == 0x40) + status.Add("RDY"); - if((registers.Status & 0x40) == 0x40) - status.Add("RDY"); + if((registers.Status & 0x80) == 0x80) + status.Add("BSY"); - if((registers.Status & 0x80) == 0x80) - status.Add("BSY"); + if((registers.Error & 0x01) == 0x01) + error.Add("AMNF"); - if((registers.Error & 0x01) == 0x01) - error.Add("AMNF"); + if((registers.Error & 0x02) == 0x02) + error.Add("T0NF"); - if((registers.Error & 0x02) == 0x02) - error.Add("T0NF"); + if((registers.Error & 0x04) == 0x04) + error.Add("ABRT"); - if((registers.Error & 0x04) == 0x04) - error.Add("ABRT"); + if((registers.Error & 0x08) == 0x08) + error.Add("MCR"); - if((registers.Error & 0x08) == 0x08) - error.Add("MCR"); + if((registers.Error & 0x10) == 0x10) + error.Add("IDNF"); - if((registers.Error & 0x10) == 0x10) - error.Add("IDNF"); + if((registers.Error & 0x20) == 0x20) + error.Add("MC"); - if((registers.Error & 0x20) == 0x20) - error.Add("MC"); + if((registers.Error & 0x40) == 0x40) + error.Add("UNC"); - if((registers.Error & 0x40) == 0x40) - error.Add("UNC"); + if((registers.Error & 0x80) == 0x80) + error.Add("BBK"); - if((registers.Error & 0x80) == 0x80) - error.Add("BBK"); - - _logSw.WriteLine("ATA command {0} error: status = {1}, error = {2}.", command, string.Join(' ', status), - string.Join(' ', error)); - - _logSw.Flush(); - } - } - - /// Register an ATA error after trying to read using CHS commands - /// Cylinder - /// Head - /// Sector - /// true if operating system returned an error status instead of the device - /// Operating system error number - /// Error registers - public void WriteLine(ushort cylinder, byte head, byte sector, bool osError, int errno, - AtaErrorRegistersChs registers) - { - if(osError) - { - _logSw.WriteLine("ATA reading C/H/S {0}/{1}/{2} operating system error: {3}.", cylinder, head, sector, - errno); - - _logSw.Flush(); - } - else - { - List error = new List(); - List status = new List(); - - if((registers.Status & 0x01) == 0x01) - status.Add("ERR"); - - if((registers.Status & 0x02) == 0x02) - status.Add("IDX"); - - if((registers.Status & 0x04) == 0x04) - status.Add("CORR"); - - if((registers.Status & 0x08) == 0x08) - status.Add("DRQ"); - - if((registers.Status & 0x10) == 0x10) - status.Add("SRV"); - - if((registers.Status & 0x20) == 0x20) - status.Add("DF"); - - if((registers.Status & 0x40) == 0x40) - status.Add("RDY"); - - if((registers.Status & 0x80) == 0x80) - status.Add("BSY"); - - if((registers.Error & 0x01) == 0x01) - error.Add("AMNF"); - - if((registers.Error & 0x02) == 0x02) - error.Add("T0NF"); - - if((registers.Error & 0x04) == 0x04) - error.Add("ABRT"); - - if((registers.Error & 0x08) == 0x08) - error.Add("MCR"); - - if((registers.Error & 0x10) == 0x10) - error.Add("IDNF"); - - if((registers.Error & 0x20) == 0x20) - error.Add("MC"); - - if((registers.Error & 0x40) == 0x40) - error.Add("UNC"); - - if((registers.Error & 0x80) == 0x80) - error.Add("BBK"); - - _logSw.WriteLine("ATA reading C/H/S {0}/{1}/{2} error: status = {3}, error = {4}.", cylinder, head, - sector, string.Join(' ', status), string.Join(' ', error)); - - _logSw.Flush(); - } - } - - /// Register an ATA error after trying to read using 28-bit LBA commands - /// Starting block - /// true if operating system returned an error status instead of the device - /// Operating system error number - /// Error registers - public void WriteLine(ulong block, bool osError, int errno, AtaErrorRegistersLba28 registers) - { - if(osError) - { - _logSw.WriteLine("ATA reading LBA {0} operating system error: {1}.", block, errno); - _logSw.Flush(); - } - else - { - List error = new List(); - List status = new List(); - - if((registers.Status & 0x01) == 0x01) - status.Add("ERR"); - - if((registers.Status & 0x02) == 0x02) - status.Add("IDX"); - - if((registers.Status & 0x04) == 0x04) - status.Add("CORR"); - - if((registers.Status & 0x08) == 0x08) - status.Add("DRQ"); - - if((registers.Status & 0x10) == 0x10) - status.Add("SRV"); - - if((registers.Status & 0x20) == 0x20) - status.Add("DF"); - - if((registers.Status & 0x40) == 0x40) - status.Add("RDY"); - - if((registers.Status & 0x80) == 0x80) - status.Add("BSY"); - - if((registers.Error & 0x01) == 0x01) - error.Add("AMNF"); - - if((registers.Error & 0x02) == 0x02) - error.Add("T0NF"); - - if((registers.Error & 0x04) == 0x04) - error.Add("ABRT"); - - if((registers.Error & 0x08) == 0x08) - error.Add("MCR"); - - if((registers.Error & 0x10) == 0x10) - error.Add("IDNF"); - - if((registers.Error & 0x20) == 0x20) - error.Add("MC"); - - if((registers.Error & 0x40) == 0x40) - error.Add("UNC"); - - if((registers.Error & 0x80) == 0x80) - error.Add("BBK"); - - _logSw.WriteLine("ATA reading LBA {0} error: status = {1}, error = {2}.", block, - string.Join(' ', status), string.Join(' ', error)); - - _logSw.Flush(); - } - } - - /// Register an ATA error after trying to read using 48-bit LBA commands - /// Starting block - /// true if operating system returned an error status instead of the device - /// Operating system error number - /// Error registers - public void WriteLine(ulong block, bool osError, int errno, AtaErrorRegistersLba48 registers) - { - if(osError) - { - _logSw.WriteLine("ATA reading LBA {0} operating system error: {1}.", block, errno); - _logSw.Flush(); - } - else - { - List error = new List(); - List status = new List(); - - if((registers.Status & 0x01) == 0x01) - status.Add("ERR"); - - if((registers.Status & 0x02) == 0x02) - status.Add("IDX"); - - if((registers.Status & 0x04) == 0x04) - status.Add("CORR"); - - if((registers.Status & 0x08) == 0x08) - status.Add("DRQ"); - - if((registers.Status & 0x10) == 0x10) - status.Add("SRV"); - - if((registers.Status & 0x20) == 0x20) - status.Add("DF"); - - if((registers.Status & 0x40) == 0x40) - status.Add("RDY"); - - if((registers.Status & 0x80) == 0x80) - status.Add("BSY"); - - if((registers.Error & 0x01) == 0x01) - error.Add("AMNF"); - - if((registers.Error & 0x02) == 0x02) - error.Add("T0NF"); - - if((registers.Error & 0x04) == 0x04) - error.Add("ABRT"); - - if((registers.Error & 0x08) == 0x08) - error.Add("MCR"); - - if((registers.Error & 0x10) == 0x10) - error.Add("IDNF"); - - if((registers.Error & 0x20) == 0x20) - error.Add("MC"); - - if((registers.Error & 0x40) == 0x40) - error.Add("UNC"); - - if((registers.Error & 0x80) == 0x80) - error.Add("BBK"); - - _logSw.WriteLine("ATA reading LBA {0} error: status = {1}, error = {2}.", block, - string.Join(' ', status), string.Join(' ', error)); - - _logSw.Flush(); - } - } - - /// Register a SCSI error after sending a command - /// Command - /// true if operating system returned an error status instead of the device - /// Operating system error number - /// REQUEST SENSE response buffer - public void WriteLine(string command, bool osError, int errno, byte[] senseBuffer) - { - if(osError) - { - _logSw.WriteLine("SCSI command {0} operating system error: {1}.", command, errno); - _logSw.Flush(); - - return; - } - - DecodedSense? decodedSense = Sense.Decode(senseBuffer); - string prettySense = Sense.PrettifySense(senseBuffer); - string hexSense = string.Join(' ', senseBuffer.Select(b => $"{b:X2}")); - - if(decodedSense.HasValue) - { - if(prettySense != null) - { - if(prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) - prettySense = prettySense.Substring(12); - - if(prettySense.EndsWith('\n')) - prettySense = prettySense.Substring(0, prettySense.Length - 1); - - prettySense = prettySense.Replace("\n", " - "); - - _logSw.WriteLine("SCSI command {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}, {5}.", command, - decodedSense.Value.SenseKey, decodedSense.Value.ASC, decodedSense.Value.ASCQ, - hexSense, prettySense); - } - else - { - _logSw.WriteLine("SCSI command {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", command, - decodedSense.Value.SenseKey, decodedSense.Value.ASC, decodedSense.Value.ASCQ, - hexSense); - } - } - else - { - if(prettySense != null) - { - if(prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) - prettySense = prettySense.Substring(12); - - if(prettySense.EndsWith('\n')) - prettySense = prettySense.Substring(0, prettySense.Length - 1); - - prettySense = prettySense.Replace("\n", " - "); - - _logSw.WriteLine("SCSI command {0} error: {1}, {2}.", command, hexSense, prettySense); - } - else - { - _logSw.WriteLine("SCSI command {0} error: {1}", command, hexSense); - } - } + _logSw.WriteLine("ATA command {0} error: status = {1}, error = {2}.", command, string.Join(' ', status), + string.Join(' ', error)); _logSw.Flush(); } - - /// Register an SCSI error after trying to read - /// Starting block - /// true if operating system returned an error status instead of the device - /// Operating system error number - /// REQUEST SENSE response buffer - public void WriteLine(ulong block, bool osError, int errno, byte[] senseBuffer) - { - if(osError) - { - _logSw.WriteLine("SCSI reading LBA {0} operating system error: {1}.", block, errno); - _logSw.Flush(); - - if(senseBuffer is null || - senseBuffer.Length == 0 || - senseBuffer.All(s => s == 0)) - return; - } - - DecodedSense? decodedSense = Sense.Decode(senseBuffer); - string prettySense = Sense.PrettifySense(senseBuffer); - string hexSense = string.Join(' ', senseBuffer.Select(b => $"{b:X2}")); - - if(decodedSense.HasValue) - { - if(prettySense != null) - { - if(prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) - prettySense = prettySense.Substring(12); - - if(prettySense.EndsWith('\n')) - prettySense = prettySense.Substring(0, prettySense.Length - 1); - - prettySense = prettySense.Replace("\n", " - "); - - _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}, {5}.", block, - decodedSense.Value.SenseKey, decodedSense.Value.ASC, decodedSense.Value.ASCQ, - hexSense, prettySense); - } - else - { - _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", block, - decodedSense.Value.SenseKey, decodedSense.Value.ASC, decodedSense.Value.ASCQ, - hexSense); - } - } - else - { - if(prettySense != null) - { - if(prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) - prettySense = prettySense.Substring(12); - - if(prettySense.EndsWith('\n')) - prettySense = prettySense.Substring(0, prettySense.Length - 1); - - prettySense = prettySense.Replace("\n", " - "); - - _logSw.WriteLine("SCSI reading LBA {0} error: {1}, {2}.", block, hexSense, prettySense); - } - else - { - _logSw.WriteLine("SCSI reading LBA {0} error: {1}", block, hexSense); - } - } - - _logSw.Flush(); - } - - /// Register a SecureDigital / MultiMediaCard error after sending a command - /// Command - /// true if operating system returned an error status instead of the device - /// Operating system error number - /// Response - public void WriteLine(string command, bool osError, int errno, uint[] response) - { - if(osError) - { - _logSw.WriteLine("SD/MMC command {0} operating system error: {1}.", command, errno); - _logSw.Flush(); - - return; - } - - // TODO: Decode response - _logSw.WriteLine("SD/MMC command {0} error: {1}", command, - string.Join(" - ", response.Select(r => $"0x{r:X8}"))); - - _logSw.Flush(); - } - - /// Register a SecureDigital / MultiMediaCard error after trying to read - /// Starting block - /// true if operating system returned an error status instead of the device - /// Operating system error number - /// Byte addressed - /// Response - public void WriteLine(ulong block, bool osError, int errno, bool byteAddressed, uint[] response) - - { - if(osError) - { - _logSw.WriteLine("SD/MMC reading LBA {0} ({1}-addressed) operating system error: {2}.", block, - byteAddressed ? "byte" : "block", errno); - - _logSw.Flush(); - - return; - } - - _logSw.WriteLine("SD/MMC reading LBA {0} ({1}-addressed) error: {2}", block, - byteAddressed ? "byte" : "block", string.Join(" - ", response.Select(r => $"0x{r:X8}"))); - - throw new NotImplementedException(); - } + } + + /// Register an ATA error after trying to read using CHS commands + /// Cylinder + /// Head + /// Sector + /// true if operating system returned an error status instead of the device + /// Operating system error number + /// Error registers + public void WriteLine(ushort cylinder, byte head, byte sector, bool osError, int errno, + AtaErrorRegistersChs registers) + { + if(osError) + { + _logSw.WriteLine("ATA reading C/H/S {0}/{1}/{2} operating system error: {3}.", cylinder, head, sector, + errno); + + _logSw.Flush(); + } + else + { + List error = new List(); + List status = new List(); + + if((registers.Status & 0x01) == 0x01) + status.Add("ERR"); + + if((registers.Status & 0x02) == 0x02) + status.Add("IDX"); + + if((registers.Status & 0x04) == 0x04) + status.Add("CORR"); + + if((registers.Status & 0x08) == 0x08) + status.Add("DRQ"); + + if((registers.Status & 0x10) == 0x10) + status.Add("SRV"); + + if((registers.Status & 0x20) == 0x20) + status.Add("DF"); + + if((registers.Status & 0x40) == 0x40) + status.Add("RDY"); + + if((registers.Status & 0x80) == 0x80) + status.Add("BSY"); + + if((registers.Error & 0x01) == 0x01) + error.Add("AMNF"); + + if((registers.Error & 0x02) == 0x02) + error.Add("T0NF"); + + if((registers.Error & 0x04) == 0x04) + error.Add("ABRT"); + + if((registers.Error & 0x08) == 0x08) + error.Add("MCR"); + + if((registers.Error & 0x10) == 0x10) + error.Add("IDNF"); + + if((registers.Error & 0x20) == 0x20) + error.Add("MC"); + + if((registers.Error & 0x40) == 0x40) + error.Add("UNC"); + + if((registers.Error & 0x80) == 0x80) + error.Add("BBK"); + + _logSw.WriteLine("ATA reading C/H/S {0}/{1}/{2} error: status = {3}, error = {4}.", cylinder, head, + sector, string.Join(' ', status), string.Join(' ', error)); + + _logSw.Flush(); + } + } + + /// Register an ATA error after trying to read using 28-bit LBA commands + /// Starting block + /// true if operating system returned an error status instead of the device + /// Operating system error number + /// Error registers + public void WriteLine(ulong block, bool osError, int errno, AtaErrorRegistersLba28 registers) + { + if(osError) + { + _logSw.WriteLine("ATA reading LBA {0} operating system error: {1}.", block, errno); + _logSw.Flush(); + } + else + { + List error = new List(); + List status = new List(); + + if((registers.Status & 0x01) == 0x01) + status.Add("ERR"); + + if((registers.Status & 0x02) == 0x02) + status.Add("IDX"); + + if((registers.Status & 0x04) == 0x04) + status.Add("CORR"); + + if((registers.Status & 0x08) == 0x08) + status.Add("DRQ"); + + if((registers.Status & 0x10) == 0x10) + status.Add("SRV"); + + if((registers.Status & 0x20) == 0x20) + status.Add("DF"); + + if((registers.Status & 0x40) == 0x40) + status.Add("RDY"); + + if((registers.Status & 0x80) == 0x80) + status.Add("BSY"); + + if((registers.Error & 0x01) == 0x01) + error.Add("AMNF"); + + if((registers.Error & 0x02) == 0x02) + error.Add("T0NF"); + + if((registers.Error & 0x04) == 0x04) + error.Add("ABRT"); + + if((registers.Error & 0x08) == 0x08) + error.Add("MCR"); + + if((registers.Error & 0x10) == 0x10) + error.Add("IDNF"); + + if((registers.Error & 0x20) == 0x20) + error.Add("MC"); + + if((registers.Error & 0x40) == 0x40) + error.Add("UNC"); + + if((registers.Error & 0x80) == 0x80) + error.Add("BBK"); + + _logSw.WriteLine("ATA reading LBA {0} error: status = {1}, error = {2}.", block, + string.Join(' ', status), string.Join(' ', error)); + + _logSw.Flush(); + } + } + + /// Register an ATA error after trying to read using 48-bit LBA commands + /// Starting block + /// true if operating system returned an error status instead of the device + /// Operating system error number + /// Error registers + public void WriteLine(ulong block, bool osError, int errno, AtaErrorRegistersLba48 registers) + { + if(osError) + { + _logSw.WriteLine("ATA reading LBA {0} operating system error: {1}.", block, errno); + _logSw.Flush(); + } + else + { + List error = new List(); + List status = new List(); + + if((registers.Status & 0x01) == 0x01) + status.Add("ERR"); + + if((registers.Status & 0x02) == 0x02) + status.Add("IDX"); + + if((registers.Status & 0x04) == 0x04) + status.Add("CORR"); + + if((registers.Status & 0x08) == 0x08) + status.Add("DRQ"); + + if((registers.Status & 0x10) == 0x10) + status.Add("SRV"); + + if((registers.Status & 0x20) == 0x20) + status.Add("DF"); + + if((registers.Status & 0x40) == 0x40) + status.Add("RDY"); + + if((registers.Status & 0x80) == 0x80) + status.Add("BSY"); + + if((registers.Error & 0x01) == 0x01) + error.Add("AMNF"); + + if((registers.Error & 0x02) == 0x02) + error.Add("T0NF"); + + if((registers.Error & 0x04) == 0x04) + error.Add("ABRT"); + + if((registers.Error & 0x08) == 0x08) + error.Add("MCR"); + + if((registers.Error & 0x10) == 0x10) + error.Add("IDNF"); + + if((registers.Error & 0x20) == 0x20) + error.Add("MC"); + + if((registers.Error & 0x40) == 0x40) + error.Add("UNC"); + + if((registers.Error & 0x80) == 0x80) + error.Add("BBK"); + + _logSw.WriteLine("ATA reading LBA {0} error: status = {1}, error = {2}.", block, + string.Join(' ', status), string.Join(' ', error)); + + _logSw.Flush(); + } + } + + /// Register a SCSI error after sending a command + /// Command + /// true if operating system returned an error status instead of the device + /// Operating system error number + /// REQUEST SENSE response buffer + public void WriteLine(string command, bool osError, int errno, byte[] senseBuffer) + { + if(osError) + { + _logSw.WriteLine("SCSI command {0} operating system error: {1}.", command, errno); + _logSw.Flush(); + + return; + } + + DecodedSense? decodedSense = Sense.Decode(senseBuffer); + string prettySense = Sense.PrettifySense(senseBuffer); + string hexSense = string.Join(' ', senseBuffer.Select(b => $"{b:X2}")); + + if(decodedSense.HasValue) + { + if(prettySense != null) + { + if(prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) + prettySense = prettySense.Substring(12); + + if(prettySense.EndsWith('\n')) + prettySense = prettySense.Substring(0, prettySense.Length - 1); + + prettySense = prettySense.Replace("\n", " - "); + + _logSw.WriteLine("SCSI command {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}, {5}.", command, + decodedSense.Value.SenseKey, decodedSense.Value.ASC, decodedSense.Value.ASCQ, + hexSense, prettySense); + } + else + { + _logSw.WriteLine("SCSI command {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", command, + decodedSense.Value.SenseKey, decodedSense.Value.ASC, decodedSense.Value.ASCQ, + hexSense); + } + } + else + { + if(prettySense != null) + { + if(prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) + prettySense = prettySense.Substring(12); + + if(prettySense.EndsWith('\n')) + prettySense = prettySense.Substring(0, prettySense.Length - 1); + + prettySense = prettySense.Replace("\n", " - "); + + _logSw.WriteLine("SCSI command {0} error: {1}, {2}.", command, hexSense, prettySense); + } + else + { + _logSw.WriteLine("SCSI command {0} error: {1}", command, hexSense); + } + } + + _logSw.Flush(); + } + + /// Register an SCSI error after trying to read + /// Starting block + /// true if operating system returned an error status instead of the device + /// Operating system error number + /// REQUEST SENSE response buffer + public void WriteLine(ulong block, bool osError, int errno, byte[] senseBuffer) + { + if(osError) + { + _logSw.WriteLine("SCSI reading LBA {0} operating system error: {1}.", block, errno); + _logSw.Flush(); + + if(senseBuffer is null || + senseBuffer.Length == 0 || + senseBuffer.All(s => s == 0)) + return; + } + + DecodedSense? decodedSense = Sense.Decode(senseBuffer); + string prettySense = Sense.PrettifySense(senseBuffer); + string hexSense = string.Join(' ', senseBuffer.Select(b => $"{b:X2}")); + + if(decodedSense.HasValue) + { + if(prettySense != null) + { + if(prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) + prettySense = prettySense.Substring(12); + + if(prettySense.EndsWith('\n')) + prettySense = prettySense.Substring(0, prettySense.Length - 1); + + prettySense = prettySense.Replace("\n", " - "); + + _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}, {5}.", block, + decodedSense.Value.SenseKey, decodedSense.Value.ASC, decodedSense.Value.ASCQ, + hexSense, prettySense); + } + else + { + _logSw.WriteLine("SCSI reading LBA {0} error: SENSE {1} ASC {2:X2}h ASCQ {3:X2}h, {4}.", block, + decodedSense.Value.SenseKey, decodedSense.Value.ASC, decodedSense.Value.ASCQ, + hexSense); + } + } + else + { + if(prettySense != null) + { + if(prettySense.StartsWith("SCSI SENSE: ", StringComparison.Ordinal)) + prettySense = prettySense.Substring(12); + + if(prettySense.EndsWith('\n')) + prettySense = prettySense.Substring(0, prettySense.Length - 1); + + prettySense = prettySense.Replace("\n", " - "); + + _logSw.WriteLine("SCSI reading LBA {0} error: {1}, {2}.", block, hexSense, prettySense); + } + else + { + _logSw.WriteLine("SCSI reading LBA {0} error: {1}", block, hexSense); + } + } + + _logSw.Flush(); + } + + /// Register a SecureDigital / MultiMediaCard error after sending a command + /// Command + /// true if operating system returned an error status instead of the device + /// Operating system error number + /// Response + public void WriteLine(string command, bool osError, int errno, uint[] response) + { + if(osError) + { + _logSw.WriteLine("SD/MMC command {0} operating system error: {1}.", command, errno); + _logSw.Flush(); + + return; + } + + // TODO: Decode response + _logSw.WriteLine("SD/MMC command {0} error: {1}", command, + string.Join(" - ", response.Select(r => $"0x{r:X8}"))); + + _logSw.Flush(); + } + + /// Register a SecureDigital / MultiMediaCard error after trying to read + /// Starting block + /// true if operating system returned an error status instead of the device + /// Operating system error number + /// Byte addressed + /// Response + public void WriteLine(ulong block, bool osError, int errno, bool byteAddressed, uint[] response) + + { + if(osError) + { + _logSw.WriteLine("SD/MMC reading LBA {0} ({1}-addressed) operating system error: {2}.", block, + byteAddressed ? "byte" : "block", errno); + + _logSw.Flush(); + + return; + } + + _logSw.WriteLine("SD/MMC reading LBA {0} ({1}-addressed) error: {2}", block, + byteAddressed ? "byte" : "block", string.Join(" - ", response.Select(r => $"0x{r:X8}"))); + + throw new NotImplementedException(); } } \ No newline at end of file diff --git a/Aaru.Core/Logging/IBGLog.cs b/Aaru.Core/Logging/IBGLog.cs index e79f3ca14..d79012306 100644 --- a/Aaru.Core/Logging/IBGLog.cs +++ b/Aaru.Core/Logging/IBGLog.cs @@ -36,317 +36,316 @@ using System.IO; using System.Text; using Aaru.Devices; -namespace Aaru.Core.Logging +namespace Aaru.Core.Logging; + +/// Implements a log in the format used by IMGBurn +internal sealed class IbgLog { - /// Implements a log in the format used by IMGBurn - internal sealed class IbgLog + readonly CultureInfo _ibgCulture; + readonly double _ibgDivider; + readonly string _ibgMediaType; + readonly StringBuilder _ibgSb; + readonly string _logFile; + DateTime _ibgDatePoint; + ulong _ibgIntSector; + double _ibgIntSpeed; + double _ibgMaxSpeed; + int _ibgSampleRate; + int _ibgSnaps; + bool _ibgStartSet; + double _ibgStartSpeed; + + /// Initializes the IMGBurn log + /// Log file + /// Profile as defined by SCSI MultiMedia Commands specification + internal IbgLog(string outputFile, ushort currentProfile) { - readonly CultureInfo _ibgCulture; - readonly double _ibgDivider; - readonly string _ibgMediaType; - readonly StringBuilder _ibgSb; - readonly string _logFile; - DateTime _ibgDatePoint; - ulong _ibgIntSector; - double _ibgIntSpeed; - double _ibgMaxSpeed; - int _ibgSampleRate; - int _ibgSnaps; - bool _ibgStartSet; - double _ibgStartSpeed; + if(string.IsNullOrEmpty(outputFile)) + return; - /// Initializes the IMGBurn log - /// Log file - /// Profile as defined by SCSI MultiMedia Commands specification - internal IbgLog(string outputFile, ushort currentProfile) + _logFile = outputFile; + _ibgSb = new StringBuilder(); + _ibgDatePoint = DateTime.Now; + _ibgCulture = new CultureInfo("en-US"); + _ibgStartSet = false; + _ibgMaxSpeed = 0; + _ibgIntSpeed = 0; + _ibgSnaps = 0; + _ibgIntSector = 0; + + switch(currentProfile) { - if(string.IsNullOrEmpty(outputFile)) - return; + case 0x0001: + _ibgMediaType = "HDD"; + _ibgDivider = 1353; - _logFile = outputFile; - _ibgSb = new StringBuilder(); - _ibgDatePoint = DateTime.Now; - _ibgCulture = new CultureInfo("en-US"); - _ibgStartSet = false; - _ibgMaxSpeed = 0; - _ibgIntSpeed = 0; - _ibgSnaps = 0; - _ibgIntSector = 0; + break; + case 0x0002: + _ibgMediaType = "PD-650"; + _ibgDivider = 150; - switch(currentProfile) - { - case 0x0001: - _ibgMediaType = "HDD"; - _ibgDivider = 1353; + break; + case 0x0005: + _ibgMediaType = "CD-MO"; + _ibgDivider = 150; - break; - case 0x0002: - _ibgMediaType = "PD-650"; - _ibgDivider = 150; + break; + case 0x0008: + _ibgMediaType = "CD-ROM"; + _ibgDivider = 150; - break; - case 0x0005: - _ibgMediaType = "CD-MO"; - _ibgDivider = 150; + break; + case 0x0009: + _ibgMediaType = "CD-R"; + _ibgDivider = 150; - break; - case 0x0008: - _ibgMediaType = "CD-ROM"; - _ibgDivider = 150; + break; + case 0x000A: + _ibgMediaType = "CD-RW"; + _ibgDivider = 150; - break; - case 0x0009: - _ibgMediaType = "CD-R"; - _ibgDivider = 150; + break; + case 0x0010: + _ibgMediaType = "DVD-ROM"; + _ibgDivider = 1353; - break; - case 0x000A: - _ibgMediaType = "CD-RW"; - _ibgDivider = 150; + break; + case 0x0011: + _ibgMediaType = "DVD-R"; + _ibgDivider = 1353; - break; - case 0x0010: - _ibgMediaType = "DVD-ROM"; - _ibgDivider = 1353; + break; + case 0x0012: + _ibgMediaType = "DVD-RAM"; + _ibgDivider = 1353; - break; - case 0x0011: - _ibgMediaType = "DVD-R"; - _ibgDivider = 1353; + break; + case 0x0013: + case 0x0014: + _ibgMediaType = "DVD-RW"; + _ibgDivider = 1353; - break; - case 0x0012: - _ibgMediaType = "DVD-RAM"; - _ibgDivider = 1353; + break; + case 0x0015: + case 0x0016: + _ibgMediaType = "DVD-R DL"; + _ibgDivider = 1353; - break; - case 0x0013: - case 0x0014: - _ibgMediaType = "DVD-RW"; - _ibgDivider = 1353; + break; + case 0x0017: + _ibgMediaType = "DVD-RW DL"; + _ibgDivider = 1353; - break; - case 0x0015: - case 0x0016: - _ibgMediaType = "DVD-R DL"; - _ibgDivider = 1353; + break; + case 0x0018: + _ibgMediaType = "DVD-Download"; + _ibgDivider = 1353; - break; - case 0x0017: - _ibgMediaType = "DVD-RW DL"; - _ibgDivider = 1353; + break; + case 0x001A: + _ibgMediaType = "DVD+RW"; + _ibgDivider = 1353; - break; - case 0x0018: - _ibgMediaType = "DVD-Download"; - _ibgDivider = 1353; + break; + case 0x001B: + _ibgMediaType = "DVD+R"; + _ibgDivider = 1353; - break; - case 0x001A: - _ibgMediaType = "DVD+RW"; - _ibgDivider = 1353; + break; + case 0x0020: + _ibgMediaType = "DDCD-ROM"; + _ibgDivider = 150; - break; - case 0x001B: - _ibgMediaType = "DVD+R"; - _ibgDivider = 1353; + break; + case 0x0021: + _ibgMediaType = "DDCD-R"; + _ibgDivider = 150; - break; - case 0x0020: - _ibgMediaType = "DDCD-ROM"; - _ibgDivider = 150; + break; + case 0x0022: + _ibgMediaType = "DDCD-RW"; + _ibgDivider = 150; - break; - case 0x0021: - _ibgMediaType = "DDCD-R"; - _ibgDivider = 150; + break; + case 0x002A: + _ibgMediaType = "DVD+RW DL"; + _ibgDivider = 1353; - break; - case 0x0022: - _ibgMediaType = "DDCD-RW"; - _ibgDivider = 150; + break; + case 0x002B: + _ibgMediaType = "DVD+R DL"; + _ibgDivider = 1353; - break; - case 0x002A: - _ibgMediaType = "DVD+RW DL"; - _ibgDivider = 1353; + break; + case 0x0040: + _ibgMediaType = "BD-ROM"; + _ibgDivider = 4500; - break; - case 0x002B: - _ibgMediaType = "DVD+R DL"; - _ibgDivider = 1353; + break; + case 0x0041: + case 0x0042: + _ibgMediaType = "BD-R"; + _ibgDivider = 4500; - break; - case 0x0040: - _ibgMediaType = "BD-ROM"; - _ibgDivider = 4500; + break; + case 0x0043: + _ibgMediaType = "BD-RE"; + _ibgDivider = 4500; - break; - case 0x0041: - case 0x0042: - _ibgMediaType = "BD-R"; - _ibgDivider = 4500; + break; + case 0x0050: + _ibgMediaType = "HD DVD-ROM"; + _ibgDivider = 4500; - break; - case 0x0043: - _ibgMediaType = "BD-RE"; - _ibgDivider = 4500; + break; + case 0x0051: + _ibgMediaType = "HD DVD-R"; + _ibgDivider = 4500; - break; - case 0x0050: - _ibgMediaType = "HD DVD-ROM"; - _ibgDivider = 4500; + break; + case 0x0052: + _ibgMediaType = "HD DVD-RAM"; + _ibgDivider = 4500; - break; - case 0x0051: - _ibgMediaType = "HD DVD-R"; - _ibgDivider = 4500; + break; + case 0x0053: + _ibgMediaType = "HD DVD-RW"; + _ibgDivider = 4500; - break; - case 0x0052: - _ibgMediaType = "HD DVD-RAM"; - _ibgDivider = 4500; + break; + case 0x0058: + _ibgMediaType = "HD DVD-R DL"; + _ibgDivider = 4500; - break; - case 0x0053: - _ibgMediaType = "HD DVD-RW"; - _ibgDivider = 4500; + break; + case 0x005A: + _ibgMediaType = "HD DVD-RW DL"; + _ibgDivider = 4500; - break; - case 0x0058: - _ibgMediaType = "HD DVD-R DL"; - _ibgDivider = 4500; + break; + default: + _ibgMediaType = "Unknown"; + _ibgDivider = 1353; - break; - case 0x005A: - _ibgMediaType = "HD DVD-RW DL"; - _ibgDivider = 4500; - - break; - default: - _ibgMediaType = "Unknown"; - _ibgDivider = 1353; - - break; - } - } - - /// Adds a new speed snapshot to the log - /// Sector for the snapshot - /// Current speed at the snapshot - internal void Write(ulong sector, double currentSpeed) - { - if(_logFile == null) - return; - - _ibgIntSpeed += currentSpeed; - _ibgSampleRate += (int)Math.Floor((DateTime.Now - _ibgDatePoint).TotalMilliseconds); - _ibgSnaps++; - - if(_ibgSampleRate < 100) - return; - - if(_ibgIntSpeed > 0 && - !_ibgStartSet) - { - _ibgStartSpeed = _ibgIntSpeed / _ibgSnaps / _ibgDivider; - _ibgStartSet = true; - } - - _ibgSb.AppendFormat("{0:0.00},{1},{2:0},0", _ibgIntSpeed / _ibgSnaps / _ibgDivider, _ibgIntSector, - _ibgSampleRate).AppendLine(); - - if(_ibgIntSpeed / _ibgSnaps / _ibgDivider > _ibgMaxSpeed) - _ibgMaxSpeed = _ibgIntSpeed / _ibgDivider; - - _ibgDatePoint = DateTime.Now; - _ibgIntSpeed = 0; - _ibgSampleRate = 0; - _ibgSnaps = 0; - _ibgIntSector = sector; - } - - /// Closes the IMGBurn log - /// Device - /// Media blocks - /// Bytes per block - /// Total seconds spent dumping - /// Speed at the end - /// Average speed - /// Device path - internal void Close(Device dev, ulong blocks, ulong blockSize, double totalSeconds, double currentSpeed, - double averageSpeed, string devicePath) - { - if(_logFile == null) - return; - - var ibgFs = new FileStream(_logFile, FileMode.Create); - var ibgHeader = new StringBuilder(); - string ibgBusType; - - if(dev.IsUsb) - ibgBusType = "USB"; - else if(dev.IsFireWire) - ibgBusType = "FireWire"; - else - ibgBusType = dev.Type.ToString(); - - ibgHeader.AppendLine("IBGD"); - ibgHeader.AppendLine(); - ibgHeader.AppendLine("[START_CONFIGURATION]"); - ibgHeader.AppendLine("IBGD_VERSION=2"); - ibgHeader.AppendLine(); - ibgHeader.AppendFormat("DATE={0}", DateTime.Now).AppendLine(); - ibgHeader.AppendLine(); - ibgHeader.AppendFormat("SAMPLE_RATE={0}", 100).AppendLine(); - - ibgHeader.AppendLine(); - - ibgHeader.AppendFormat("DEVICE=[0:0:0] {0} {1} ({2}) ({3})", dev.Manufacturer, dev.Model, devicePath, - ibgBusType).AppendLine(); - - ibgHeader.AppendLine("DEVICE_ADDRESS=0:0:0"); - ibgHeader.AppendFormat("DEVICE_MAKEMODEL={0} {1}", dev.Manufacturer, dev.Model).AppendLine(); - ibgHeader.AppendFormat("DEVICE_FIRMWAREVERSION={0}", dev.FirmwareRevision).AppendLine(); - ibgHeader.AppendFormat("DEVICE_DRIVELETTER={0}", devicePath).AppendLine(); - ibgHeader.AppendFormat("DEVICE_BUSTYPE={0}", ibgBusType).AppendLine(); - ibgHeader.AppendLine(); - - ibgHeader.AppendFormat("MEDIA_TYPE={0}", _ibgMediaType).AppendLine(); - ibgHeader.AppendLine("MEDIA_BOOKTYPE=Unknown"); - ibgHeader.AppendLine("MEDIA_ID=N/A"); - ibgHeader.AppendLine("MEDIA_TRACKPATH=PTP"); - ibgHeader.AppendLine("MEDIA_SPEEDS=N/A"); - ibgHeader.AppendFormat("MEDIA_CAPACITY={0}", blocks).AppendLine(); - ibgHeader.AppendLine("MEDIA_LAYER_BREAK=0"); - ibgHeader.AppendLine(); - ibgHeader.AppendLine("DATA_IMAGEFILE=/dev/null"); - ibgHeader.AppendFormat("DATA_SECTORS={0}", blocks).AppendLine(); - ibgHeader.AppendFormat("DATA_TYPE=MODE1/{0}", blockSize).AppendLine(); - ibgHeader.AppendLine("DATA_VOLUMEIDENTIFIER="); - ibgHeader.AppendLine(); - ibgHeader.AppendFormat(_ibgCulture, "VERIFY_SPEED_START={0:0.00}", _ibgStartSpeed).AppendLine(); - ibgHeader.AppendFormat(_ibgCulture, "VERIFY_SPEED_END={0:0.00}", currentSpeed / _ibgDivider).AppendLine(); - - ibgHeader.AppendFormat(_ibgCulture, "VERIFY_SPEED_AVERAGE={0:0.00}", averageSpeed / _ibgDivider). - AppendLine(); - - ibgHeader.AppendFormat(_ibgCulture, "VERIFY_SPEED_MAX={0:0.00}", _ibgMaxSpeed).AppendLine(); - ibgHeader.AppendFormat(_ibgCulture, "VERIFY_TIME_TAKEN={0:0}", Math.Floor(totalSeconds)).AppendLine(); - ibgHeader.AppendLine("[END_CONFIGURATION]"); - ibgHeader.AppendLine(); - ibgHeader.AppendLine("HRPC=True"); - ibgHeader.AppendLine(); - ibgHeader.AppendLine("[START_VERIFY_GRAPH_VALUES]"); - ibgHeader.Append(_ibgSb); - ibgHeader.AppendLine("[END_VERIFY_GRAPH_VALUES]"); - ibgHeader.AppendLine(); - ibgHeader.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n"); - - var sr = new StreamWriter(ibgFs); - sr.Write(ibgHeader); - sr.Close(); - ibgFs.Close(); + break; } } + + /// Adds a new speed snapshot to the log + /// Sector for the snapshot + /// Current speed at the snapshot + internal void Write(ulong sector, double currentSpeed) + { + if(_logFile == null) + return; + + _ibgIntSpeed += currentSpeed; + _ibgSampleRate += (int)Math.Floor((DateTime.Now - _ibgDatePoint).TotalMilliseconds); + _ibgSnaps++; + + if(_ibgSampleRate < 100) + return; + + if(_ibgIntSpeed > 0 && + !_ibgStartSet) + { + _ibgStartSpeed = _ibgIntSpeed / _ibgSnaps / _ibgDivider; + _ibgStartSet = true; + } + + _ibgSb.AppendFormat("{0:0.00},{1},{2:0},0", _ibgIntSpeed / _ibgSnaps / _ibgDivider, _ibgIntSector, + _ibgSampleRate).AppendLine(); + + if(_ibgIntSpeed / _ibgSnaps / _ibgDivider > _ibgMaxSpeed) + _ibgMaxSpeed = _ibgIntSpeed / _ibgDivider; + + _ibgDatePoint = DateTime.Now; + _ibgIntSpeed = 0; + _ibgSampleRate = 0; + _ibgSnaps = 0; + _ibgIntSector = sector; + } + + /// Closes the IMGBurn log + /// Device + /// Media blocks + /// Bytes per block + /// Total seconds spent dumping + /// Speed at the end + /// Average speed + /// Device path + internal void Close(Device dev, ulong blocks, ulong blockSize, double totalSeconds, double currentSpeed, + double averageSpeed, string devicePath) + { + if(_logFile == null) + return; + + var ibgFs = new FileStream(_logFile, FileMode.Create); + var ibgHeader = new StringBuilder(); + string ibgBusType; + + if(dev.IsUsb) + ibgBusType = "USB"; + else if(dev.IsFireWire) + ibgBusType = "FireWire"; + else + ibgBusType = dev.Type.ToString(); + + ibgHeader.AppendLine("IBGD"); + ibgHeader.AppendLine(); + ibgHeader.AppendLine("[START_CONFIGURATION]"); + ibgHeader.AppendLine("IBGD_VERSION=2"); + ibgHeader.AppendLine(); + ibgHeader.AppendFormat("DATE={0}", DateTime.Now).AppendLine(); + ibgHeader.AppendLine(); + ibgHeader.AppendFormat("SAMPLE_RATE={0}", 100).AppendLine(); + + ibgHeader.AppendLine(); + + ibgHeader.AppendFormat("DEVICE=[0:0:0] {0} {1} ({2}) ({3})", dev.Manufacturer, dev.Model, devicePath, + ibgBusType).AppendLine(); + + ibgHeader.AppendLine("DEVICE_ADDRESS=0:0:0"); + ibgHeader.AppendFormat("DEVICE_MAKEMODEL={0} {1}", dev.Manufacturer, dev.Model).AppendLine(); + ibgHeader.AppendFormat("DEVICE_FIRMWAREVERSION={0}", dev.FirmwareRevision).AppendLine(); + ibgHeader.AppendFormat("DEVICE_DRIVELETTER={0}", devicePath).AppendLine(); + ibgHeader.AppendFormat("DEVICE_BUSTYPE={0}", ibgBusType).AppendLine(); + ibgHeader.AppendLine(); + + ibgHeader.AppendFormat("MEDIA_TYPE={0}", _ibgMediaType).AppendLine(); + ibgHeader.AppendLine("MEDIA_BOOKTYPE=Unknown"); + ibgHeader.AppendLine("MEDIA_ID=N/A"); + ibgHeader.AppendLine("MEDIA_TRACKPATH=PTP"); + ibgHeader.AppendLine("MEDIA_SPEEDS=N/A"); + ibgHeader.AppendFormat("MEDIA_CAPACITY={0}", blocks).AppendLine(); + ibgHeader.AppendLine("MEDIA_LAYER_BREAK=0"); + ibgHeader.AppendLine(); + ibgHeader.AppendLine("DATA_IMAGEFILE=/dev/null"); + ibgHeader.AppendFormat("DATA_SECTORS={0}", blocks).AppendLine(); + ibgHeader.AppendFormat("DATA_TYPE=MODE1/{0}", blockSize).AppendLine(); + ibgHeader.AppendLine("DATA_VOLUMEIDENTIFIER="); + ibgHeader.AppendLine(); + ibgHeader.AppendFormat(_ibgCulture, "VERIFY_SPEED_START={0:0.00}", _ibgStartSpeed).AppendLine(); + ibgHeader.AppendFormat(_ibgCulture, "VERIFY_SPEED_END={0:0.00}", currentSpeed / _ibgDivider).AppendLine(); + + ibgHeader.AppendFormat(_ibgCulture, "VERIFY_SPEED_AVERAGE={0:0.00}", averageSpeed / _ibgDivider). + AppendLine(); + + ibgHeader.AppendFormat(_ibgCulture, "VERIFY_SPEED_MAX={0:0.00}", _ibgMaxSpeed).AppendLine(); + ibgHeader.AppendFormat(_ibgCulture, "VERIFY_TIME_TAKEN={0:0}", Math.Floor(totalSeconds)).AppendLine(); + ibgHeader.AppendLine("[END_CONFIGURATION]"); + ibgHeader.AppendLine(); + ibgHeader.AppendLine("HRPC=True"); + ibgHeader.AppendLine(); + ibgHeader.AppendLine("[START_VERIFY_GRAPH_VALUES]"); + ibgHeader.Append(_ibgSb); + ibgHeader.AppendLine("[END_VERIFY_GRAPH_VALUES]"); + ibgHeader.AppendLine(); + ibgHeader.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", "\r\n"); + + var sr = new StreamWriter(ibgFs); + sr.Write(ibgHeader); + sr.Close(); + ibgFs.Close(); + } } \ No newline at end of file diff --git a/Aaru.Core/Logging/MHDDLog.cs b/Aaru.Core/Logging/MHDDLog.cs index 910c6b4cd..f7075d32d 100644 --- a/Aaru.Core/Logging/MHDDLog.cs +++ b/Aaru.Core/Logging/MHDDLog.cs @@ -37,135 +37,134 @@ using System.Text; using Aaru.CommonTypes.Enums; using Aaru.Devices; -namespace Aaru.Core.Logging +namespace Aaru.Core.Logging; + +/// Implements a log in the format used by MHDD +internal sealed class MhddLog { - /// Implements a log in the format used by MHDD - internal sealed class MhddLog + const string MHDD_VER = "VER:2 "; + readonly string _logFile; + readonly MemoryStream _mhddFs; + + /// Initializes the MHDD log + /// Log file + /// Device + /// Blocks in media + /// Bytes per block + /// How many blocks read at once + /// Disable saving paths or serial numbers in log + internal MhddLog(string outputFile, Device dev, ulong blocks, ulong blockSize, ulong blocksToRead, + bool @private) { - const string MHDD_VER = "VER:2 "; - readonly string _logFile; - readonly MemoryStream _mhddFs; + if(dev == null || + string.IsNullOrEmpty(outputFile)) + return; - /// Initializes the MHDD log - /// Log file - /// Device - /// Blocks in media - /// Bytes per block - /// How many blocks read at once - /// Disable saving paths or serial numbers in log - internal MhddLog(string outputFile, Device dev, ulong blocks, ulong blockSize, ulong blocksToRead, - bool @private) + _mhddFs = new MemoryStream(); + _logFile = outputFile; + + string mode; + + switch(dev.Type) { - if(dev == null || - string.IsNullOrEmpty(outputFile)) - return; + case DeviceType.ATA: + case DeviceType.ATAPI: + mode = "MODE: IDE"; - _mhddFs = new MemoryStream(); - _logFile = outputFile; + break; + case DeviceType.SCSI: + mode = "MODE: SCSI"; - string mode; + break; + case DeviceType.MMC: + mode = "MODE: MMC"; - switch(dev.Type) - { - case DeviceType.ATA: - case DeviceType.ATAPI: - mode = "MODE: IDE"; + break; + case DeviceType.NVMe: + mode = "MODE: NVMe"; - break; - case DeviceType.SCSI: - mode = "MODE: SCSI"; + break; + case DeviceType.SecureDigital: + mode = "MODE: SD"; - break; - case DeviceType.MMC: - mode = "MODE: MMC"; + break; + default: + mode = "MODE: IDE"; - break; - case DeviceType.NVMe: - mode = "MODE: NVMe"; - - break; - case DeviceType.SecureDigital: - mode = "MODE: SD"; - - break; - default: - mode = "MODE: IDE"; - - break; - } - - string device = $"DEVICE: {dev.Manufacturer} {dev.Model}"; - string fw = $"F/W: {dev.FirmwareRevision}"; - string sn = $"S/N: {(@private ? "" : dev.Serial)}"; - string sectors = string.Format(new CultureInfo("en-US"), "SECTORS: {0:n0}", blocks); - string sectorSize = string.Format(new CultureInfo("en-US"), "SECTOR SIZE: {0:n0} bytes", blockSize); - - string scanBlockSize = - string.Format(new CultureInfo("en-US"), "SCAN BLOCK SIZE: {0:n0} sectors", blocksToRead); - - byte[] deviceBytes = Encoding.ASCII.GetBytes(device); - byte[] modeBytes = Encoding.ASCII.GetBytes(mode); - byte[] fwBytes = Encoding.ASCII.GetBytes(fw); - byte[] snBytes = Encoding.ASCII.GetBytes(sn); - byte[] sectorsBytes = Encoding.ASCII.GetBytes(sectors); - byte[] sectorSizeBytes = Encoding.ASCII.GetBytes(sectorSize); - byte[] scanBlockSizeBytes = Encoding.ASCII.GetBytes(scanBlockSize); - byte[] verBytes = Encoding.ASCII.GetBytes(MHDD_VER); - - uint pointer = (uint)(deviceBytes.Length + modeBytes.Length + fwBytes.Length + snBytes.Length + - sectorsBytes.Length + sectorSizeBytes.Length + scanBlockSizeBytes.Length + - verBytes.Length + (2 * 9) + // New lines - 4); // Pointer - - byte[] newLine = new byte[2]; - newLine[0] = 0x0D; - newLine[1] = 0x0A; - - _mhddFs.Write(BitConverter.GetBytes(pointer), 0, 4); - _mhddFs.Write(newLine, 0, 2); - _mhddFs.Write(verBytes, 0, verBytes.Length); - _mhddFs.Write(newLine, 0, 2); - _mhddFs.Write(modeBytes, 0, modeBytes.Length); - _mhddFs.Write(newLine, 0, 2); - _mhddFs.Write(deviceBytes, 0, deviceBytes.Length); - _mhddFs.Write(newLine, 0, 2); - _mhddFs.Write(fwBytes, 0, fwBytes.Length); - _mhddFs.Write(newLine, 0, 2); - _mhddFs.Write(snBytes, 0, snBytes.Length); - _mhddFs.Write(newLine, 0, 2); - _mhddFs.Write(sectorsBytes, 0, sectorsBytes.Length); - _mhddFs.Write(newLine, 0, 2); - _mhddFs.Write(sectorSizeBytes, 0, sectorSizeBytes.Length); - _mhddFs.Write(newLine, 0, 2); - _mhddFs.Write(scanBlockSizeBytes, 0, scanBlockSizeBytes.Length); - _mhddFs.Write(newLine, 0, 2); + break; } - /// Logs a new read - /// Starting sector - /// Duration in milliseconds - internal void Write(ulong sector, double duration) - { - if(_logFile == null) - return; + string device = $"DEVICE: {dev.Manufacturer} {dev.Model}"; + string fw = $"F/W: {dev.FirmwareRevision}"; + string sn = $"S/N: {(@private ? "" : dev.Serial)}"; + string sectors = string.Format(new CultureInfo("en-US"), "SECTORS: {0:n0}", blocks); + string sectorSize = string.Format(new CultureInfo("en-US"), "SECTOR SIZE: {0:n0} bytes", blockSize); - byte[] sectorBytes = BitConverter.GetBytes(sector); - byte[] durationBytes = BitConverter.GetBytes((ulong)(duration * 1000)); + string scanBlockSize = + string.Format(new CultureInfo("en-US"), "SCAN BLOCK SIZE: {0:n0} sectors", blocksToRead); - _mhddFs.Write(sectorBytes, 0, 8); - _mhddFs.Write(durationBytes, 0, 8); - } + byte[] deviceBytes = Encoding.ASCII.GetBytes(device); + byte[] modeBytes = Encoding.ASCII.GetBytes(mode); + byte[] fwBytes = Encoding.ASCII.GetBytes(fw); + byte[] snBytes = Encoding.ASCII.GetBytes(sn); + byte[] sectorsBytes = Encoding.ASCII.GetBytes(sectors); + byte[] sectorSizeBytes = Encoding.ASCII.GetBytes(sectorSize); + byte[] scanBlockSizeBytes = Encoding.ASCII.GetBytes(scanBlockSize); + byte[] verBytes = Encoding.ASCII.GetBytes(MHDD_VER); - /// Closes and writes to file the MHDD log - internal void Close() - { - if(_logFile == null) - return; + uint pointer = (uint)(deviceBytes.Length + modeBytes.Length + fwBytes.Length + snBytes.Length + + sectorsBytes.Length + sectorSizeBytes.Length + scanBlockSizeBytes.Length + + verBytes.Length + (2 * 9) + // New lines + 4); // Pointer - var fs = new FileStream(_logFile, FileMode.Create); - _mhddFs.WriteTo(fs); - _mhddFs.Close(); - fs.Close(); - } + byte[] newLine = new byte[2]; + newLine[0] = 0x0D; + newLine[1] = 0x0A; + + _mhddFs.Write(BitConverter.GetBytes(pointer), 0, 4); + _mhddFs.Write(newLine, 0, 2); + _mhddFs.Write(verBytes, 0, verBytes.Length); + _mhddFs.Write(newLine, 0, 2); + _mhddFs.Write(modeBytes, 0, modeBytes.Length); + _mhddFs.Write(newLine, 0, 2); + _mhddFs.Write(deviceBytes, 0, deviceBytes.Length); + _mhddFs.Write(newLine, 0, 2); + _mhddFs.Write(fwBytes, 0, fwBytes.Length); + _mhddFs.Write(newLine, 0, 2); + _mhddFs.Write(snBytes, 0, snBytes.Length); + _mhddFs.Write(newLine, 0, 2); + _mhddFs.Write(sectorsBytes, 0, sectorsBytes.Length); + _mhddFs.Write(newLine, 0, 2); + _mhddFs.Write(sectorSizeBytes, 0, sectorSizeBytes.Length); + _mhddFs.Write(newLine, 0, 2); + _mhddFs.Write(scanBlockSizeBytes, 0, scanBlockSizeBytes.Length); + _mhddFs.Write(newLine, 0, 2); + } + + /// Logs a new read + /// Starting sector + /// Duration in milliseconds + internal void Write(ulong sector, double duration) + { + if(_logFile == null) + return; + + byte[] sectorBytes = BitConverter.GetBytes(sector); + byte[] durationBytes = BitConverter.GetBytes((ulong)(duration * 1000)); + + _mhddFs.Write(sectorBytes, 0, 8); + _mhddFs.Write(durationBytes, 0, 8); + } + + /// Closes and writes to file the MHDD log + internal void Close() + { + if(_logFile == null) + return; + + var fs = new FileStream(_logFile, FileMode.Create); + _mhddFs.WriteTo(fs); + _mhddFs.Close(); + fs.Close(); } } \ No newline at end of file diff --git a/Aaru.Core/Logging/SubchannelLog.cs b/Aaru.Core/Logging/SubchannelLog.cs index 011ec1073..1ddb5fcda 100644 --- a/Aaru.Core/Logging/SubchannelLog.cs +++ b/Aaru.Core/Logging/SubchannelLog.cs @@ -30,267 +30,266 @@ using System; using System.IO; using Aaru.Decoders.CD; -namespace Aaru.Core.Logging +namespace Aaru.Core.Logging; + +/// Logs subchannel data +public class SubchannelLog { - /// Logs subchannel data - public class SubchannelLog + const int SUB_SIZE = 96; + readonly bool _bcd; + readonly StreamWriter _logSw; + + /// Initializes the subchannel log + /// Output log file + /// Drive returns subchannel in BCD format + public SubchannelLog(string outputFile, bool bcd) { - const int SUB_SIZE = 96; - readonly bool _bcd; - readonly StreamWriter _logSw; + if(string.IsNullOrEmpty(outputFile)) + return; - /// Initializes the subchannel log - /// Output log file - /// Drive returns subchannel in BCD format - public SubchannelLog(string outputFile, bool bcd) + _bcd = bcd; + + _logSw = new StreamWriter(outputFile, true); + + _logSw.WriteLine("Start subchannel logging at {0}", DateTime.Now); + _logSw.WriteLine("######################################################"); + _logSw.Flush(); + } + + /// Finishes and closes the subchannel log + public void Close() + { + _logSw.WriteLine("######################################################"); + _logSw.WriteLine("End logging at {0}", DateTime.Now); + _logSw.Close(); + } + + /// Logs an entry to the subchannel log + /// Subchannel data + /// Set to true if the subchannel data is raw + /// First LBA read from drive to retrieve the data + /// Number of blocks read + /// Set to true if the subchannel has been generated, false if read from media + /// Set to true if the subchannel has been fixed, false if as is + public void WriteEntry(byte[] subchannel, bool raw, long startingLba, uint blocks, bool generated, bool @fixed) + { + if(subchannel.Length / SUB_SIZE != blocks) { - if(string.IsNullOrEmpty(outputFile)) - return; - - _bcd = bcd; - - _logSw = new StreamWriter(outputFile, true); - - _logSw.WriteLine("Start subchannel logging at {0}", DateTime.Now); - _logSw.WriteLine("######################################################"); + _logSw.WriteLine("Data length is invalid!"); _logSw.Flush(); + + return; } - /// Finishes and closes the subchannel log - public void Close() + int[] p = new int[subchannel.Length / 8]; + int[] q = new int[subchannel.Length / 8]; + int[] r = new int[subchannel.Length / 8]; + int[] s = new int[subchannel.Length / 8]; + int[] t = new int[subchannel.Length / 8]; + int[] u = new int[subchannel.Length / 8]; + int[] v = new int[subchannel.Length / 8]; + int[] w = new int[subchannel.Length / 8]; + + for(int i = 0; i < subchannel.Length; i += 8) { - _logSw.WriteLine("######################################################"); - _logSw.WriteLine("End logging at {0}", DateTime.Now); - _logSw.Close(); + p[i / 8] = subchannel[i] & 0x80; + p[i / 8] += (subchannel[i + 1] & 0x80) >> 1; + p[i / 8] += (subchannel[i + 2] & 0x80) >> 2; + p[i / 8] += (subchannel[i + 3] & 0x80) >> 3; + p[i / 8] += (subchannel[i + 4] & 0x80) >> 4; + p[i / 8] += (subchannel[i + 5] & 0x80) >> 5; + p[i / 8] += (subchannel[i + 6] & 0x80) >> 6; + p[i / 8] += (subchannel[i + 7] & 0x80) >> 7; + + q[i / 8] = (subchannel[i] & 0x40) << 1; + q[i / 8] += subchannel[i + 1] & 0x40; + q[i / 8] += (subchannel[i + 2] & 0x40) >> 1; + q[i / 8] += (subchannel[i + 3] & 0x40) >> 2; + q[i / 8] += (subchannel[i + 4] & 0x40) >> 3; + q[i / 8] += (subchannel[i + 5] & 0x40) >> 4; + q[i / 8] += (subchannel[i + 6] & 0x40) >> 5; + q[i / 8] += (subchannel[i + 7] & 0x40) >> 6; + + r[i / 8] = (subchannel[i] & 0x20) << 2; + r[i / 8] += (subchannel[i + 1] & 0x20) << 1; + r[i / 8] += subchannel[i + 2] & 0x20; + r[i / 8] += (subchannel[i + 3] & 0x20) >> 1; + r[i / 8] += (subchannel[i + 4] & 0x20) >> 2; + r[i / 8] += (subchannel[i + 5] & 0x20) >> 3; + r[i / 8] += (subchannel[i + 6] & 0x20) >> 4; + r[i / 8] += (subchannel[i + 7] & 0x20) >> 5; + + s[i / 8] = (subchannel[i] & 0x10) << 3; + s[i / 8] += (subchannel[i + 1] & 0x10) << 2; + s[i / 8] += (subchannel[i + 2] & 0x10) << 1; + s[i / 8] += subchannel[i + 3] & 0x10; + s[i / 8] += (subchannel[i + 4] & 0x10) >> 1; + s[i / 8] += (subchannel[i + 5] & 0x10) >> 2; + s[i / 8] += (subchannel[i + 6] & 0x10) >> 3; + s[i / 8] += (subchannel[i + 7] & 0x10) >> 4; + + t[i / 8] = (subchannel[i] & 0x08) << 4; + t[i / 8] += (subchannel[i + 1] & 0x08) << 3; + t[i / 8] += (subchannel[i + 2] & 0x08) << 2; + t[i / 8] += (subchannel[i + 3] & 0x08) << 1; + t[i / 8] += subchannel[i + 4] & 0x08; + t[i / 8] += (subchannel[i + 5] & 0x08) >> 1; + t[i / 8] += (subchannel[i + 6] & 0x08) >> 2; + t[i / 8] += (subchannel[i + 7] & 0x08) >> 3; + + u[i / 8] = (subchannel[i] & 0x04) << 5; + u[i / 8] += (subchannel[i + 1] & 0x04) << 4; + u[i / 8] += (subchannel[i + 2] & 0x04) << 3; + u[i / 8] += (subchannel[i + 3] & 0x04) << 2; + u[i / 8] += (subchannel[i + 4] & 0x04) << 1; + u[i / 8] += subchannel[i + 5] & 0x04; + u[i / 8] += (subchannel[i + 6] & 0x04) >> 1; + u[i / 8] += (subchannel[i + 7] & 0x04) >> 2; + + v[i / 8] = (subchannel[i] & 0x02) << 6; + v[i / 8] += (subchannel[i + 1] & 0x02) << 5; + v[i / 8] += (subchannel[i + 2] & 0x02) << 4; + v[i / 8] += (subchannel[i + 3] & 0x02) << 3; + v[i / 8] += (subchannel[i + 4] & 0x02) << 2; + v[i / 8] += (subchannel[i + 5] & 0x02) << 1; + v[i / 8] += subchannel[i + 6] & 0x02; + v[i / 8] += (subchannel[i + 7] & 0x02) >> 1; + + w[i / 8] = (subchannel[i] & 0x01) << 7; + w[i / 8] += (subchannel[i + 1] & 0x01) << 6; + w[i / 8] += (subchannel[i + 2] & 0x01) << 5; + w[i / 8] += (subchannel[i + 3] & 0x01) << 4; + w[i / 8] += (subchannel[i + 4] & 0x01) << 3; + w[i / 8] += (subchannel[i + 5] & 0x01) << 2; + w[i / 8] += (subchannel[i + 6] & 0x01) << 1; + w[i / 8] += subchannel[i + 7] & 0x01; } - /// Logs an entry to the subchannel log - /// Subchannel data - /// Set to true if the subchannel data is raw - /// First LBA read from drive to retrieve the data - /// Number of blocks read - /// Set to true if the subchannel has been generated, false if read from media - /// Set to true if the subchannel has been fixed, false if as is - public void WriteEntry(byte[] subchannel, bool raw, long startingLba, uint blocks, bool generated, bool @fixed) + for(uint block = 0; block < blocks; block++) { - if(subchannel.Length / SUB_SIZE != blocks) - { - _logSw.WriteLine("Data length is invalid!"); - _logSw.Flush(); + bool rwEmpty = true; - return; - } - - int[] p = new int[subchannel.Length / 8]; - int[] q = new int[subchannel.Length / 8]; - int[] r = new int[subchannel.Length / 8]; - int[] s = new int[subchannel.Length / 8]; - int[] t = new int[subchannel.Length / 8]; - int[] u = new int[subchannel.Length / 8]; - int[] v = new int[subchannel.Length / 8]; - int[] w = new int[subchannel.Length / 8]; - - for(int i = 0; i < subchannel.Length; i += 8) - { - p[i / 8] = subchannel[i] & 0x80; - p[i / 8] += (subchannel[i + 1] & 0x80) >> 1; - p[i / 8] += (subchannel[i + 2] & 0x80) >> 2; - p[i / 8] += (subchannel[i + 3] & 0x80) >> 3; - p[i / 8] += (subchannel[i + 4] & 0x80) >> 4; - p[i / 8] += (subchannel[i + 5] & 0x80) >> 5; - p[i / 8] += (subchannel[i + 6] & 0x80) >> 6; - p[i / 8] += (subchannel[i + 7] & 0x80) >> 7; - - q[i / 8] = (subchannel[i] & 0x40) << 1; - q[i / 8] += subchannel[i + 1] & 0x40; - q[i / 8] += (subchannel[i + 2] & 0x40) >> 1; - q[i / 8] += (subchannel[i + 3] & 0x40) >> 2; - q[i / 8] += (subchannel[i + 4] & 0x40) >> 3; - q[i / 8] += (subchannel[i + 5] & 0x40) >> 4; - q[i / 8] += (subchannel[i + 6] & 0x40) >> 5; - q[i / 8] += (subchannel[i + 7] & 0x40) >> 6; - - r[i / 8] = (subchannel[i] & 0x20) << 2; - r[i / 8] += (subchannel[i + 1] & 0x20) << 1; - r[i / 8] += subchannel[i + 2] & 0x20; - r[i / 8] += (subchannel[i + 3] & 0x20) >> 1; - r[i / 8] += (subchannel[i + 4] & 0x20) >> 2; - r[i / 8] += (subchannel[i + 5] & 0x20) >> 3; - r[i / 8] += (subchannel[i + 6] & 0x20) >> 4; - r[i / 8] += (subchannel[i + 7] & 0x20) >> 5; - - s[i / 8] = (subchannel[i] & 0x10) << 3; - s[i / 8] += (subchannel[i + 1] & 0x10) << 2; - s[i / 8] += (subchannel[i + 2] & 0x10) << 1; - s[i / 8] += subchannel[i + 3] & 0x10; - s[i / 8] += (subchannel[i + 4] & 0x10) >> 1; - s[i / 8] += (subchannel[i + 5] & 0x10) >> 2; - s[i / 8] += (subchannel[i + 6] & 0x10) >> 3; - s[i / 8] += (subchannel[i + 7] & 0x10) >> 4; - - t[i / 8] = (subchannel[i] & 0x08) << 4; - t[i / 8] += (subchannel[i + 1] & 0x08) << 3; - t[i / 8] += (subchannel[i + 2] & 0x08) << 2; - t[i / 8] += (subchannel[i + 3] & 0x08) << 1; - t[i / 8] += subchannel[i + 4] & 0x08; - t[i / 8] += (subchannel[i + 5] & 0x08) >> 1; - t[i / 8] += (subchannel[i + 6] & 0x08) >> 2; - t[i / 8] += (subchannel[i + 7] & 0x08) >> 3; - - u[i / 8] = (subchannel[i] & 0x04) << 5; - u[i / 8] += (subchannel[i + 1] & 0x04) << 4; - u[i / 8] += (subchannel[i + 2] & 0x04) << 3; - u[i / 8] += (subchannel[i + 3] & 0x04) << 2; - u[i / 8] += (subchannel[i + 4] & 0x04) << 1; - u[i / 8] += subchannel[i + 5] & 0x04; - u[i / 8] += (subchannel[i + 6] & 0x04) >> 1; - u[i / 8] += (subchannel[i + 7] & 0x04) >> 2; - - v[i / 8] = (subchannel[i] & 0x02) << 6; - v[i / 8] += (subchannel[i + 1] & 0x02) << 5; - v[i / 8] += (subchannel[i + 2] & 0x02) << 4; - v[i / 8] += (subchannel[i + 3] & 0x02) << 3; - v[i / 8] += (subchannel[i + 4] & 0x02) << 2; - v[i / 8] += (subchannel[i + 5] & 0x02) << 1; - v[i / 8] += subchannel[i + 6] & 0x02; - v[i / 8] += (subchannel[i + 7] & 0x02) >> 1; - - w[i / 8] = (subchannel[i] & 0x01) << 7; - w[i / 8] += (subchannel[i + 1] & 0x01) << 6; - w[i / 8] += (subchannel[i + 2] & 0x01) << 5; - w[i / 8] += (subchannel[i + 3] & 0x01) << 4; - w[i / 8] += (subchannel[i + 4] & 0x01) << 3; - w[i / 8] += (subchannel[i + 5] & 0x01) << 2; - w[i / 8] += (subchannel[i + 6] & 0x01) << 1; - w[i / 8] += subchannel[i + 7] & 0x01; - } - - for(uint block = 0; block < blocks; block++) - { - bool rwEmpty = true; - - if(raw) - for(uint i = 12 * block; i < (12 * block) + 12; i++) - { - if((r[i] == 0 && s[i] == 0 && t[i] == 0 && u[i] == 0 && v[i] == 0 && w[i] == 0) || - (r[i] == 0xFF && s[i] == 0xFF && t[i] == 0xFF && u[i] == 0xFF && v[i] == 0xFF && - w[i] == 0xFF)) - continue; - - rwEmpty = false; - - break; - } - - bool corruptedPause = false; - bool pause = false; - - for(int i = 0; i < 12; i++) + if(raw) + for(uint i = 12 * block; i < (12 * block) + 12; i++) { - if(p[i] == 0 || - p[i] == 0xFF) + if((r[i] == 0 && s[i] == 0 && t[i] == 0 && u[i] == 0 && v[i] == 0 && w[i] == 0) || + (r[i] == 0xFF && s[i] == 0xFF && t[i] == 0xFF && u[i] == 0xFF && v[i] == 0xFF && + w[i] == 0xFF)) continue; - corruptedPause = true; + rwEmpty = false; break; } - if(!corruptedPause) - pause = p[0] == 1; + bool corruptedPause = false; + bool pause = false; - byte[] subBuf = new byte[12]; - subBuf[0] = (byte)q[0 + (block * 12)]; - subBuf[1] = (byte)q[1 + (block * 12)]; - subBuf[2] = (byte)q[2 + (block * 12)]; - subBuf[3] = (byte)q[3 + (block * 12)]; - subBuf[4] = (byte)q[4 + (block * 12)]; - subBuf[5] = (byte)q[5 + (block * 12)]; - subBuf[6] = (byte)q[6 + (block * 12)]; - subBuf[7] = (byte)q[7 + (block * 12)]; - subBuf[8] = (byte)q[8 + (block * 12)]; - subBuf[9] = (byte)q[9 + (block * 12)]; - subBuf[10] = (byte)q[10 + (block * 12)]; - subBuf[11] = (byte)q[11 + (block * 12)]; + for(int i = 0; i < 12; i++) + { + if(p[i] == 0 || + p[i] == 0xFF) + continue; - string prettyQ = Subchannel.PrettifyQ(subBuf, generated || _bcd, startingLba + block, corruptedPause, - pause, rwEmpty); + corruptedPause = true; - if(generated) - prettyQ += " (GENERATED)"; - else if(@fixed) - prettyQ += " (FIXED)"; - - _logSw.WriteLine(prettyQ); + break; } - _logSw.Flush(); + if(!corruptedPause) + pause = p[0] == 1; + + byte[] subBuf = new byte[12]; + subBuf[0] = (byte)q[0 + (block * 12)]; + subBuf[1] = (byte)q[1 + (block * 12)]; + subBuf[2] = (byte)q[2 + (block * 12)]; + subBuf[3] = (byte)q[3 + (block * 12)]; + subBuf[4] = (byte)q[4 + (block * 12)]; + subBuf[5] = (byte)q[5 + (block * 12)]; + subBuf[6] = (byte)q[6 + (block * 12)]; + subBuf[7] = (byte)q[7 + (block * 12)]; + subBuf[8] = (byte)q[8 + (block * 12)]; + subBuf[9] = (byte)q[9 + (block * 12)]; + subBuf[10] = (byte)q[10 + (block * 12)]; + subBuf[11] = (byte)q[11 + (block * 12)]; + + string prettyQ = Subchannel.PrettifyQ(subBuf, generated || _bcd, startingLba + block, corruptedPause, + pause, rwEmpty); + + if(generated) + prettyQ += " (GENERATED)"; + else if(@fixed) + prettyQ += " (FIXED)"; + + _logSw.WriteLine(prettyQ); } - /// Logs message indicating the P subchannel has been fixed - /// LBA fix belongs to - public void WritePFix(long lba) => WriteMessageWithPosition(lba, "fixed P subchannel using weight average."); + _logSw.Flush(); + } - /// Logs message indicating the R-W subchannels have been fixed - /// LBA fix belongs to - public void WriteRwFix(long lba) => WriteMessageWithPosition(lba, "fixed R-W subchannels writing empty data."); + /// Logs message indicating the P subchannel has been fixed + /// LBA fix belongs to + public void WritePFix(long lba) => WriteMessageWithPosition(lba, "fixed P subchannel using weight average."); - /// Logs message indicating the ADR field of the Q subchannel has been fixed - /// LBA fix belongs to - public void WriteQAdrFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct ADR."); + /// Logs message indicating the R-W subchannels have been fixed + /// LBA fix belongs to + public void WriteRwFix(long lba) => WriteMessageWithPosition(lba, "fixed R-W subchannels writing empty data."); - /// Logs message indicating the CONTROL field of the Q subchannel has been fixed - /// LBA fix belongs to - public void WriteQCtrlFix(long lba) => - WriteMessageWithPosition(lba, "fixed Q subchannel with correct CONTROL."); + /// Logs message indicating the ADR field of the Q subchannel has been fixed + /// LBA fix belongs to + public void WriteQAdrFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct ADR."); - /// Logs message indicating the ZERO field of the Q subchannel has been fixed - /// LBA fix belongs to - public void WriteQZeroFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct ZERO."); + /// Logs message indicating the CONTROL field of the Q subchannel has been fixed + /// LBA fix belongs to + public void WriteQCtrlFix(long lba) => + WriteMessageWithPosition(lba, "fixed Q subchannel with correct CONTROL."); - /// Logs message indicating the TNO field of the Q subchannel has been fixed - /// LBA fix belongs to - public void WriteQTnoFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct TNO."); + /// Logs message indicating the ZERO field of the Q subchannel has been fixed + /// LBA fix belongs to + public void WriteQZeroFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct ZERO."); - /// Logs message indicating the INDEX field of the Q subchannel has been fixed - /// LBA fix belongs to - public void WriteQIndexFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct INDEX."); + /// Logs message indicating the TNO field of the Q subchannel has been fixed + /// LBA fix belongs to + public void WriteQTnoFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct TNO."); - /// Logs message indicating the relative position of the Q subchannel has been fixed - /// LBA fix belongs to - public void WriteQRelPosFix(long lba) => - WriteMessageWithPosition(lba, "fixed Q subchannel with correct RELATIVE POSITION."); + /// Logs message indicating the INDEX field of the Q subchannel has been fixed + /// LBA fix belongs to + public void WriteQIndexFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct INDEX."); - /// Logs message indicating the absolute position of the Q subchannel has been fixed - /// LBA fix belongs to - public void WriteQAbsPosFix(long lba) => - WriteMessageWithPosition(lba, "fixed Q subchannel with correct ABSOLUTE POSITION."); + /// Logs message indicating the relative position of the Q subchannel has been fixed + /// LBA fix belongs to + public void WriteQRelPosFix(long lba) => + WriteMessageWithPosition(lba, "fixed Q subchannel with correct RELATIVE POSITION."); - /// Logs message indicating the CRC of the Q subchannel has been fixed - /// LBA fix belongs to - public void WriteQCrcFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct CRC."); + /// Logs message indicating the absolute position of the Q subchannel has been fixed + /// LBA fix belongs to + public void WriteQAbsPosFix(long lba) => + WriteMessageWithPosition(lba, "fixed Q subchannel with correct ABSOLUTE POSITION."); - /// Logs message indicating the the Q subchannel has been fixed with a known good MCN - /// LBA fix belongs to - public void WriteQMcnFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with known good MCN."); + /// Logs message indicating the CRC of the Q subchannel has been fixed + /// LBA fix belongs to + public void WriteQCrcFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with correct CRC."); - /// Logs message indicating the the Q subchannel has been fixed with a known good ISRC - /// LBA fix belongs to - public void WriteQIsrcFix(long lba) => - WriteMessageWithPosition(lba, "fixed Q subchannel with known good ISRC."); + /// Logs message indicating the the Q subchannel has been fixed with a known good MCN + /// LBA fix belongs to + public void WriteQMcnFix(long lba) => WriteMessageWithPosition(lba, "fixed Q subchannel with known good MCN."); - /// Logs a message with a specified position - /// LBA position - /// Message to log - public void WriteMessageWithPosition(long lba, string message) - { - long minute = (lba + 150) / 4500; - long second = (lba + 150) % 4500 / 75; - long frame = (lba + 150) % 4500 % 75; - string area = lba < 0 ? "Lead-In" : "Program"; + /// Logs message indicating the the Q subchannel has been fixed with a known good ISRC + /// LBA fix belongs to + public void WriteQIsrcFix(long lba) => + WriteMessageWithPosition(lba, "fixed Q subchannel with known good ISRC."); - _logSw.WriteLine($"{minute:D2}:{second:D2}:{frame:D2} - LBA {lba,6}: {area} area, {message}"); - _logSw.Flush(); - } + /// Logs a message with a specified position + /// LBA position + /// Message to log + public void WriteMessageWithPosition(long lba, string message) + { + long minute = (lba + 150) / 4500; + long second = (lba + 150) % 4500 / 75; + long frame = (lba + 150) % 4500 % 75; + string area = lba < 0 ? "Lead-In" : "Program"; + + _logSw.WriteLine($"{minute:D2}:{second:D2}:{frame:D2} - LBA {lba,6}: {area} area, {message}"); + _logSw.Flush(); } } \ No newline at end of file diff --git a/Aaru.Core/Media/CompactDisc.cs b/Aaru.Core/Media/CompactDisc.cs index c5a436af6..cb43a055d 100644 --- a/Aaru.Core/Media/CompactDisc.cs +++ b/Aaru.Core/Media/CompactDisc.cs @@ -39,416 +39,395 @@ using Aaru.Decoders.CD; using Aaru.Devices; using Aaru.Helpers; -namespace Aaru.Core.Media +namespace Aaru.Core.Media; + +/// Operations over CD based media +public static class CompactDisc { - /// Operations over CD based media - public static class CompactDisc + /// Writes subchannel data to an image + /// Subchannel read by drive + /// Subchannel user wants written to image + /// Subchannel data + /// Starting sector + /// How many sectors were read + /// Subchannel log + /// List of ISRCs + /// Current track number + /// Disc's MCN + /// List of tracks + /// List of subchannel extents + /// If we want to fix subchannel position + /// Output image + /// If we want to fix subchannel contents + /// If we want to fix Q subchannel CRC if the contents look sane + /// Dumping log + /// Status update callback + /// List of smallest known pregap per track + /// Set if we are dumping, otherwise converting + /// true if indexes have changed, false otherwise + public static bool WriteSubchannelToImage(MmcSubchannel supportedSubchannel, MmcSubchannel desiredSubchannel, + byte[] sub, ulong sectorAddress, uint length, SubchannelLog subLog, + Dictionary isrcs, byte currentTrack, ref string mcn, + Track[] tracks, HashSet subchannelExtents, + bool fixSubchannelPosition, IWritableOpticalImage outputPlugin, + bool fixSubchannel, bool fixSubchannelCrc, DumpLog dumpLog, + UpdateStatusHandler updateStatus, + Dictionary smallestPregapLbaPerTrack, bool dumping) { - /// Writes subchannel data to an image - /// Subchannel read by drive - /// Subchannel user wants written to image - /// Subchannel data - /// Starting sector - /// How many sectors were read - /// Subchannel log - /// List of ISRCs - /// Current track number - /// Disc's MCN - /// List of tracks - /// List of subchannel extents - /// If we want to fix subchannel position - /// Output image - /// If we want to fix subchannel contents - /// If we want to fix Q subchannel CRC if the contents look sane - /// Dumping log - /// Status update callback - /// List of smallest known pregap per track - /// Set if we are dumping, otherwise converting - /// true if indexes have changed, false otherwise - public static bool WriteSubchannelToImage(MmcSubchannel supportedSubchannel, MmcSubchannel desiredSubchannel, - byte[] sub, ulong sectorAddress, uint length, SubchannelLog subLog, - Dictionary isrcs, byte currentTrack, ref string mcn, - Track[] tracks, HashSet subchannelExtents, - bool fixSubchannelPosition, IWritableOpticalImage outputPlugin, - bool fixSubchannel, bool fixSubchannelCrc, DumpLog dumpLog, - UpdateStatusHandler updateStatus, - Dictionary smallestPregapLbaPerTrack, bool dumping) + // We need to work in PW raw subchannels + if(supportedSubchannel == MmcSubchannel.Q16) + sub = Subchannel.ConvertQToRaw(sub); + + // If not desired to fix, or to save, the subchannel, just save as is (or none) + if(!fixSubchannelPosition && + desiredSubchannel != MmcSubchannel.None) + outputPlugin.WriteSectorsTag(sub, sectorAddress, length, SectorTagType.CdSectorSubchannel); + + subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)sectorAddress, length, false, + false); + + byte[] deSub = Subchannel.Deinterleave(sub); + + bool indexesChanged = CheckIndexesFromSubchannel(deSub, isrcs, currentTrack, ref mcn, tracks, dumpLog, + updateStatus, smallestPregapLbaPerTrack, dumping); + + if(!fixSubchannelPosition || + desiredSubchannel == MmcSubchannel.None) + return indexesChanged; + + int prePos = int.MinValue; + + // Check subchannel + for(int subPos = 0; subPos < deSub.Length; subPos += 96) { - // We need to work in PW raw subchannels - if(supportedSubchannel == MmcSubchannel.Q16) - sub = Subchannel.ConvertQToRaw(sub); + // Expected LBA + long lba = (long)sectorAddress + (subPos / 96); - // If not desired to fix, or to save, the subchannel, just save as is (or none) - if(!fixSubchannelPosition && - desiredSubchannel != MmcSubchannel.None) - outputPlugin.WriteSectorsTag(sub, sectorAddress, length, SectorTagType.CdSectorSubchannel); + // We fixed the subchannel + bool @fixed = false; - subLog?.WriteEntry(sub, supportedSubchannel == MmcSubchannel.Raw, (long)sectorAddress, length, false, - false); + byte[] q = new byte[12]; + Array.Copy(deSub, subPos + 12, q, 0, 12); - byte[] deSub = Subchannel.Deinterleave(sub); + // Check Q CRC + CRC16CCITTContext.Data(q, 10, out byte[] crc); + bool crcOk = crc[0] == q[10] && crc[1] == q[11]; - bool indexesChanged = CheckIndexesFromSubchannel(deSub, isrcs, currentTrack, ref mcn, tracks, dumpLog, - updateStatus, smallestPregapLbaPerTrack, dumping); + // Start considering P to be OK + bool pOk = true; + int pWeight = 0; - if(!fixSubchannelPosition || - desiredSubchannel == MmcSubchannel.None) - return indexesChanged; - - int prePos = int.MinValue; - - // Check subchannel - for(int subPos = 0; subPos < deSub.Length; subPos += 96) + // Check P and weight + for(int p = subPos; p < subPos + 12; p++) { - // Expected LBA - long lba = (long)sectorAddress + (subPos / 96); + if(deSub[p] != 0 && + deSub[p] != 255) + pOk = false; - // We fixed the subchannel - bool @fixed = false; - - byte[] q = new byte[12]; - Array.Copy(deSub, subPos + 12, q, 0, 12); - - // Check Q CRC - CRC16CCITTContext.Data(q, 10, out byte[] crc); - bool crcOk = crc[0] == q[10] && crc[1] == q[11]; - - // Start considering P to be OK - bool pOk = true; - int pWeight = 0; - - // Check P and weight - for(int p = subPos; p < subPos + 12; p++) - { - if(deSub[p] != 0 && - deSub[p] != 255) - pOk = false; - - for(int w = 0; w < 8; w++) - if(((deSub[p] >> w) & 1) > 0) - pWeight++; - } - - // This seems to be a somewhat common pattern - bool rOk = deSub.Skip(subPos + 24).Take(12).All(r => r == 0x00) || - deSub.Skip(subPos + 24).Take(12).All(r => r == 0xFF); - - bool sOk = deSub.Skip(subPos + 36).Take(12).All(s => s == 0x00) || - deSub.Skip(subPos + 36).Take(12).All(s => s == 0xFF); - - bool tOk = deSub.Skip(subPos + 48).Take(12).All(t => t == 0x00) || - deSub.Skip(subPos + 48).Take(12).All(t => t == 0xFF); - - bool uOk = deSub.Skip(subPos + 60).Take(12).All(u => u == 0x00) || - deSub.Skip(subPos + 60).Take(12).All(u => u == 0xFF); - - bool vOk = deSub.Skip(subPos + 72).Take(12).All(v => v == 0x00) || - deSub.Skip(subPos + 72).Take(12).All(v => v == 0xFF); - - bool wOk = deSub.Skip(subPos + 84).Take(12).All(w => w == 0x00) || - deSub.Skip(subPos + 84).Take(12).All(w => w == 0xFF); - - bool rwOk = rOk && sOk && tOk && uOk && vOk && wOk; - bool rwPacket = false; - bool cdtextPacket = false; - - // Check RW contents - if(!rwOk) - { - byte[] sectorSub = new byte[96]; - Array.Copy(sub, subPos, sectorSub, 0, 96); - - DetectRwPackets(sectorSub, out _, out rwPacket, out cdtextPacket); - - // TODO: CD+G reed solomon - if(rwPacket && !cdtextPacket) - rwOk = true; - - if(cdtextPacket) - rwOk = CheckCdTextPackets(sectorSub); - } - - // Fix P - if(!pOk && fixSubchannel) - { - if(pWeight >= 48) - for(int p = subPos; p < subPos + 12; p++) - deSub[p] = 255; - else - for(int p = subPos; p < subPos + 12; p++) - deSub[p] = 0; - - pOk = true; - @fixed = true; - - subLog?.WritePFix(lba); - } - - // RW is not a known pattern or packet, fix it - if(!rwOk && - !rwPacket && - !cdtextPacket && - fixSubchannel) - { - for(int rw = subPos + 24; rw < subPos + 96; rw++) - deSub[rw] = 0; - - rwOk = true; - @fixed = true; - - subLog.WriteRwFix(lba); - } - - byte smin, ssec, amin, asec, aframe; - int aPos; - - // Fix Q - if(!crcOk && - fixSubchannel && - subPos > 0 && - subPos < deSub.Length - 96) - { - isrcs.TryGetValue(currentTrack, out string knownGoodIsrc); - - crcOk = FixQSubchannel(deSub, q, subPos, mcn, knownGoodIsrc, fixSubchannelCrc, out bool fixedAdr, - out bool controlFix, out bool fixedZero, out bool fixedTno, - out bool fixedIndex, out bool fixedRelPos, out bool fixedAbsPos, - out bool fixedCrc, out bool fixedMcn, out bool fixedIsrc); - - if(crcOk) - { - Array.Copy(q, 0, deSub, subPos + 12, 12); - @fixed = true; - - if(fixedAdr) - subLog?.WriteQAdrFix(lba); - - if(controlFix) - subLog?.WriteQCtrlFix(lba); - - if(fixedZero) - subLog?.WriteQZeroFix(lba); - - if(fixedTno) - subLog?.WriteQTnoFix(lba); - - if(fixedIndex) - subLog?.WriteQIndexFix(lba); - - if(fixedRelPos) - subLog?.WriteQRelPosFix(lba); - - if(fixedAbsPos) - subLog?.WriteQAbsPosFix(lba); - - if(fixedCrc) - subLog?.WriteQCrcFix(lba); - - if(fixedMcn) - subLog?.WriteQMcnFix(lba); - - if(fixedIsrc) - subLog?.WriteQIsrcFix(lba); - } - } - - if(!pOk || - !crcOk || - !rwOk) - continue; - - aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); - - if((q[0] & 0x3) == 1) - { - amin = (byte)((q[7] / 16 * 10) + (q[7] & 0x0F)); - asec = (byte)((q[8] / 16 * 10) + (q[8] & 0x0F)); - aPos = (amin * 60 * 75) + (asec * 75) + aframe - 150; - } - else - { - ulong expectedSectorAddress = sectorAddress + (ulong)(subPos / 96) + 150; - smin = (byte)(expectedSectorAddress / 60 / 75); - expectedSectorAddress -= (ulong)(smin * 60 * 75); - ssec = (byte)(expectedSectorAddress / 75); - - aPos = (smin * 60 * 75) + (ssec * 75) + aframe - 150; - - // Next second - if(aPos < prePos) - aPos += 75; - } - - // TODO: Negative sectors - if(aPos < 0) - continue; - - prePos = aPos; - - byte[] posSub = new byte[96]; - Array.Copy(deSub, subPos, posSub, 0, 96); - posSub = Subchannel.Interleave(posSub); - outputPlugin.WriteSectorTag(posSub, (ulong)aPos, SectorTagType.CdSectorSubchannel); - - subchannelExtents.Remove(aPos); - - if(@fixed) - subLog?.WriteEntry(posSub, supportedSubchannel == MmcSubchannel.Raw, lba, 1, false, true); + for(int w = 0; w < 8; w++) + if(((deSub[p] >> w) & 1) > 0) + pWeight++; } - return indexesChanged; + // This seems to be a somewhat common pattern + bool rOk = deSub.Skip(subPos + 24).Take(12).All(r => r == 0x00) || + deSub.Skip(subPos + 24).Take(12).All(r => r == 0xFF); + + bool sOk = deSub.Skip(subPos + 36).Take(12).All(s => s == 0x00) || + deSub.Skip(subPos + 36).Take(12).All(s => s == 0xFF); + + bool tOk = deSub.Skip(subPos + 48).Take(12).All(t => t == 0x00) || + deSub.Skip(subPos + 48).Take(12).All(t => t == 0xFF); + + bool uOk = deSub.Skip(subPos + 60).Take(12).All(u => u == 0x00) || + deSub.Skip(subPos + 60).Take(12).All(u => u == 0xFF); + + bool vOk = deSub.Skip(subPos + 72).Take(12).All(v => v == 0x00) || + deSub.Skip(subPos + 72).Take(12).All(v => v == 0xFF); + + bool wOk = deSub.Skip(subPos + 84).Take(12).All(w => w == 0x00) || + deSub.Skip(subPos + 84).Take(12).All(w => w == 0xFF); + + bool rwOk = rOk && sOk && tOk && uOk && vOk && wOk; + bool rwPacket = false; + bool cdtextPacket = false; + + // Check RW contents + if(!rwOk) + { + byte[] sectorSub = new byte[96]; + Array.Copy(sub, subPos, sectorSub, 0, 96); + + DetectRwPackets(sectorSub, out _, out rwPacket, out cdtextPacket); + + // TODO: CD+G reed solomon + if(rwPacket && !cdtextPacket) + rwOk = true; + + if(cdtextPacket) + rwOk = CheckCdTextPackets(sectorSub); + } + + // Fix P + if(!pOk && fixSubchannel) + { + if(pWeight >= 48) + for(int p = subPos; p < subPos + 12; p++) + deSub[p] = 255; + else + for(int p = subPos; p < subPos + 12; p++) + deSub[p] = 0; + + pOk = true; + @fixed = true; + + subLog?.WritePFix(lba); + } + + // RW is not a known pattern or packet, fix it + if(!rwOk && + !rwPacket && + !cdtextPacket && + fixSubchannel) + { + for(int rw = subPos + 24; rw < subPos + 96; rw++) + deSub[rw] = 0; + + rwOk = true; + @fixed = true; + + subLog.WriteRwFix(lba); + } + + byte smin, ssec, amin, asec, aframe; + int aPos; + + // Fix Q + if(!crcOk && + fixSubchannel && + subPos > 0 && + subPos < deSub.Length - 96) + { + isrcs.TryGetValue(currentTrack, out string knownGoodIsrc); + + crcOk = FixQSubchannel(deSub, q, subPos, mcn, knownGoodIsrc, fixSubchannelCrc, out bool fixedAdr, + out bool controlFix, out bool fixedZero, out bool fixedTno, + out bool fixedIndex, out bool fixedRelPos, out bool fixedAbsPos, + out bool fixedCrc, out bool fixedMcn, out bool fixedIsrc); + + if(crcOk) + { + Array.Copy(q, 0, deSub, subPos + 12, 12); + @fixed = true; + + if(fixedAdr) + subLog?.WriteQAdrFix(lba); + + if(controlFix) + subLog?.WriteQCtrlFix(lba); + + if(fixedZero) + subLog?.WriteQZeroFix(lba); + + if(fixedTno) + subLog?.WriteQTnoFix(lba); + + if(fixedIndex) + subLog?.WriteQIndexFix(lba); + + if(fixedRelPos) + subLog?.WriteQRelPosFix(lba); + + if(fixedAbsPos) + subLog?.WriteQAbsPosFix(lba); + + if(fixedCrc) + subLog?.WriteQCrcFix(lba); + + if(fixedMcn) + subLog?.WriteQMcnFix(lba); + + if(fixedIsrc) + subLog?.WriteQIsrcFix(lba); + } + } + + if(!pOk || + !crcOk || + !rwOk) + continue; + + aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); + + if((q[0] & 0x3) == 1) + { + amin = (byte)((q[7] / 16 * 10) + (q[7] & 0x0F)); + asec = (byte)((q[8] / 16 * 10) + (q[8] & 0x0F)); + aPos = (amin * 60 * 75) + (asec * 75) + aframe - 150; + } + else + { + ulong expectedSectorAddress = sectorAddress + (ulong)(subPos / 96) + 150; + smin = (byte)(expectedSectorAddress / 60 / 75); + expectedSectorAddress -= (ulong)(smin * 60 * 75); + ssec = (byte)(expectedSectorAddress / 75); + + aPos = (smin * 60 * 75) + (ssec * 75) + aframe - 150; + + // Next second + if(aPos < prePos) + aPos += 75; + } + + // TODO: Negative sectors + if(aPos < 0) + continue; + + prePos = aPos; + + byte[] posSub = new byte[96]; + Array.Copy(deSub, subPos, posSub, 0, 96); + posSub = Subchannel.Interleave(posSub); + outputPlugin.WriteSectorTag(posSub, (ulong)aPos, SectorTagType.CdSectorSubchannel); + + subchannelExtents.Remove(aPos); + + if(@fixed) + subLog?.WriteEntry(posSub, supportedSubchannel == MmcSubchannel.Raw, lba, 1, false, true); } - /// Check subchannel for indexes - /// De-interleaved subchannel - /// List of ISRCs - /// Current track number - /// Disc's MCN - /// List of tracks - /// Dumping log - /// Status update callback - /// List of smallest known pregap per track - /// Set if we are dumping, otherwise converting - /// true if indexes have changed, false otherwise - static bool CheckIndexesFromSubchannel(byte[] deSub, Dictionary isrcs, byte currentTrack, - ref string mcn, Track[] tracks, DumpLog dumpLog, - UpdateStatusHandler updateStatus, - Dictionary smallestPregapLbaPerTrack, bool dumping) + return indexesChanged; + } + + /// Check subchannel for indexes + /// De-interleaved subchannel + /// List of ISRCs + /// Current track number + /// Disc's MCN + /// List of tracks + /// Dumping log + /// Status update callback + /// List of smallest known pregap per track + /// Set if we are dumping, otherwise converting + /// true if indexes have changed, false otherwise + static bool CheckIndexesFromSubchannel(byte[] deSub, Dictionary isrcs, byte currentTrack, + ref string mcn, Track[] tracks, DumpLog dumpLog, + UpdateStatusHandler updateStatus, + Dictionary smallestPregapLbaPerTrack, bool dumping) + { + bool status = false; + + // Check subchannel + for(int subPos = 0; subPos < deSub.Length; subPos += 96) { - bool status = false; + byte[] q = new byte[12]; + Array.Copy(deSub, subPos + 12, q, 0, 12); - // Check subchannel - for(int subPos = 0; subPos < deSub.Length; subPos += 96) + CRC16CCITTContext.Data(q, 10, out byte[] crc); + bool crcOk = crc[0] == q[10] && crc[1] == q[11]; + + switch(q[0] & 0x3) { - byte[] q = new byte[12]; - Array.Copy(deSub, subPos + 12, q, 0, 12); - - CRC16CCITTContext.Data(q, 10, out byte[] crc); - bool crcOk = crc[0] == q[10] && crc[1] == q[11]; - - switch(q[0] & 0x3) + // ISRC + case 3: { - // ISRC - case 3: + string isrc = Subchannel.DecodeIsrc(q); + + if(isrc == null || + isrc == "000000000000") + continue; + + if(!crcOk) + continue; + + if(!isrcs.ContainsKey(currentTrack)) { - string isrc = Subchannel.DecodeIsrc(q); + dumpLog?.WriteLine($"Found new ISRC {isrc} for track {currentTrack}."); + updateStatus?.Invoke($"Found new ISRC {isrc} for track {currentTrack}."); + } + else if(isrcs[currentTrack] != isrc) + { + dumpLog?. + WriteLine($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}."); - if(isrc == null || - isrc == "000000000000") - continue; - - if(!crcOk) - continue; - - if(!isrcs.ContainsKey(currentTrack)) - { - dumpLog?.WriteLine($"Found new ISRC {isrc} for track {currentTrack}."); - updateStatus?.Invoke($"Found new ISRC {isrc} for track {currentTrack}."); - } - else if(isrcs[currentTrack] != isrc) - { - dumpLog?. - WriteLine($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}."); - - updateStatus?. - Invoke($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}."); - } - - isrcs[currentTrack] = isrc; - - break; + updateStatus?. + Invoke($"ISRC for track {currentTrack} changed from {isrcs[currentTrack]} to {isrc}."); } - // MCN - case 2: + isrcs[currentTrack] = isrc; + + break; + } + + // MCN + case 2: + { + string newMcn = Subchannel.DecodeMcn(q); + + if(newMcn == null || + newMcn == "0000000000000") + continue; + + if(!crcOk) + continue; + + if(mcn is null) { - string newMcn = Subchannel.DecodeMcn(q); - - if(newMcn == null || - newMcn == "0000000000000") - continue; - - if(!crcOk) - continue; - - if(mcn is null) - { - dumpLog?.WriteLine($"Found new MCN {newMcn}."); - updateStatus?.Invoke($"Found new MCN {newMcn}."); - } - else if(mcn != newMcn) - { - dumpLog?.WriteLine($"MCN changed from {mcn} to {newMcn}."); - updateStatus?.Invoke($"MCN changed from {mcn} to {newMcn}."); - } - - mcn = newMcn; - - break; + dumpLog?.WriteLine($"Found new MCN {newMcn}."); + updateStatus?.Invoke($"Found new MCN {newMcn}."); + } + else if(mcn != newMcn) + { + dumpLog?.WriteLine($"MCN changed from {mcn} to {newMcn}."); + updateStatus?.Invoke($"MCN changed from {mcn} to {newMcn}."); } - // Positioning - case 1 when !crcOk: continue; - case 1: - { - byte trackNo = (byte)((q[1] / 16 * 10) + (q[1] & 0x0F)); + mcn = newMcn; - for(int i = 0; i < tracks.Length; i++) + break; + } + + // Positioning + case 1 when !crcOk: continue; + case 1: + { + byte trackNo = (byte)((q[1] / 16 * 10) + (q[1] & 0x0F)); + + for(int i = 0; i < tracks.Length; i++) + { + if(tracks[i].Sequence != trackNo) + continue; + + // Pregap + if(q[2] == 0 && + trackNo > 1) { - if(tracks[i].Sequence != trackNo) + byte pmin = (byte)((q[3] / 16 * 10) + (q[3] & 0x0F)); + byte psec = (byte)((q[4] / 16 * 10) + (q[4] & 0x0F)); + byte pframe = (byte)((q[5] / 16 * 10) + (q[5] & 0x0F)); + int qPos = (pmin * 60 * 75) + (psec * 75) + pframe; + + // When we are dumping we calculate the pregap in reverse from index 1 back. + // When we are not, we go from index 0. + if(!smallestPregapLbaPerTrack.ContainsKey(trackNo)) + smallestPregapLbaPerTrack[trackNo] = dumping ? 1 : 0; + + uint firstTrackNumberInSameSession = tracks. + Where(t => t.Session == + tracks[i].Session). + Min(t => t.Sequence); + + if(tracks[i].Sequence == firstTrackNumberInSameSession) continue; - // Pregap - if(q[2] == 0 && - trackNo > 1) + if(qPos < smallestPregapLbaPerTrack[trackNo]) { - byte pmin = (byte)((q[3] / 16 * 10) + (q[3] & 0x0F)); - byte psec = (byte)((q[4] / 16 * 10) + (q[4] & 0x0F)); - byte pframe = (byte)((q[5] / 16 * 10) + (q[5] & 0x0F)); - int qPos = (pmin * 60 * 75) + (psec * 75) + pframe; + int dif = smallestPregapLbaPerTrack[trackNo] - qPos; + tracks[i].Pregap += (ulong)dif; + tracks[i].StartSector -= (ulong)dif; + smallestPregapLbaPerTrack[trackNo] = qPos; - // When we are dumping we calculate the pregap in reverse from index 1 back. - // When we are not, we go from index 0. - if(!smallestPregapLbaPerTrack.ContainsKey(trackNo)) - smallestPregapLbaPerTrack[trackNo] = dumping ? 1 : 0; - - uint firstTrackNumberInSameSession = tracks. - Where(t => t.Session == - tracks[i].Session). - Min(t => t.Sequence); - - if(tracks[i].Sequence == firstTrackNumberInSameSession) - continue; - - if(qPos < smallestPregapLbaPerTrack[trackNo]) - { - int dif = smallestPregapLbaPerTrack[trackNo] - qPos; - tracks[i].Pregap += (ulong)dif; - tracks[i].StartSector -= (ulong)dif; - smallestPregapLbaPerTrack[trackNo] = qPos; - - if(i > 0 && - tracks[i - 1].EndSector >= tracks[i].StartSector) - tracks[i - 1].EndSector = tracks[i].StartSector - 1; - - dumpLog?. - WriteLine($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors."); - - updateStatus?. - Invoke($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors."); - - status = true; - } - - if(tracks[i].Pregap >= (ulong)qPos) - continue; - - ulong oldPregap = tracks[i].Pregap; - - tracks[i].Pregap = (ulong)qPos; - tracks[i].StartSector -= tracks[i].Pregap - oldPregap; - - if(i > 0 && + if(i > 0 && tracks[i - 1].EndSector >= tracks[i].StartSector) tracks[i - 1].EndSector = tracks[i].StartSector - 1; @@ -459,545 +438,581 @@ namespace Aaru.Core.Media Invoke($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors."); status = true; - - continue; } - if(q[2] == 0) + if(tracks[i].Pregap >= (ulong)qPos) continue; - byte amin = (byte)((q[7] / 16 * 10) + (q[7] & 0x0F)); - byte asec = (byte)((q[8] / 16 * 10) + (q[8] & 0x0F)); - byte aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); - int aPos = (amin * 60 * 75) + (asec * 75) + aframe - 150; + ulong oldPregap = tracks[i].Pregap; - if(tracks[i].Indexes.ContainsKey(q[2]) && - aPos >= tracks[i].Indexes[q[2]]) - continue; + tracks[i].Pregap = (ulong)qPos; + tracks[i].StartSector -= tracks[i].Pregap - oldPregap; - dumpLog?.WriteLine($"Setting index {q[2]} for track {trackNo} to LBA {aPos}."); - updateStatus?.Invoke($"Setting index {q[2]} for track {trackNo} to LBA {aPos}."); + if(i > 0 && + tracks[i - 1].EndSector >= tracks[i].StartSector) + tracks[i - 1].EndSector = tracks[i].StartSector - 1; - tracks[i].Indexes[q[2]] = aPos; + dumpLog?. + WriteLine($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors."); + + updateStatus?. + Invoke($"Pregap for track {trackNo} set to {tracks[i].Pregap} sectors."); status = true; + + continue; } - break; + if(q[2] == 0) + continue; + + byte amin = (byte)((q[7] / 16 * 10) + (q[7] & 0x0F)); + byte asec = (byte)((q[8] / 16 * 10) + (q[8] & 0x0F)); + byte aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); + int aPos = (amin * 60 * 75) + (asec * 75) + aframe - 150; + + if(tracks[i].Indexes.ContainsKey(q[2]) && + aPos >= tracks[i].Indexes[q[2]]) + continue; + + dumpLog?.WriteLine($"Setting index {q[2]} for track {trackNo} to LBA {aPos}."); + updateStatus?.Invoke($"Setting index {q[2]} for track {trackNo} to LBA {aPos}."); + + tracks[i].Indexes[q[2]] = aPos; + + status = true; } + + break; } } + } + return status; + } + + /// Detect RW packets + /// Subchannel data + /// Set if it contains a ZERO packet + /// Set if it contains a GRAPHICS, EXTENDED GRAPHICS or MIDI packet + /// Set if it contains a TEXT packet + static void DetectRwPackets(byte[] subchannel, out bool zero, out bool rwPacket, out bool cdtextPacket) + { + zero = false; + rwPacket = false; + cdtextPacket = false; + + byte[] cdTextPack1 = new byte[18]; + byte[] cdTextPack2 = new byte[18]; + byte[] cdTextPack3 = new byte[18]; + byte[] cdTextPack4 = new byte[18]; + byte[] cdSubRwPack1 = new byte[24]; + byte[] cdSubRwPack2 = new byte[24]; + byte[] cdSubRwPack3 = new byte[24]; + byte[] cdSubRwPack4 = new byte[24]; + + int i = 0; + + for(int j = 0; j < 18; j++) + { + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F)); + } + + i = 0; + + for(int j = 0; j < 24; j++) + cdSubRwPack1[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack2[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack3[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack4[j] = (byte)(subchannel[i++] & 0x3F); + + switch(cdSubRwPack1[0]) + { + case 0x00: + zero = true; + + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x18: + case 0x38: + rwPacket = true; + + break; + case 0x14: + cdtextPacket = true; + + break; + } + + switch(cdSubRwPack2[0]) + { + case 0x00: + zero = true; + + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x18: + case 0x38: + rwPacket = true; + + break; + case 0x14: + cdtextPacket = true; + + break; + } + + switch(cdSubRwPack3[0]) + { + case 0x00: + zero = true; + + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x18: + case 0x38: + rwPacket = true; + + break; + case 0x14: + cdtextPacket = true; + + break; + } + + switch(cdSubRwPack4[0]) + { + case 0x00: + zero = true; + + break; + case 0x08: + case 0x09: + case 0x0A: + case 0x18: + case 0x38: + rwPacket = true; + + break; + case 0x14: + cdtextPacket = true; + + break; + } + + if((cdTextPack1[0] & 0x80) == 0x80) + cdtextPacket = true; + + if((cdTextPack2[0] & 0x80) == 0x80) + cdtextPacket = true; + + if((cdTextPack3[0] & 0x80) == 0x80) + cdtextPacket = true; + + if((cdTextPack4[0] & 0x80) == 0x80) + cdtextPacket = true; + } + + /// Checks if subchannel contains a TEXT packet + /// Subchannel data + /// true if subchannel contains a TEXT packet, false otherwise + static bool CheckCdTextPackets(byte[] subchannel) + { + byte[] cdTextPack1 = new byte[18]; + byte[] cdTextPack2 = new byte[18]; + byte[] cdTextPack3 = new byte[18]; + byte[] cdTextPack4 = new byte[18]; + + int i = 0; + + for(int j = 0; j < 18; j++) + { + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F)); + } + + for(int j = 0; j < 18; j++) + { + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); + + cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); + + if(j < 18) + cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F)); + } + + bool status = true; + + if((cdTextPack1[0] & 0x80) == 0x80) + { + ushort cdTextPack1Crc = BigEndianBitConverter.ToUInt16(cdTextPack1, 16); + byte[] cdTextPack1ForCrc = new byte[16]; + Array.Copy(cdTextPack1, 0, cdTextPack1ForCrc, 0, 16); + ushort calculatedCdtp1Crc = CRC16CCITTContext.Calculate(cdTextPack1ForCrc); + + if(cdTextPack1Crc != calculatedCdtp1Crc && + cdTextPack1Crc != 0) + status = false; + } + + if((cdTextPack2[0] & 0x80) == 0x80) + { + ushort cdTextPack2Crc = BigEndianBitConverter.ToUInt16(cdTextPack2, 16); + byte[] cdTextPack2ForCrc = new byte[16]; + Array.Copy(cdTextPack2, 0, cdTextPack2ForCrc, 0, 16); + ushort calculatedCdtp2Crc = CRC16CCITTContext.Calculate(cdTextPack2ForCrc); + + if(cdTextPack2Crc != calculatedCdtp2Crc && + cdTextPack2Crc != 0) + status = false; + } + + if((cdTextPack3[0] & 0x80) == 0x80) + { + ushort cdTextPack3Crc = BigEndianBitConverter.ToUInt16(cdTextPack3, 16); + byte[] cdTextPack3ForCrc = new byte[16]; + Array.Copy(cdTextPack3, 0, cdTextPack3ForCrc, 0, 16); + ushort calculatedCdtp3Crc = CRC16CCITTContext.Calculate(cdTextPack3ForCrc); + + if(cdTextPack3Crc != calculatedCdtp3Crc && + cdTextPack3Crc != 0) + status = false; + } + + if((cdTextPack4[0] & 0x80) != 0x80) return status; + + ushort cdTextPack4Crc = BigEndianBitConverter.ToUInt16(cdTextPack4, 16); + byte[] cdTextPack4ForCrc = new byte[16]; + Array.Copy(cdTextPack4, 0, cdTextPack4ForCrc, 0, 16); + ushort calculatedCdtp4Crc = CRC16CCITTContext.Calculate(cdTextPack4ForCrc); + + if(cdTextPack4Crc == calculatedCdtp4Crc || + cdTextPack4Crc == 0) + return status; + + return false; + } + + /// Fixes Q subchannel + /// Deinterleaved subchannel data + /// Q subchannel + /// Position in deSub + /// Disc's MCN + /// Track ISRC + /// Set to true if we should fix the CRC, false otherwise + /// Set to true if we fixed the ADR, false otherwise + /// Set to true if we fixed the CONTROL, false otherwise + /// Set to true if we fixed the ZERO, false otherwise + /// Set to true if we fixed the TNO, false otherwise + /// Set to true if we fixed the INDEX, false otherwise + /// Set to true if we fixed the PMIN, PSEC and/or PFRAME, false otherwise + /// Set to true if we fixed the AMIN, ASEC and/or AFRAME, false otherwise + /// Set to true if we fixed the CRC, false otherwise + /// Set to true if we fixed the MCN, false otherwise + /// Set to true if we fixed the ISRC, false otherwise + /// true if it was fixed correctly, false otherwise + static bool FixQSubchannel(byte[] deSub, byte[] q, int subPos, string mcn, string isrc, bool fixCrc, + out bool fixedAdr, out bool controlFix, out bool fixedZero, out bool fixedTno, + out bool fixedIndex, out bool fixedRelPos, out bool fixedAbsPos, out bool fixedCrc, + out bool fixedMcn, out bool fixedIsrc) + { + byte amin, asec, aframe, pmin, psec, pframe; + byte rmin, rsec, rframe; + int aPos, rPos, pPos, dPos; + controlFix = false; + fixedZero = false; + fixedTno = false; + fixedIndex = false; + fixedRelPos = false; + fixedAbsPos = false; + fixedCrc = false; + fixedMcn = false; + fixedIsrc = false; + + byte[] preQ = new byte[12]; + byte[] nextQ = new byte[12]; + Array.Copy(deSub, subPos + 12 - 96, preQ, 0, 12); + Array.Copy(deSub, subPos + 12 + 96, nextQ, 0, 12); + bool status; + + CRC16CCITTContext.Data(preQ, 10, out byte[] preCrc); + bool preCrcOk = preCrc[0] == preQ[10] && preCrc[1] == preQ[11]; + + CRC16CCITTContext.Data(nextQ, 10, out byte[] nextCrc); + bool nextCrcOk = nextCrc[0] == nextQ[10] && nextCrc[1] == nextQ[11]; + + fixedAdr = false; + + // Extraneous bits in ADR + if((q[0] & 0xC) != 0) + { + q[0] &= 0xF3; + fixedAdr = true; } - /// Detect RW packets - /// Subchannel data - /// Set if it contains a ZERO packet - /// Set if it contains a GRAPHICS, EXTENDED GRAPHICS or MIDI packet - /// Set if it contains a TEXT packet - static void DetectRwPackets(byte[] subchannel, out bool zero, out bool rwPacket, out bool cdtextPacket) + CRC16CCITTContext.Data(q, 10, out byte[] qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(fixedAdr && status) + return true; + + int oldAdr = q[0] & 0x3; + + // Try Q-Mode 1 + q[0] = (byte)((q[0] & 0xF0) + 1); + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) { - zero = false; - rwPacket = false; - cdtextPacket = false; + fixedAdr = true; - byte[] cdTextPack1 = new byte[18]; - byte[] cdTextPack2 = new byte[18]; - byte[] cdTextPack3 = new byte[18]; - byte[] cdTextPack4 = new byte[18]; - byte[] cdSubRwPack1 = new byte[24]; - byte[] cdSubRwPack2 = new byte[24]; - byte[] cdSubRwPack3 = new byte[24]; - byte[] cdSubRwPack4 = new byte[24]; - - int i = 0; - - for(int j = 0; j < 18; j++) - { - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F)); - } - - i = 0; - - for(int j = 0; j < 24; j++) - cdSubRwPack1[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack2[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack3[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack4[j] = (byte)(subchannel[i++] & 0x3F); - - switch(cdSubRwPack1[0]) - { - case 0x00: - zero = true; - - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x18: - case 0x38: - rwPacket = true; - - break; - case 0x14: - cdtextPacket = true; - - break; - } - - switch(cdSubRwPack2[0]) - { - case 0x00: - zero = true; - - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x18: - case 0x38: - rwPacket = true; - - break; - case 0x14: - cdtextPacket = true; - - break; - } - - switch(cdSubRwPack3[0]) - { - case 0x00: - zero = true; - - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x18: - case 0x38: - rwPacket = true; - - break; - case 0x14: - cdtextPacket = true; - - break; - } - - switch(cdSubRwPack4[0]) - { - case 0x00: - zero = true; - - break; - case 0x08: - case 0x09: - case 0x0A: - case 0x18: - case 0x38: - rwPacket = true; - - break; - case 0x14: - cdtextPacket = true; - - break; - } - - if((cdTextPack1[0] & 0x80) == 0x80) - cdtextPacket = true; - - if((cdTextPack2[0] & 0x80) == 0x80) - cdtextPacket = true; - - if((cdTextPack3[0] & 0x80) == 0x80) - cdtextPacket = true; - - if((cdTextPack4[0] & 0x80) == 0x80) - cdtextPacket = true; + return true; } - /// Checks if subchannel contains a TEXT packet - /// Subchannel data - /// true if subchannel contains a TEXT packet, false otherwise - static bool CheckCdTextPackets(byte[] subchannel) + // Try Q-Mode 2 + q[0] = (byte)((q[0] & 0xF0) + 2); + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) { - byte[] cdTextPack1 = new byte[18]; - byte[] cdTextPack2 = new byte[18]; - byte[] cdTextPack3 = new byte[18]; - byte[] cdTextPack4 = new byte[18]; + fixedAdr = true; - int i = 0; - - for(int j = 0; j < 18; j++) - { - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack1[j] = (byte)(cdTextPack1[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack2[j] = (byte)(cdTextPack2[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack3[j] = (byte)(cdTextPack3[j] | (subchannel[i++] & 0x3F)); - } - - for(int j = 0; j < 18; j++) - { - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x3F) << 2)); - - cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0xC0) >> 4)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x0F) << 4)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j++] | ((subchannel[i] & 0x3C) >> 2)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | ((subchannel[i++] & 0x03) << 6)); - - if(j < 18) - cdTextPack4[j] = (byte)(cdTextPack4[j] | (subchannel[i++] & 0x3F)); - } - - bool status = true; - - if((cdTextPack1[0] & 0x80) == 0x80) - { - ushort cdTextPack1Crc = BigEndianBitConverter.ToUInt16(cdTextPack1, 16); - byte[] cdTextPack1ForCrc = new byte[16]; - Array.Copy(cdTextPack1, 0, cdTextPack1ForCrc, 0, 16); - ushort calculatedCdtp1Crc = CRC16CCITTContext.Calculate(cdTextPack1ForCrc); - - if(cdTextPack1Crc != calculatedCdtp1Crc && - cdTextPack1Crc != 0) - status = false; - } - - if((cdTextPack2[0] & 0x80) == 0x80) - { - ushort cdTextPack2Crc = BigEndianBitConverter.ToUInt16(cdTextPack2, 16); - byte[] cdTextPack2ForCrc = new byte[16]; - Array.Copy(cdTextPack2, 0, cdTextPack2ForCrc, 0, 16); - ushort calculatedCdtp2Crc = CRC16CCITTContext.Calculate(cdTextPack2ForCrc); - - if(cdTextPack2Crc != calculatedCdtp2Crc && - cdTextPack2Crc != 0) - status = false; - } - - if((cdTextPack3[0] & 0x80) == 0x80) - { - ushort cdTextPack3Crc = BigEndianBitConverter.ToUInt16(cdTextPack3, 16); - byte[] cdTextPack3ForCrc = new byte[16]; - Array.Copy(cdTextPack3, 0, cdTextPack3ForCrc, 0, 16); - ushort calculatedCdtp3Crc = CRC16CCITTContext.Calculate(cdTextPack3ForCrc); - - if(cdTextPack3Crc != calculatedCdtp3Crc && - cdTextPack3Crc != 0) - status = false; - } - - if((cdTextPack4[0] & 0x80) != 0x80) - return status; - - ushort cdTextPack4Crc = BigEndianBitConverter.ToUInt16(cdTextPack4, 16); - byte[] cdTextPack4ForCrc = new byte[16]; - Array.Copy(cdTextPack4, 0, cdTextPack4ForCrc, 0, 16); - ushort calculatedCdtp4Crc = CRC16CCITTContext.Calculate(cdTextPack4ForCrc); - - if(cdTextPack4Crc == calculatedCdtp4Crc || - cdTextPack4Crc == 0) - return status; - - return false; + return true; } - /// Fixes Q subchannel - /// Deinterleaved subchannel data - /// Q subchannel - /// Position in deSub - /// Disc's MCN - /// Track ISRC - /// Set to true if we should fix the CRC, false otherwise - /// Set to true if we fixed the ADR, false otherwise - /// Set to true if we fixed the CONTROL, false otherwise - /// Set to true if we fixed the ZERO, false otherwise - /// Set to true if we fixed the TNO, false otherwise - /// Set to true if we fixed the INDEX, false otherwise - /// Set to true if we fixed the PMIN, PSEC and/or PFRAME, false otherwise - /// Set to true if we fixed the AMIN, ASEC and/or AFRAME, false otherwise - /// Set to true if we fixed the CRC, false otherwise - /// Set to true if we fixed the MCN, false otherwise - /// Set to true if we fixed the ISRC, false otherwise - /// true if it was fixed correctly, false otherwise - static bool FixQSubchannel(byte[] deSub, byte[] q, int subPos, string mcn, string isrc, bool fixCrc, - out bool fixedAdr, out bool controlFix, out bool fixedZero, out bool fixedTno, - out bool fixedIndex, out bool fixedRelPos, out bool fixedAbsPos, out bool fixedCrc, - out bool fixedMcn, out bool fixedIsrc) + // Try Q-Mode 3 + q[0] = (byte)((q[0] & 0xF0) + 3); + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) { - byte amin, asec, aframe, pmin, psec, pframe; - byte rmin, rsec, rframe; - int aPos, rPos, pPos, dPos; - controlFix = false; - fixedZero = false; - fixedTno = false; - fixedIndex = false; - fixedRelPos = false; - fixedAbsPos = false; - fixedCrc = false; - fixedMcn = false; - fixedIsrc = false; + fixedAdr = true; - byte[] preQ = new byte[12]; - byte[] nextQ = new byte[12]; - Array.Copy(deSub, subPos + 12 - 96, preQ, 0, 12); - Array.Copy(deSub, subPos + 12 + 96, nextQ, 0, 12); - bool status; + return true; + } - CRC16CCITTContext.Data(preQ, 10, out byte[] preCrc); - bool preCrcOk = preCrc[0] == preQ[10] && preCrc[1] == preQ[11]; + q[0] = (byte)((q[0] & 0xF0) + oldAdr); - CRC16CCITTContext.Data(nextQ, 10, out byte[] nextCrc); - bool nextCrcOk = nextCrc[0] == nextQ[10] && nextCrc[1] == nextQ[11]; + oldAdr = q[0]; - fixedAdr = false; + // Try using previous control + if(preCrcOk && (q[0] & 0xF0) != (preQ[0] & 0xF0)) + { + q[0] = (byte)((q[0] & 0x03) + (preQ[0] & 0xF0)); - // Extraneous bits in ADR - if((q[0] & 0xC) != 0) - { - q[0] &= 0xF3; - fixedAdr = true; - } - - CRC16CCITTContext.Data(q, 10, out byte[] qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(fixedAdr && status) - return true; - - int oldAdr = q[0] & 0x3; - - // Try Q-Mode 1 - q[0] = (byte)((q[0] & 0xF0) + 1); CRC16CCITTContext.Data(q, 10, out qCrc); status = qCrc[0] == q[10] && qCrc[1] == q[11]; if(status) { - fixedAdr = true; - - return true; - } - - // Try Q-Mode 2 - q[0] = (byte)((q[0] & 0xF0) + 2); - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - fixedAdr = true; - - return true; - } - - // Try Q-Mode 3 - q[0] = (byte)((q[0] & 0xF0) + 3); - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - fixedAdr = true; - - return true; - } - - q[0] = (byte)((q[0] & 0xF0) + oldAdr); - - oldAdr = q[0]; - - // Try using previous control - if(preCrcOk && (q[0] & 0xF0) != (preQ[0] & 0xF0)) - { - q[0] = (byte)((q[0] & 0x03) + (preQ[0] & 0xF0)); - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - controlFix = true; - - return true; - } - - q[0] = (byte)oldAdr; - } - - // Try using next control - if(nextCrcOk && (q[0] & 0xF0) != (nextQ[0] & 0xF0)) - { - q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0)); - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - { - controlFix = true; - - return true; - } - - q[0] = (byte)oldAdr; - } - - if(preCrcOk && - nextCrcOk && - (nextQ[0] & 0xF0) == (preQ[0] & 0xF0) && - (q[0] & 0xF0) != (nextQ[0] & 0xF0)) - { - q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0)); - controlFix = true; + + return true; } - switch(q[0] & 0x3) + q[0] = (byte)oldAdr; + } + + // Try using next control + if(nextCrcOk && (q[0] & 0xF0) != (nextQ[0] & 0xF0)) + { + q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0)); + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) { - // Positioning - case 1: + controlFix = true; + + return true; + } + + q[0] = (byte)oldAdr; + } + + if(preCrcOk && + nextCrcOk && + (nextQ[0] & 0xF0) == (preQ[0] & 0xF0) && + (q[0] & 0xF0) != (nextQ[0] & 0xF0)) + { + q[0] = (byte)((q[0] & 0x03) + (nextQ[0] & 0xF0)); + + controlFix = true; + } + + switch(q[0] & 0x3) + { + // Positioning + case 1: + { + // ZERO not zero + if(q[6] != 0) { - // ZERO not zero - if(q[6] != 0) + q[6] = 0; + fixedZero = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + + if(preCrcOk && nextCrcOk) + { + if(preQ[1] == nextQ[1] && + preQ[1] != q[1]) { - q[6] = 0; - fixedZero = true; + q[1] = preQ[1]; + fixedTno = true; CRC16CCITTContext.Data(q, 10, out qCrc); status = qCrc[0] == q[10] && qCrc[1] == q[11]; @@ -1005,216 +1020,84 @@ namespace Aaru.Core.Media if(status) return true; } + } - if(preCrcOk && nextCrcOk) + if(preCrcOk && nextCrcOk) + { + if(preQ[2] == nextQ[2] && + preQ[2] != q[2]) { - if(preQ[1] == nextQ[1] && - preQ[1] != q[1]) - { - q[1] = preQ[1]; - fixedTno = true; + q[2] = preQ[2]; + fixedIndex = true; - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; - if(status) - return true; - } + if(status) + return true; } + } - if(preCrcOk && nextCrcOk) + amin = (byte)((q[7] / 16 * 10) + (q[7] & 0x0F)); + asec = (byte)((q[8] / 16 * 10) + (q[8] & 0x0F)); + aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); + aPos = (amin * 60 * 75) + (asec * 75) + aframe - 150; + + pmin = (byte)((q[3] / 16 * 10) + (q[3] & 0x0F)); + psec = (byte)((q[4] / 16 * 10) + (q[4] & 0x0F)); + pframe = (byte)((q[5] / 16 * 10) + (q[5] & 0x0F)); + pPos = (pmin * 60 * 75) + (psec * 75) + pframe; + + // TODO: pregap + // Not pregap + if(q[2] > 0) + { + // Previous was not pregap either + if(preQ[2] > 0 && preCrcOk) { - if(preQ[2] == nextQ[2] && - preQ[2] != q[2]) - { - q[2] = preQ[2]; - fixedIndex = true; + rmin = (byte)((preQ[3] / 16 * 10) + (preQ[3] & 0x0F)); + rsec = (byte)((preQ[4] / 16 * 10) + (preQ[4] & 0x0F)); + rframe = (byte)((preQ[5] / 16 * 10) + (preQ[5] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - amin = (byte)((q[7] / 16 * 10) + (q[7] & 0x0F)); - asec = (byte)((q[8] / 16 * 10) + (q[8] & 0x0F)); - aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); - aPos = (amin * 60 * 75) + (asec * 75) + aframe - 150; - - pmin = (byte)((q[3] / 16 * 10) + (q[3] & 0x0F)); - psec = (byte)((q[4] / 16 * 10) + (q[4] & 0x0F)); - pframe = (byte)((q[5] / 16 * 10) + (q[5] & 0x0F)); - pPos = (pmin * 60 * 75) + (psec * 75) + pframe; - - // TODO: pregap - // Not pregap - if(q[2] > 0) - { - // Previous was not pregap either - if(preQ[2] > 0 && preCrcOk) - { - rmin = (byte)((preQ[3] / 16 * 10) + (preQ[3] & 0x0F)); - rsec = (byte)((preQ[4] / 16 * 10) + (preQ[4] & 0x0F)); - rframe = (byte)((preQ[5] / 16 * 10) + (preQ[5] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - - dPos = pPos - rPos; - - if(dPos != 1) - { - q[3] = preQ[3]; - q[4] = preQ[4]; - q[5] = preQ[5]; - - // BCD add 1, so 0x39 becomes 0x40 - if((q[5] & 0xF) == 9) - q[5] += 7; - else - q[5]++; - - // 74 frames, so from 0x00 to 0x74, BCD - if(q[5] >= 0x74) - { - // 0 frames - q[5] = 0; - - // Add 1 second - if((q[4] & 0xF) == 9) - q[4] += 7; - else - q[4]++; - - // 60 seconds, so from 0x00 to 0x59, BCD - if(q[4] >= 0x59) - { - // 0 seconds - q[4] = 0; - - // Add 1 minute - q[3]++; - } - } - - fixedRelPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - // Next is not pregap and we didn't fix relative position with previous - if(nextQ[2] > 0 && - nextCrcOk && - !fixedRelPos) - { - rmin = (byte)((nextQ[3] / 16 * 10) + (nextQ[3] & 0x0F)); - rsec = (byte)((nextQ[4] / 16 * 10) + (nextQ[4] & 0x0F)); - rframe = (byte)((nextQ[5] / 16 * 10) + (nextQ[5] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - - dPos = rPos - pPos; - - if(dPos != 1) - { - q[3] = nextQ[3]; - q[4] = nextQ[4]; - q[5] = nextQ[5]; - - // If frames is 0 - if(q[5] == 0) - { - // If seconds is 0 - if(q[4] == 0) - { - // BCD decrease minutes - if((q[3] & 0xF) == 0) - q[3] = (byte)((q[3] & 0xF0) - 0x10); - else - q[3]--; - - q[4] = 0x59; - q[5] = 0x73; - } - else - { - // BCD decrease seconds - if((q[4] & 0xF) == 0) - q[4] = (byte)((q[4] & 0xF0) - 0x10); - else - q[4]--; - - q[5] = 0x73; - } - } - - // BCD decrease frames - else if((q[5] & 0xF) == 0) - q[5] = (byte)((q[5] & 0xF0) - 0x10); - else - q[5]--; - - fixedRelPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - } - - // Previous Q's CRC is correct - if(preCrcOk) - { - rmin = (byte)((preQ[7] / 16 * 10) + (preQ[7] & 0x0F)); - rsec = (byte)((preQ[8] / 16 * 10) + (preQ[8] & 0x0F)); - rframe = (byte)((preQ[9] / 16 * 10) + (preQ[9] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe - 150; - - dPos = aPos - rPos; + dPos = pPos - rPos; if(dPos != 1) { - q[7] = preQ[7]; - q[8] = preQ[8]; - q[9] = preQ[9]; + q[3] = preQ[3]; + q[4] = preQ[4]; + q[5] = preQ[5]; // BCD add 1, so 0x39 becomes 0x40 - if((q[9] & 0xF) == 9) - q[9] += 7; + if((q[5] & 0xF) == 9) + q[5] += 7; else - q[9]++; + q[5]++; // 74 frames, so from 0x00 to 0x74, BCD - if(q[9] >= 0x74) + if(q[5] >= 0x74) { // 0 frames - q[9] = 0; + q[5] = 0; // Add 1 second - if((q[8] & 0xF) == 9) - q[8] += 7; + if((q[4] & 0xF) == 9) + q[4] += 7; else - q[8]++; + q[4]++; // 60 seconds, so from 0x00 to 0x59, BCD - if(q[8] >= 0x59) + if(q[4] >= 0x59) { // 0 seconds - q[8] = 0; + q[4] = 0; // Add 1 minute - q[7]++; + q[3]++; } } - fixedAbsPos = true; + fixedRelPos = true; CRC16CCITTContext.Data(q, 10, out qCrc); status = qCrc[0] == q[10] && qCrc[1] == q[11]; @@ -1227,121 +1110,8 @@ namespace Aaru.Core.Media // Next is not pregap and we didn't fix relative position with previous if(nextQ[2] > 0 && nextCrcOk && - !fixedAbsPos) + !fixedRelPos) { - rmin = (byte)((nextQ[7] / 16 * 10) + (nextQ[7] & 0x0F)); - rsec = (byte)((nextQ[8] / 16 * 10) + (nextQ[8] & 0x0F)); - rframe = (byte)((nextQ[9] / 16 * 10) + (nextQ[9] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe - 150; - - dPos = rPos - pPos; - - if(dPos != 1) - { - q[7] = nextQ[7]; - q[8] = nextQ[8]; - q[9] = nextQ[9]; - - // If frames is 0 - if(q[9] == 0) - { - // If seconds is 0 - if(q[8] == 0) - { - // BCD decrease minutes - if((q[7] & 0xF) == 0) - q[7] = (byte)((q[7] & 0xF0) - 0x10); - else - q[7]--; - - q[8] = 0x59; - q[9] = 0x73; - } - else - { - // BCD decrease seconds - if((q[8] & 0xF) == 0) - q[8] = (byte)((q[8] & 0xF0) - 0x10); - else - q[8]--; - - q[9] = 0x73; - } - } - - // BCD decrease frames - else if((q[9] & 0xF) == 0) - q[9] = (byte)((q[9] & 0xF0) - 0x10); - else - q[9]--; - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - // Game Over - if(!fixCrc || status) - return false; - - // Previous Q's CRC is correct - if(preCrcOk) - { - rmin = (byte)((preQ[7] / 16 * 10) + (preQ[7] & 0x0F)); - rsec = (byte)((preQ[8] / 16 * 10) + (preQ[8] & 0x0F)); - rframe = (byte)((preQ[9] / 16 * 10) + (preQ[9] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe - 150; - - dPos = aPos - rPos; - - bool absOk = dPos == 1; - - rmin = (byte)((preQ[3] / 16 * 10) + (preQ[3] & 0x0F)); - rsec = (byte)((preQ[4] / 16 * 10) + (preQ[4] & 0x0F)); - rframe = (byte)((preQ[5] / 16 * 10) + (preQ[5] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - - dPos = pPos - rPos; - - bool relOk = dPos == 1; - - if(q[0] != preQ[0] || - q[1] != preQ[1] || - q[2] != preQ[2] || - q[6] != 0 || - !absOk || - !relOk) - return false; - - CRC16CCITTContext.Data(q, 10, out qCrc); - q[10] = qCrc[0]; - q[11] = qCrc[1]; - - fixedCrc = true; - - return true; - } - - // Next Q's CRC is correct - if(nextCrcOk) - { - rmin = (byte)((nextQ[7] / 16 * 10) + (nextQ[7] & 0x0F)); - rsec = (byte)((nextQ[8] / 16 * 10) + (nextQ[8] & 0x0F)); - rframe = (byte)((nextQ[9] / 16 * 10) + (nextQ[9] & 0x0F)); - rPos = (rmin * 60 * 75) + (rsec * 75) + rframe - 150; - - dPos = rPos - aPos; - - bool absOk = dPos == 1; - rmin = (byte)((nextQ[3] / 16 * 10) + (nextQ[3] & 0x0F)); rsec = (byte)((nextQ[4] / 16 * 10) + (nextQ[4] & 0x0F)); rframe = (byte)((nextQ[5] / 16 * 10) + (nextQ[5] & 0x0F)); @@ -1349,100 +1119,102 @@ namespace Aaru.Core.Media dPos = rPos - pPos; - bool relOk = dPos == 1; + if(dPos != 1) + { + q[3] = nextQ[3]; + q[4] = nextQ[4]; + q[5] = nextQ[5]; - if(q[0] != nextQ[0] || - q[1] != nextQ[1] || - q[2] != nextQ[2] || - q[6] != 0 || - !absOk || - !relOk) - return false; + // If frames is 0 + if(q[5] == 0) + { + // If seconds is 0 + if(q[4] == 0) + { + // BCD decrease minutes + if((q[3] & 0xF) == 0) + q[3] = (byte)((q[3] & 0xF0) - 0x10); + else + q[3]--; - CRC16CCITTContext.Data(q, 10, out qCrc); - q[10] = qCrc[0]; - q[11] = qCrc[1]; + q[4] = 0x59; + q[5] = 0x73; + } + else + { + // BCD decrease seconds + if((q[4] & 0xF) == 0) + q[4] = (byte)((q[4] & 0xF0) - 0x10); + else + q[4]--; - fixedCrc = true; + q[5] = 0x73; + } + } - return true; + // BCD decrease frames + else if((q[5] & 0xF) == 0) + q[5] = (byte)((q[5] & 0xF0) - 0x10); + else + q[5]--; + + fixedRelPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } } - - // Ok if previous and next are both BAD I won't rewrite the CRC at all - break; } - // MCN - case 2: + // Previous Q's CRC is correct + if(preCrcOk) { - // Previous Q's CRC is correct - if(preCrcOk) - { - rframe = (byte)((preQ[9] / 16 * 10) + (preQ[9] & 0x0F)); - aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); + rmin = (byte)((preQ[7] / 16 * 10) + (preQ[7] & 0x0F)); + rsec = (byte)((preQ[8] / 16 * 10) + (preQ[8] & 0x0F)); + rframe = (byte)((preQ[9] / 16 * 10) + (preQ[9] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe - 150; - if(aframe - rframe != 1) + dPos = aPos - rPos; + + if(dPos != 1) + { + q[7] = preQ[7]; + q[8] = preQ[8]; + q[9] = preQ[9]; + + // BCD add 1, so 0x39 becomes 0x40 + if((q[9] & 0xF) == 9) + q[9] += 7; + else + q[9]++; + + // 74 frames, so from 0x00 to 0x74, BCD + if(q[9] >= 0x74) { - q[9] = preQ[9]; + // 0 frames + q[9] = 0; - if((q[9] & 0xF) == 9) - q[9] += 7; + // Add 1 second + if((q[8] & 0xF) == 9) + q[8] += 7; else - q[9]++; + q[8]++; - if(q[9] >= 0x74) - q[9] = 0; + // 60 seconds, so from 0x00 to 0x59, BCD + if(q[8] >= 0x59) + { + // 0 seconds + q[8] = 0; - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; + // Add 1 minute + q[7]++; + } } - } - // Next Q's CRC is correct - else if(nextCrcOk) - { - rframe = (byte)((nextQ[9] / 16 * 10) + (nextQ[9] & 0x0F)); - aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); - - if(aframe - rframe != 1) - { - q[9] = nextQ[9]; - - if(q[9] == 0) - q[9] = 0x73; - else if((q[9] & 0xF) == 0) - q[9] = (byte)((q[9] & 0xF0) - 0x10); - else - q[9]--; - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - // We know the MCN - if(mcn != null) - { - q[1] = (byte)((((mcn[0] - 0x30) & 0x0F) * 16) + ((mcn[1] - 0x30) & 0x0F)); - q[2] = (byte)((((mcn[2] - 0x30) & 0x0F) * 16) + ((mcn[3] - 0x30) & 0x0F)); - q[3] = (byte)((((mcn[4] - 0x30) & 0x0F) * 16) + ((mcn[5] - 0x30) & 0x0F)); - q[4] = (byte)((((mcn[6] - 0x30) & 0x0F) * 16) + ((mcn[7] - 0x30) & 0x0F)); - q[5] = (byte)((((mcn[8] - 0x30) & 0x0F) * 16) + ((mcn[9] - 0x30) & 0x0F)); - q[6] = (byte)((((mcn[10] - 0x30) & 0x0F) * 16) + ((mcn[11] - 0x30) & 0x0F)); - q[7] = (byte)(((mcn[12] - 0x30) & 0x0F) * 8); - q[8] = 0; - - fixedMcn = true; + fixedAbsPos = true; CRC16CCITTContext.Data(q, 10, out qCrc); status = qCrc[0] == q[10] && qCrc[1] == q[11]; @@ -1450,10 +1222,103 @@ namespace Aaru.Core.Media if(status) return true; } + } - if(!fixCrc || - !nextCrcOk || - !preCrcOk) + // Next is not pregap and we didn't fix relative position with previous + if(nextQ[2] > 0 && + nextCrcOk && + !fixedAbsPos) + { + rmin = (byte)((nextQ[7] / 16 * 10) + (nextQ[7] & 0x0F)); + rsec = (byte)((nextQ[8] / 16 * 10) + (nextQ[8] & 0x0F)); + rframe = (byte)((nextQ[9] / 16 * 10) + (nextQ[9] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe - 150; + + dPos = rPos - pPos; + + if(dPos != 1) + { + q[7] = nextQ[7]; + q[8] = nextQ[8]; + q[9] = nextQ[9]; + + // If frames is 0 + if(q[9] == 0) + { + // If seconds is 0 + if(q[8] == 0) + { + // BCD decrease minutes + if((q[7] & 0xF) == 0) + q[7] = (byte)((q[7] & 0xF0) - 0x10); + else + q[7]--; + + q[8] = 0x59; + q[9] = 0x73; + } + else + { + // BCD decrease seconds + if((q[8] & 0xF) == 0) + q[8] = (byte)((q[8] & 0xF0) - 0x10); + else + q[8]--; + + q[9] = 0x73; + } + } + + // BCD decrease frames + else if((q[9] & 0xF) == 0) + q[9] = (byte)((q[9] & 0xF0) - 0x10); + else + q[9]--; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + // Game Over + if(!fixCrc || status) + return false; + + // Previous Q's CRC is correct + if(preCrcOk) + { + rmin = (byte)((preQ[7] / 16 * 10) + (preQ[7] & 0x0F)); + rsec = (byte)((preQ[8] / 16 * 10) + (preQ[8] & 0x0F)); + rframe = (byte)((preQ[9] / 16 * 10) + (preQ[9] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe - 150; + + dPos = aPos - rPos; + + bool absOk = dPos == 1; + + rmin = (byte)((preQ[3] / 16 * 10) + (preQ[3] & 0x0F)); + rsec = (byte)((preQ[4] / 16 * 10) + (preQ[4] & 0x0F)); + rframe = (byte)((preQ[5] / 16 * 10) + (preQ[5] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; + + dPos = pPos - rPos; + + bool relOk = dPos == 1; + + if(q[0] != preQ[0] || + q[1] != preQ[1] || + q[2] != preQ[2] || + q[6] != 0 || + !absOk || + !relOk) return false; CRC16CCITTContext.Data(q, 10, out qCrc); @@ -1465,94 +1330,33 @@ namespace Aaru.Core.Media return true; } - // ISRC - case 3: + // Next Q's CRC is correct + if(nextCrcOk) { - // Previous Q's CRC is correct - if(preCrcOk) - { - rframe = (byte)((preQ[9] / 16 * 10) + (preQ[9] & 0x0F)); - aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); + rmin = (byte)((nextQ[7] / 16 * 10) + (nextQ[7] & 0x0F)); + rsec = (byte)((nextQ[8] / 16 * 10) + (nextQ[8] & 0x0F)); + rframe = (byte)((nextQ[9] / 16 * 10) + (nextQ[9] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe - 150; - if(aframe - rframe != 1) - { - q[9] = preQ[9]; + dPos = rPos - aPos; - if((q[9] & 0xF) == 9) - q[9] += 7; - else - q[9]++; + bool absOk = dPos == 1; - if(q[9] >= 0x74) - q[9] = 0; + rmin = (byte)((nextQ[3] / 16 * 10) + (nextQ[3] & 0x0F)); + rsec = (byte)((nextQ[4] / 16 * 10) + (nextQ[4] & 0x0F)); + rframe = (byte)((nextQ[5] / 16 * 10) + (nextQ[5] & 0x0F)); + rPos = (rmin * 60 * 75) + (rsec * 75) + rframe; - fixedAbsPos = true; + dPos = rPos - pPos; - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; + bool relOk = dPos == 1; - if(status) - return true; - } - } - - // Next Q's CRC is correct - else if(nextCrcOk) - { - rframe = (byte)((nextQ[9] / 16 * 10) + (nextQ[9] & 0x0F)); - aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); - - if(aframe - rframe != 1) - { - q[9] = nextQ[9]; - - if(q[9] == 0) - q[9] = 0x73; - else if((q[9] & 0xF) == 0) - q[9] = (byte)((q[9] & 0xF0) - 0x10); - else - q[9]--; - - fixedAbsPos = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - } - - // We know the ISRC - if(isrc != null) - { - byte i1 = Subchannel.GetIsrcCode(isrc[0]); - byte i2 = Subchannel.GetIsrcCode(isrc[1]); - byte i3 = Subchannel.GetIsrcCode(isrc[2]); - byte i4 = Subchannel.GetIsrcCode(isrc[3]); - byte i5 = Subchannel.GetIsrcCode(isrc[4]); - - q[1] = (byte)((i1 << 2) + ((i2 & 0x30) >> 4)); - q[2] = (byte)(((i2 & 0xF) << 4) + (i3 >> 2)); - q[3] = (byte)(((i3 & 0x3) << 6) + i4); - q[4] = (byte)(i5 << 2); - q[5] = (byte)((((isrc[5] - 0x30) & 0x0F) * 16) + ((isrc[6] - 0x30) & 0x0F)); - q[6] = (byte)((((isrc[7] - 0x30) & 0x0F) * 16) + ((isrc[8] - 0x30) & 0x0F)); - q[7] = (byte)((((isrc[9] - 0x30) & 0x0F) * 16) + ((isrc[10] - 0x30) & 0x0F)); - q[8] = (byte)(((isrc[11] - 0x30) & 0x0F) * 16); - - fixedIsrc = true; - - CRC16CCITTContext.Data(q, 10, out qCrc); - status = qCrc[0] == q[10] && qCrc[1] == q[11]; - - if(status) - return true; - } - - if(!fixCrc || - !nextCrcOk || - !preCrcOk) + if(q[0] != nextQ[0] || + q[1] != nextQ[1] || + q[2] != nextQ[2] || + q[6] != 0 || + !absOk || + !relOk) return false; CRC16CCITTContext.Data(q, 10, out qCrc); @@ -1563,79 +1367,274 @@ namespace Aaru.Core.Media return true; } + + // Ok if previous and next are both BAD I won't rewrite the CRC at all + break; } - return false; - } - - /// Generates a correct subchannel all the missing ones - /// List of missing subchannels - /// List of tracks - /// Flags of tracks - /// Disc size - /// Subchannel log - /// Dump log - /// Progress initialization callback - /// Progress update callback - /// Progress finalization callback - /// Output image - public static void GenerateSubchannels(HashSet subchannelExtents, Track[] tracks, - Dictionary trackFlags, ulong blocks, SubchannelLog subLog, - DumpLog dumpLog, InitProgressHandler initProgress, - UpdateProgressHandler updateProgress, EndProgressHandler endProgress, - IWritableImage outputPlugin) - { - initProgress?.Invoke(); - - foreach(int sector in subchannelExtents) + // MCN + case 2: { - Track track = tracks.LastOrDefault(t => (int)t.StartSector <= sector); - byte trkFlags; - byte flags; - ulong trackStart; - ulong pregap; - - if(track == null) - continue; - - // Hidden track - if(track.Sequence == 0) + // Previous Q's CRC is correct + if(preCrcOk) { - track = tracks.FirstOrDefault(t => (int)t.Sequence == 1); - trackStart = 0; - pregap = track?.StartSector ?? 0; - } - else - { - trackStart = track.StartSector; - pregap = track.Pregap; + rframe = (byte)((preQ[9] / 16 * 10) + (preQ[9] & 0x0F)); + aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); + + if(aframe - rframe != 1) + { + q[9] = preQ[9]; + + if((q[9] & 0xF) == 9) + q[9] += 7; + else + q[9]++; + + if(q[9] >= 0x74) + q[9] = 0; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } } - if(!trackFlags.TryGetValue((byte)(track?.Sequence ?? 0), out trkFlags) && - track?.Type != TrackType.Audio) - flags = (byte)CdFlags.DataTrack; - else - flags = trkFlags; + // Next Q's CRC is correct + else if(nextCrcOk) + { + rframe = (byte)((nextQ[9] / 16 * 10) + (nextQ[9] & 0x0F)); + aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); - byte index; + if(aframe - rframe != 1) + { + q[9] = nextQ[9]; - if(track?.Indexes?.Count > 0) - index = (byte)track.Indexes.LastOrDefault(i => i.Value >= sector).Key; - else - index = 0; + if(q[9] == 0) + q[9] = 0x73; + else if((q[9] & 0xF) == 0) + q[9] = (byte)((q[9] & 0xF0) - 0x10); + else + q[9]--; - updateProgress?.Invoke($"Generating subchannel for sector {sector}...", sector, (long)blocks); - dumpLog?.WriteLine($"Generating subchannel for sector {sector}."); + fixedAbsPos = true; - byte[] sub = Subchannel.Generate(sector, track?.Sequence ?? 0, (int)pregap, (int)trackStart, flags, - index); + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; - outputPlugin.WriteSectorsTag(sub, (ulong)sector, 1, SectorTagType.CdSectorSubchannel); + if(status) + return true; + } + } - subLog?.WriteEntry(sub, true, sector, 1, true, false); + // We know the MCN + if(mcn != null) + { + q[1] = (byte)((((mcn[0] - 0x30) & 0x0F) * 16) + ((mcn[1] - 0x30) & 0x0F)); + q[2] = (byte)((((mcn[2] - 0x30) & 0x0F) * 16) + ((mcn[3] - 0x30) & 0x0F)); + q[3] = (byte)((((mcn[4] - 0x30) & 0x0F) * 16) + ((mcn[5] - 0x30) & 0x0F)); + q[4] = (byte)((((mcn[6] - 0x30) & 0x0F) * 16) + ((mcn[7] - 0x30) & 0x0F)); + q[5] = (byte)((((mcn[8] - 0x30) & 0x0F) * 16) + ((mcn[9] - 0x30) & 0x0F)); + q[6] = (byte)((((mcn[10] - 0x30) & 0x0F) * 16) + ((mcn[11] - 0x30) & 0x0F)); + q[7] = (byte)(((mcn[12] - 0x30) & 0x0F) * 8); + q[8] = 0; + + fixedMcn = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + + if(!fixCrc || + !nextCrcOk || + !preCrcOk) + return false; + + CRC16CCITTContext.Data(q, 10, out qCrc); + q[10] = qCrc[0]; + q[11] = qCrc[1]; + + fixedCrc = true; + + return true; } - endProgress?.Invoke(); + // ISRC + case 3: + { + // Previous Q's CRC is correct + if(preCrcOk) + { + rframe = (byte)((preQ[9] / 16 * 10) + (preQ[9] & 0x0F)); + aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); + + if(aframe - rframe != 1) + { + q[9] = preQ[9]; + + if((q[9] & 0xF) == 9) + q[9] += 7; + else + q[9]++; + + if(q[9] >= 0x74) + q[9] = 0; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + // Next Q's CRC is correct + else if(nextCrcOk) + { + rframe = (byte)((nextQ[9] / 16 * 10) + (nextQ[9] & 0x0F)); + aframe = (byte)((q[9] / 16 * 10) + (q[9] & 0x0F)); + + if(aframe - rframe != 1) + { + q[9] = nextQ[9]; + + if(q[9] == 0) + q[9] = 0x73; + else if((q[9] & 0xF) == 0) + q[9] = (byte)((q[9] & 0xF0) - 0x10); + else + q[9]--; + + fixedAbsPos = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + } + + // We know the ISRC + if(isrc != null) + { + byte i1 = Subchannel.GetIsrcCode(isrc[0]); + byte i2 = Subchannel.GetIsrcCode(isrc[1]); + byte i3 = Subchannel.GetIsrcCode(isrc[2]); + byte i4 = Subchannel.GetIsrcCode(isrc[3]); + byte i5 = Subchannel.GetIsrcCode(isrc[4]); + + q[1] = (byte)((i1 << 2) + ((i2 & 0x30) >> 4)); + q[2] = (byte)(((i2 & 0xF) << 4) + (i3 >> 2)); + q[3] = (byte)(((i3 & 0x3) << 6) + i4); + q[4] = (byte)(i5 << 2); + q[5] = (byte)((((isrc[5] - 0x30) & 0x0F) * 16) + ((isrc[6] - 0x30) & 0x0F)); + q[6] = (byte)((((isrc[7] - 0x30) & 0x0F) * 16) + ((isrc[8] - 0x30) & 0x0F)); + q[7] = (byte)((((isrc[9] - 0x30) & 0x0F) * 16) + ((isrc[10] - 0x30) & 0x0F)); + q[8] = (byte)(((isrc[11] - 0x30) & 0x0F) * 16); + + fixedIsrc = true; + + CRC16CCITTContext.Data(q, 10, out qCrc); + status = qCrc[0] == q[10] && qCrc[1] == q[11]; + + if(status) + return true; + } + + if(!fixCrc || + !nextCrcOk || + !preCrcOk) + return false; + + CRC16CCITTContext.Data(q, 10, out qCrc); + q[10] = qCrc[0]; + q[11] = qCrc[1]; + + fixedCrc = true; + + return true; + } } + + return false; + } + + /// Generates a correct subchannel all the missing ones + /// List of missing subchannels + /// List of tracks + /// Flags of tracks + /// Disc size + /// Subchannel log + /// Dump log + /// Progress initialization callback + /// Progress update callback + /// Progress finalization callback + /// Output image + public static void GenerateSubchannels(HashSet subchannelExtents, Track[] tracks, + Dictionary trackFlags, ulong blocks, SubchannelLog subLog, + DumpLog dumpLog, InitProgressHandler initProgress, + UpdateProgressHandler updateProgress, EndProgressHandler endProgress, + IWritableImage outputPlugin) + { + initProgress?.Invoke(); + + foreach(int sector in subchannelExtents) + { + Track track = tracks.LastOrDefault(t => (int)t.StartSector <= sector); + byte trkFlags; + byte flags; + ulong trackStart; + ulong pregap; + + if(track == null) + continue; + + // Hidden track + if(track.Sequence == 0) + { + track = tracks.FirstOrDefault(t => (int)t.Sequence == 1); + trackStart = 0; + pregap = track?.StartSector ?? 0; + } + else + { + trackStart = track.StartSector; + pregap = track.Pregap; + } + + if(!trackFlags.TryGetValue((byte)(track?.Sequence ?? 0), out trkFlags) && + track?.Type != TrackType.Audio) + flags = (byte)CdFlags.DataTrack; + else + flags = trkFlags; + + byte index; + + if(track?.Indexes?.Count > 0) + index = (byte)track.Indexes.LastOrDefault(i => i.Value >= sector).Key; + else + index = 0; + + updateProgress?.Invoke($"Generating subchannel for sector {sector}...", sector, (long)blocks); + dumpLog?.WriteLine($"Generating subchannel for sector {sector}."); + + byte[] sub = Subchannel.Generate(sector, track?.Sequence ?? 0, (int)pregap, (int)trackStart, flags, + index); + + outputPlugin.WriteSectorsTag(sub, (ulong)sector, 1, SectorTagType.CdSectorSubchannel); + + subLog?.WriteEntry(sub, true, sector, 1, true, false); + } + + endProgress?.Invoke(); } } \ No newline at end of file diff --git a/Aaru.Core/Media/Detection/MMC.cs b/Aaru.Core/Media/Detection/MMC.cs index 87c98e047..4d0b0ada6 100644 --- a/Aaru.Core/Media/Detection/MMC.cs +++ b/Aaru.Core/Media/Detection/MMC.cs @@ -49,669 +49,725 @@ using DMI = Aaru.Decoders.Xbox.DMI; // ReSharper disable JoinDeclarationAndInitializer -namespace Aaru.Core.Media.Detection +namespace Aaru.Core.Media.Detection; + +/// Detects media type for MMC class devices +public static class MMC { - /// Detects media type for MMC class devices - public static class MMC + /// SHA256 of PlayStation 2 boot sectors, seen in PAL discs + const string PS2_PAL_HASH = "5d04ff236613e1d8adcf9c201874acd6f6deed1e04306558b86f91cfb626f39d"; + + /// SHA256 of PlayStation 2 boot sectors, seen in Japanese, American, Malaysian and Korean discs + const string PS2_NTSC_HASH = "0bada1426e2c0351b872ef2a9ad2e5a0ac3918f4c53aa53329cb2911a8e16c23"; + + /// SHA256 of PlayStation 2 boot sectors, seen in Japanese discs + const string PS2_JAPANESE_HASH = "b82bffb809070d61fe050b7e1545df53d8f3cc648257cdff7502bc0ba6b38870"; + + static readonly byte[] _ps3Id = { - /// SHA256 of PlayStation 2 boot sectors, seen in PAL discs - const string PS2_PAL_HASH = "5d04ff236613e1d8adcf9c201874acd6f6deed1e04306558b86f91cfb626f39d"; + 0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x33, 0x00, 0x00, 0x00, 0x00 + }; - /// SHA256 of PlayStation 2 boot sectors, seen in Japanese, American, Malaysian and Korean discs - const string PS2_NTSC_HASH = "0bada1426e2c0351b872ef2a9ad2e5a0ac3918f4c53aa53329cb2911a8e16c23"; + static readonly byte[] _ps4Id = + { + 0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x34, 0x00, 0x00, 0x00, 0x00 + }; - /// SHA256 of PlayStation 2 boot sectors, seen in Japanese discs - const string PS2_JAPANESE_HASH = "b82bffb809070d61fe050b7e1545df53d8f3cc648257cdff7502bc0ba6b38870"; + static readonly byte[] _ps5Id = + { + 0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x35, 0x00, 0x00, 0x00, 0x00 + }; - static readonly byte[] _ps3Id = - { - 0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x33, 0x00, 0x00, 0x00, 0x00 - }; + static readonly byte[] _operaId = + { + 0x01, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x01 + }; - static readonly byte[] _ps4Id = - { - 0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x34, 0x00, 0x00, 0x00, 0x00 - }; + // Only present on bootable CDs, but those make more than 99% of all available + static readonly byte[] _fmTownsBootId = + { + 0x49, 0x50, 0x4C, 0x34, 0xEB, 0x55, 0x06 + }; - static readonly byte[] _ps5Id = - { - 0x50, 0x6C, 0x61, 0x79, 0x53, 0x74, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x35, 0x00, 0x00, 0x00, 0x00 - }; + /// Present on first two seconds of second track, says "COPYRIGHT BANDAI" + static readonly byte[] _playdiaCopyright = + { + 0x43, 0x4F, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x42, 0x41, 0x4E, 0x44, 0x41, 0x49 + }; - static readonly byte[] _operaId = - { - 0x01, 0x5A, 0x5A, 0x5A, 0x5A, 0x5A, 0x01 - }; + static readonly byte[] _pcEngineSignature = + { + 0x50, 0x43, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x20, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D, 0x20, 0x53, + 0x59, 0x53, 0x54, 0x45, 0x4D + }; - // Only present on bootable CDs, but those make more than 99% of all available - static readonly byte[] _fmTownsBootId = - { - 0x49, 0x50, 0x4C, 0x34, 0xEB, 0x55, 0x06 - }; + static readonly byte[] _pcFxSignature = + { + 0x50, 0x43, 0x2D, 0x46, 0x58, 0x3A, 0x48, 0x75, 0x5F, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D + }; - /// Present on first two seconds of second track, says "COPYRIGHT BANDAI" - static readonly byte[] _playdiaCopyright = - { - 0x43, 0x4F, 0x50, 0x59, 0x52, 0x49, 0x47, 0x48, 0x54, 0x20, 0x42, 0x41, 0x4E, 0x44, 0x41, 0x49 - }; + static readonly byte[] _atariSignature = + { + 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, + 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, + 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, + 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x52, 0x41, 0x20, 0x49, 0x50, 0x41, + 0x52, 0x50, 0x56, 0x4F, 0x44, 0x45, 0x44, 0x20, 0x54, 0x41, 0x20, 0x41, 0x45, 0x48, 0x44, 0x41, 0x52, 0x45, + 0x41, 0x20, 0x52, 0x54 + }; - static readonly byte[] _pcEngineSignature = - { - 0x50, 0x43, 0x20, 0x45, 0x6E, 0x67, 0x69, 0x6E, 0x65, 0x20, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D, 0x20, 0x53, - 0x59, 0x53, 0x54, 0x45, 0x4D - }; - - static readonly byte[] _pcFxSignature = - { - 0x50, 0x43, 0x2D, 0x46, 0x58, 0x3A, 0x48, 0x75, 0x5F, 0x43, 0x44, 0x2D, 0x52, 0x4F, 0x4D - }; - - static readonly byte[] _atariSignature = - { - 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, - 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, - 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, - 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x49, 0x52, 0x54, 0x41, 0x52, 0x41, 0x20, 0x49, 0x50, 0x41, - 0x52, 0x50, 0x56, 0x4F, 0x44, 0x45, 0x44, 0x20, 0x54, 0x41, 0x20, 0x41, 0x45, 0x48, 0x44, 0x41, 0x52, 0x45, - 0x41, 0x20, 0x52, 0x54 - }; - - /// This is some kind of header. Every 10 bytes there's an audio byte. - static readonly byte[] _videoNowColorFrameMarker = - { - 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, - 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, - 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, - 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, - 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, - 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, - 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, - 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, - 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, - 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, - 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, - 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, - 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, - 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x00, 0x00, 0x02, 0x01, 0x04, 0x02, 0x06, 0x03, 0xFF, 0x00, 0x08, 0x04, - 0x0A, 0x05, 0x0C, 0x06, 0x0E, 0x07, 0xFF, 0x00, 0x11, 0x08, 0x13, 0x09, 0x15, 0x0A, 0x17, 0x0B, 0xFF, 0x00, - 0x19, 0x0C, 0x1B, 0x0D, 0x1D, 0x0E, 0x1F, 0x0F, 0xFF, 0x00, 0x00, 0x28, 0x02, 0x29, 0x04, 0x2A, 0x06, 0x2B, - 0xFF, 0x00, 0x08, 0x2C, 0x0A, 0x2D, 0x0C, 0x2E, 0x0E, 0x2F, 0xFF, 0x00, 0x11, 0x30, 0x13, 0x31, 0x15, 0x32, - 0x17, 0x33, 0xFF, 0x00, 0x19, 0x34, 0x1B, 0x35, 0x1D, 0x36, 0x1F, 0x37, 0xFF, 0x00, 0x00, 0x38, 0x02, 0x39, - 0x04, 0x3A, 0x06, 0x3B, 0xFF, 0x00, 0x08, 0x3C, 0x0A, 0x3D, 0x0C, 0x3E, 0x0E, 0x3F, 0xFF, 0x00, 0x11, 0x40, - 0x13, 0x41, 0x15, 0x42, 0x17, 0x43, 0xFF, 0x00, 0x19, 0x44, 0x1B, 0x45, 0x1D, 0x46, 0x1F, 0x47, 0xFF, 0x00, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0x00 - }; - - static bool IsData(byte[] sector) - { - if(sector?.Length != 2352) - return false; - - byte[] syncMark = - { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 - }; - - byte[] testMark = new byte[12]; - Array.Copy(sector, 0, testMark, 0, 12); - - return syncMark.SequenceEqual(testMark) && (sector[0xF] == 0 || sector[0xF] == 1 || sector[0xF] == 2); - } - - static bool IsScrambledData(byte[] sector, int wantedLba, out int offset) - { - offset = 0; - - if(sector?.Length != 2352) - return false; - - byte[] syncMark = - { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 - }; - - byte[] testMark = new byte[12]; - - for(int i = 0; i <= 2336; i++) - { - Array.Copy(sector, i, testMark, 0, 12); - - if(!syncMark.SequenceEqual(testMark) || - (sector[i + 0xF] != 0x60 && sector[i + 0xF] != 0x61 && sector[i + 0xF] != 0x62)) - continue; - - // De-scramble M and S - int minute = sector[i + 12] ^ 0x01; - int second = sector[i + 13] ^ 0x80; - int frame = sector[i + 14]; - - // Convert to binary - minute = (minute / 16 * 10) + (minute & 0x0F); - second = (second / 16 * 10) + (second & 0x0F); - frame = (frame / 16 * 10) + (frame & 0x0F); - - // Calculate the first found LBA - int lba = (minute * 60 * 75) + (second * 75) + frame - 150; - - // Calculate the difference between the found LBA and the requested one - int diff = wantedLba - lba; - - offset = i + (2352 * diff); - - return true; - } + /// This is some kind of header. Every 10 bytes there's an audio byte. + static readonly byte[] _videoNowColorFrameMarker = + { + 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, + 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, + 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, + 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, + 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, + 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, + 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, + 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, + 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, + 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, + 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, + 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, + 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x81, 0xE3, 0xE3, 0xC7, + 0xC7, 0x81, 0x81, 0xE3, 0xC7, 0x00, 0x00, 0x00, 0x02, 0x01, 0x04, 0x02, 0x06, 0x03, 0xFF, 0x00, 0x08, 0x04, + 0x0A, 0x05, 0x0C, 0x06, 0x0E, 0x07, 0xFF, 0x00, 0x11, 0x08, 0x13, 0x09, 0x15, 0x0A, 0x17, 0x0B, 0xFF, 0x00, + 0x19, 0x0C, 0x1B, 0x0D, 0x1D, 0x0E, 0x1F, 0x0F, 0xFF, 0x00, 0x00, 0x28, 0x02, 0x29, 0x04, 0x2A, 0x06, 0x2B, + 0xFF, 0x00, 0x08, 0x2C, 0x0A, 0x2D, 0x0C, 0x2E, 0x0E, 0x2F, 0xFF, 0x00, 0x11, 0x30, 0x13, 0x31, 0x15, 0x32, + 0x17, 0x33, 0xFF, 0x00, 0x19, 0x34, 0x1B, 0x35, 0x1D, 0x36, 0x1F, 0x37, 0xFF, 0x00, 0x00, 0x38, 0x02, 0x39, + 0x04, 0x3A, 0x06, 0x3B, 0xFF, 0x00, 0x08, 0x3C, 0x0A, 0x3D, 0x0C, 0x3E, 0x0E, 0x3F, 0xFF, 0x00, 0x11, 0x40, + 0x13, 0x41, 0x15, 0x42, 0x17, 0x43, 0xFF, 0x00, 0x19, 0x44, 0x1B, 0x45, 0x1D, 0x46, 0x1F, 0x47, 0xFF, 0x00, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0x00 + }; + static bool IsData(byte[] sector) + { + if(sector?.Length != 2352) return false; - } - static byte[] DescrambleAndFixOffset(byte[] sector, int offsetBytes, int sectorsForOffset) + byte[] syncMark = { - byte[] descrambled = new byte[2352]; + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 + }; - int offsetFix = offsetBytes < 0 ? (2352 * sectorsForOffset) + offsetBytes : offsetBytes; + byte[] testMark = new byte[12]; + Array.Copy(sector, 0, testMark, 0, 12); - Array.Copy(sector, offsetFix, descrambled, 0, 2352); + return syncMark.SequenceEqual(testMark) && (sector[0xF] == 0 || sector[0xF] == 1 || sector[0xF] == 2); + } - return Sector.Scramble(descrambled); - } - - /// Checks if the media corresponds to CD-i. - /// Contents of LBA 0, with all headers. - /// Contents of LBA 0, with all headers. - /// true if it corresponds to a CD-i, falseotherwise. - static bool IsCdi(byte[] sector0, byte[] sector16) - { - if(sector16?.Length != 2352) - return false; - - byte[] cdiMark = - { - 0x01, 0x43, 0x44, 0x2D - }; - - bool isData = IsData(sector0); - - if(!isData || - (sector0[0xF] != 2 && sector0[0xF] != 1)) - return false; - - byte[] testMark = new byte[4]; - Array.Copy(sector16, 24, testMark, 0, 4); - - return cdiMark.SequenceEqual(testMark); - } - - static bool IsVideoNowColor(byte[] videoFrame) - { - if(videoFrame is null || - videoFrame.Length < _videoNowColorFrameMarker.Length) - return false; - - byte[] buffer = new byte[_videoNowColorFrameMarker.Length]; - - for(int framePosition = 0; framePosition + buffer.Length < videoFrame.Length; framePosition++) - { - Array.Copy(videoFrame, framePosition, buffer, 0, buffer.Length); - - for(int ab = 9; ab < buffer.Length; ab += 10) - buffer[ab] = 0; - - if(!_videoNowColorFrameMarker.SequenceEqual(buffer)) - continue; - - return true; - } + static bool IsScrambledData(byte[] sector, int wantedLba, out int offset) + { + offset = 0; + if(sector?.Length != 2352) return false; + + byte[] syncMark = + { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 + }; + + byte[] testMark = new byte[12]; + + for(int i = 0; i <= 2336; i++) + { + Array.Copy(sector, i, testMark, 0, 12); + + if(!syncMark.SequenceEqual(testMark) || + (sector[i + 0xF] != 0x60 && sector[i + 0xF] != 0x61 && sector[i + 0xF] != 0x62)) + continue; + + // De-scramble M and S + int minute = sector[i + 12] ^ 0x01; + int second = sector[i + 13] ^ 0x80; + int frame = sector[i + 14]; + + // Convert to binary + minute = (minute / 16 * 10) + (minute & 0x0F); + second = (second / 16 * 10) + (second & 0x0F); + frame = (frame / 16 * 10) + (frame & 0x0F); + + // Calculate the first found LBA + int lba = (minute * 60 * 75) + (second * 75) + frame - 150; + + // Calculate the difference between the found LBA and the requested one + int diff = wantedLba - lba; + + offset = i + (2352 * diff); + + return true; } - internal static int GetVideoNowColorOffset(byte[] data) - { - byte[] buffer = new byte[_videoNowColorFrameMarker.Length]; + return false; + } - for(int framePosition = 0; framePosition + buffer.Length < data.Length; framePosition++) + static byte[] DescrambleAndFixOffset(byte[] sector, int offsetBytes, int sectorsForOffset) + { + byte[] descrambled = new byte[2352]; + + int offsetFix = offsetBytes < 0 ? (2352 * sectorsForOffset) + offsetBytes : offsetBytes; + + Array.Copy(sector, offsetFix, descrambled, 0, 2352); + + return Sector.Scramble(descrambled); + } + + /// Checks if the media corresponds to CD-i. + /// Contents of LBA 0, with all headers. + /// Contents of LBA 0, with all headers. + /// true if it corresponds to a CD-i, falseotherwise. + static bool IsCdi(byte[] sector0, byte[] sector16) + { + if(sector16?.Length != 2352) + return false; + + byte[] cdiMark = + { + 0x01, 0x43, 0x44, 0x2D + }; + + bool isData = IsData(sector0); + + if(!isData || + (sector0[0xF] != 2 && sector0[0xF] != 1)) + return false; + + byte[] testMark = new byte[4]; + Array.Copy(sector16, 24, testMark, 0, 4); + + return cdiMark.SequenceEqual(testMark); + } + + static bool IsVideoNowColor(byte[] videoFrame) + { + if(videoFrame is null || + videoFrame.Length < _videoNowColorFrameMarker.Length) + return false; + + byte[] buffer = new byte[_videoNowColorFrameMarker.Length]; + + for(int framePosition = 0; framePosition + buffer.Length < videoFrame.Length; framePosition++) + { + Array.Copy(videoFrame, framePosition, buffer, 0, buffer.Length); + + for(int ab = 9; ab < buffer.Length; ab += 10) + buffer[ab] = 0; + + if(!_videoNowColorFrameMarker.SequenceEqual(buffer)) + continue; + + return true; + } + + return false; + } + + internal static int GetVideoNowColorOffset(byte[] data) + { + byte[] buffer = new byte[_videoNowColorFrameMarker.Length]; + + for(int framePosition = 0; framePosition + buffer.Length < data.Length; framePosition++) + { + Array.Copy(data, framePosition, buffer, 0, buffer.Length); + + for(int ab = 9; ab < buffer.Length; ab += 10) + buffer[ab] = 0; + + if(!_videoNowColorFrameMarker.SequenceEqual(buffer)) + continue; + + return 18032 - framePosition; + } + + return 0; + } + + internal static void DetectDiscType(ref MediaType mediaType, int sessions, FullTOC.CDFullTOC? decodedToc, + Device dev, out bool hiddenTrack, out bool hiddenData, + int firstTrackLastSession, ulong blocks) + { + uint startOfFirstDataTrack = uint.MaxValue; + DI.DiscInformation? blurayDi = null; + byte[] cmdBuf; + bool sense; + byte secondSessionFirstTrack = 0; + byte[] sector0; + byte[] sector1 = null; + byte[] ps2BootSectors = null; + byte[] playdia1 = null; + byte[] playdia2 = null; + byte[] firstDataSectorNotZero = null; + byte[] secondDataSectorNotZero = null; + byte[] firstTrackSecondSession = null; + byte[] firstTrackSecondSessionAudio = null; + byte[] videoNowColorFrame; + hiddenTrack = false; + hiddenData = false; + + sense = dev.GetConfiguration(out cmdBuf, out _, 0, MmcGetConfigurationRt.Current, dev.Timeout, out _); + + if(!sense) + { + Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); + + AaruConsole.DebugWriteLine("Media-Info command", "GET CONFIGURATION current profile is {0:X4}h", + ftr.CurrentProfile); + + switch(ftr.CurrentProfile) { - Array.Copy(data, framePosition, buffer, 0, buffer.Length); + case 0x0001: + mediaType = MediaType.GENERIC_HDD; - for(int ab = 9; ab < buffer.Length; ab += 10) - buffer[ab] = 0; + break; + case 0x0005: + mediaType = MediaType.CDMO; - if(!_videoNowColorFrameMarker.SequenceEqual(buffer)) - continue; + break; + case 0x0008: + mediaType = MediaType.CD; - return 18032 - framePosition; + break; + case 0x0009: + mediaType = MediaType.CDR; + + break; + case 0x000A: + mediaType = MediaType.CDRW; + + break; + case 0x0010: + mediaType = MediaType.DVDROM; + + break; + case 0x0011: + mediaType = MediaType.DVDR; + + break; + case 0x0012: + mediaType = MediaType.DVDRAM; + + break; + case 0x0013: + case 0x0014: + mediaType = MediaType.DVDRW; + + break; + case 0x0015: + case 0x0016: + mediaType = MediaType.DVDRDL; + + break; + case 0x0017: + mediaType = MediaType.DVDRWDL; + + break; + case 0x0018: + mediaType = MediaType.DVDDownload; + + break; + case 0x001A: + mediaType = MediaType.DVDPRW; + + break; + case 0x001B: + mediaType = MediaType.DVDPR; + + break; + case 0x0020: + mediaType = MediaType.DDCD; + + break; + case 0x0021: + mediaType = MediaType.DDCDR; + + break; + case 0x0022: + mediaType = MediaType.DDCDRW; + + break; + case 0x002A: + mediaType = MediaType.DVDPRWDL; + + break; + case 0x002B: + mediaType = MediaType.DVDPRDL; + + break; + case 0x0040: + mediaType = MediaType.BDROM; + + break; + case 0x0041: + case 0x0042: + mediaType = MediaType.BDR; + + break; + case 0x0043: + mediaType = MediaType.BDRE; + + break; + case 0x0050: + mediaType = MediaType.HDDVDROM; + + break; + case 0x0051: + mediaType = MediaType.HDDVDR; + + break; + case 0x0052: + mediaType = MediaType.HDDVDRAM; + + break; + case 0x0053: + mediaType = MediaType.HDDVDRW; + + break; + case 0x0058: + mediaType = MediaType.HDDVDRDL; + + break; + case 0x005A: + mediaType = MediaType.HDDVDRWDL; + + break; } - - return 0; } - internal static void DetectDiscType(ref MediaType mediaType, int sessions, FullTOC.CDFullTOC? decodedToc, - Device dev, out bool hiddenTrack, out bool hiddenData, - int firstTrackLastSession, ulong blocks) - { - uint startOfFirstDataTrack = uint.MaxValue; - DI.DiscInformation? blurayDi = null; - byte[] cmdBuf; - bool sense; - byte secondSessionFirstTrack = 0; - byte[] sector0; - byte[] sector1 = null; - byte[] ps2BootSectors = null; - byte[] playdia1 = null; - byte[] playdia2 = null; - byte[] firstDataSectorNotZero = null; - byte[] secondDataSectorNotZero = null; - byte[] firstTrackSecondSession = null; - byte[] firstTrackSecondSessionAudio = null; - byte[] videoNowColorFrame; - hiddenTrack = false; - hiddenData = false; + if(decodedToc?.TrackDescriptors.Any(t => t.SessionNumber == 2) == true) + secondSessionFirstTrack = decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == 2). + Min(t => t.POINT); - sense = dev.GetConfiguration(out cmdBuf, out _, 0, MmcGetConfigurationRt.Current, dev.Timeout, out _); + if(mediaType == MediaType.CD || + mediaType == MediaType.CDROMXA || + mediaType == MediaType.CDI) + { + sense = dev.ReadAtip(out cmdBuf, out _, dev.Timeout, out _); if(!sense) { - Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); + ATIP.CDATIP atip = ATIP.Decode(cmdBuf); - AaruConsole.DebugWriteLine("Media-Info command", "GET CONFIGURATION current profile is {0:X4}h", - ftr.CurrentProfile); + if(atip != null) - switch(ftr.CurrentProfile) - { - case 0x0001: - mediaType = MediaType.GENERIC_HDD; - - break; - case 0x0005: - mediaType = MediaType.CDMO; - - break; - case 0x0008: - mediaType = MediaType.CD; - - break; - case 0x0009: - mediaType = MediaType.CDR; - - break; - case 0x000A: - mediaType = MediaType.CDRW; - - break; - case 0x0010: - mediaType = MediaType.DVDROM; - - break; - case 0x0011: - mediaType = MediaType.DVDR; - - break; - case 0x0012: - mediaType = MediaType.DVDRAM; - - break; - case 0x0013: - case 0x0014: - mediaType = MediaType.DVDRW; - - break; - case 0x0015: - case 0x0016: - mediaType = MediaType.DVDRDL; - - break; - case 0x0017: - mediaType = MediaType.DVDRWDL; - - break; - case 0x0018: - mediaType = MediaType.DVDDownload; - - break; - case 0x001A: - mediaType = MediaType.DVDPRW; - - break; - case 0x001B: - mediaType = MediaType.DVDPR; - - break; - case 0x0020: - mediaType = MediaType.DDCD; - - break; - case 0x0021: - mediaType = MediaType.DDCDR; - - break; - case 0x0022: - mediaType = MediaType.DDCDRW; - - break; - case 0x002A: - mediaType = MediaType.DVDPRWDL; - - break; - case 0x002B: - mediaType = MediaType.DVDPRDL; - - break; - case 0x0040: - mediaType = MediaType.BDROM; - - break; - case 0x0041: - case 0x0042: - mediaType = MediaType.BDR; - - break; - case 0x0043: - mediaType = MediaType.BDRE; - - break; - case 0x0050: - mediaType = MediaType.HDDVDROM; - - break; - case 0x0051: - mediaType = MediaType.HDDVDR; - - break; - case 0x0052: - mediaType = MediaType.HDDVDRAM; - - break; - case 0x0053: - mediaType = MediaType.HDDVDRW; - - break; - case 0x0058: - mediaType = MediaType.HDDVDRDL; - - break; - case 0x005A: - mediaType = MediaType.HDDVDRWDL; - - break; - } + // Only CD-R and CD-RW have ATIP + mediaType = atip.DiscType ? MediaType.CDRW : MediaType.CDR; } + } - if(decodedToc?.TrackDescriptors.Any(t => t.SessionNumber == 2) == true) - secondSessionFirstTrack = decodedToc.Value.TrackDescriptors.Where(t => t.SessionNumber == 2). - Min(t => t.POINT); + if(mediaType == MediaType.CD || + mediaType == MediaType.CDROMXA) + { + bool hasDataTrack = false; + bool hasAudioTrack = false; + bool allFirstSessionTracksAreAudio = true; + bool hasVideoTrack = false; - if(mediaType == MediaType.CD || - mediaType == MediaType.CDROMXA || - mediaType == MediaType.CDI) + if(decodedToc.HasValue) { - sense = dev.ReadAtip(out cmdBuf, out _, dev.Timeout, out _); + FullTOC.TrackDataDescriptor a0Track = + decodedToc.Value.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA0 && t.ADR == 1); - if(!sense) - { - ATIP.CDATIP atip = ATIP.Decode(cmdBuf); + if(a0Track.POINT == 0xA0) + switch(a0Track.PSEC) + { + case 0x10: + AaruConsole.DebugWriteLine("Media detection", "TOC says disc type is CD-i."); + mediaType = MediaType.CDI; - if(atip != null) + break; + case 0x20: + AaruConsole.DebugWriteLine("Media detection", "TOC says disc type is CD-ROM XA."); + mediaType = MediaType.CDROMXA; - // Only CD-R and CD-RW have ATIP - mediaType = atip.DiscType ? MediaType.CDRW : MediaType.CDR; - } - } + break; + } - if(mediaType == MediaType.CD || - mediaType == MediaType.CDROMXA) - { - bool hasDataTrack = false; - bool hasAudioTrack = false; - bool allFirstSessionTracksAreAudio = true; - bool hasVideoTrack = false; - - if(decodedToc.HasValue) - { - FullTOC.TrackDataDescriptor a0Track = - decodedToc.Value.TrackDescriptors.FirstOrDefault(t => t.POINT == 0xA0 && t.ADR == 1); - - if(a0Track.POINT == 0xA0) - switch(a0Track.PSEC) - { - case 0x10: - AaruConsole.DebugWriteLine("Media detection", "TOC says disc type is CD-i."); - mediaType = MediaType.CDI; - - break; - case 0x20: - AaruConsole.DebugWriteLine("Media detection", "TOC says disc type is CD-ROM XA."); - mediaType = MediaType.CDROMXA; - - break; - } - - foreach(FullTOC.TrackDataDescriptor track in + foreach(FullTOC.TrackDataDescriptor track in decodedToc.Value.TrackDescriptors.Where(t => t.POINT > 0 && t.POINT <= 0x99)) - { - if(track.TNO == 1 && - ((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental)) - allFirstSessionTracksAreAudio &= firstTrackLastSession != 1; - - if((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack || - (TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental) - { - uint startAddress = (uint)((track.PHOUR * 3600 * 75) + (track.PMIN * 60 * 75) + - (track.PSEC * 75) + track.PFRAME - 150); - - if(startAddress < startOfFirstDataTrack) - startOfFirstDataTrack = startAddress; - - hasDataTrack = true; - allFirstSessionTracksAreAudio &= track.POINT >= firstTrackLastSession; - } - else - { - hasAudioTrack = true; - } - - hasVideoTrack |= track.ADR == 4; - } - } - - if(mediaType != MediaType.CDI) { - if(hasDataTrack && - hasAudioTrack && - allFirstSessionTracksAreAudio && - sessions == 2) - { - AaruConsole.DebugWriteLine("Media detection", - "Disc has audio and data tracks, two sessions, and all data tracks are in second session, setting as CD+."); + if(track.TNO == 1 && + ((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental)) + allFirstSessionTracksAreAudio &= firstTrackLastSession != 1; - mediaType = MediaType.CDPLUS; + if((TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrack || + (TocControl)(track.CONTROL & 0x0D) == TocControl.DataTrackIncremental) + { + uint startAddress = (uint)((track.PHOUR * 3600 * 75) + (track.PMIN * 60 * 75) + + (track.PSEC * 75) + track.PFRAME - 150); + + if(startAddress < startOfFirstDataTrack) + startOfFirstDataTrack = startAddress; + + hasDataTrack = true; + allFirstSessionTracksAreAudio &= track.POINT >= firstTrackLastSession; + } + else + { + hasAudioTrack = true; } - if(!hasDataTrack && - hasAudioTrack && - sessions == 1) - { - AaruConsole.DebugWriteLine("Media detection", - "Disc has only audio tracks in a single session, setting as CD Digital Audio."); - - mediaType = MediaType.CDDA; - } - - if(hasDataTrack && - !hasAudioTrack && - sessions == 1) - { - AaruConsole.DebugWriteLine("Media detection", - "Disc has only data tracks in a single session, setting as CD-ROM."); - - mediaType = MediaType.CDROM; - } - - if(hasVideoTrack && - !hasDataTrack && - sessions == 1) - { - AaruConsole.DebugWriteLine("Media detection", - "Disc has video tracks in a single session, setting as CD Video."); - - mediaType = MediaType.CDV; - } - } - - if((mediaType == MediaType.CD || mediaType == MediaType.CDROM) && hasDataTrack) - { - foreach(uint startAddress in decodedToc.Value.TrackDescriptors. - Where(t => t.POINT > 0 && t.POINT <= 0x99 && - ((TocControl)(t.CONTROL & 0x0D) == - TocControl.DataTrack || - (TocControl)(t.CONTROL & 0x0D) == - TocControl.DataTrackIncremental)). - Select(track => (uint)((track.PHOUR * 3600 * 75) + - (track.PMIN * 60 * 75) + - (track.PSEC * 75) + - track.PFRAME - 150) + 16)) - { - sense = dev.ReadCd(out cmdBuf, out _, startAddress, 2352, 1, MmcSectorTypes.AllTypes, false, - false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); - - if(sense || dev.Error) - continue; - - if(cmdBuf[0] != 0x00 || - cmdBuf[1] != 0xFF || - cmdBuf[2] != 0xFF || - cmdBuf[3] != 0xFF || - cmdBuf[4] != 0xFF || - cmdBuf[5] != 0xFF || - cmdBuf[6] != 0xFF || - cmdBuf[7] != 0xFF || - cmdBuf[8] != 0xFF || - cmdBuf[9] != 0xFF || - cmdBuf[10] != 0xFF || - cmdBuf[11] != 0x00 || - cmdBuf[15] != 0x02) - continue; - - AaruConsole.DebugWriteLine("Media detection", - "Disc has a mode 2 data track, setting as CD-ROM XA."); - - mediaType = MediaType.CDROMXA; - - break; - } + hasVideoTrack |= track.ADR == 4; } } - if(secondSessionFirstTrack != 0 && - decodedToc?.TrackDescriptors.Any(t => t.POINT == secondSessionFirstTrack) == true) + if(mediaType != MediaType.CDI) { - FullTOC.TrackDataDescriptor secondSessionFirstTrackTrack = - decodedToc.Value.TrackDescriptors.First(t => t.POINT == secondSessionFirstTrack); + if(hasDataTrack && + hasAudioTrack && + allFirstSessionTracksAreAudio && + sessions == 2) + { + AaruConsole.DebugWriteLine("Media detection", + "Disc has audio and data tracks, two sessions, and all data tracks are in second session, setting as CD+."); - uint firstSectorSecondSessionFirstTrack = - (uint)((secondSessionFirstTrackTrack.PHOUR * 3600 * 75) + - (secondSessionFirstTrackTrack.PMIN * 60 * 75) + (secondSessionFirstTrackTrack.PSEC * 75) + - secondSessionFirstTrackTrack.PFRAME - 150); + mediaType = MediaType.CDPLUS; + } + if(!hasDataTrack && + hasAudioTrack && + sessions == 1) + { + AaruConsole.DebugWriteLine("Media detection", + "Disc has only audio tracks in a single session, setting as CD Digital Audio."); + + mediaType = MediaType.CDDA; + } + + if(hasDataTrack && + !hasAudioTrack && + sessions == 1) + { + AaruConsole.DebugWriteLine("Media detection", + "Disc has only data tracks in a single session, setting as CD-ROM."); + + mediaType = MediaType.CDROM; + } + + if(hasVideoTrack && + !hasDataTrack && + sessions == 1) + { + AaruConsole.DebugWriteLine("Media detection", + "Disc has video tracks in a single session, setting as CD Video."); + + mediaType = MediaType.CDV; + } + } + + if((mediaType == MediaType.CD || mediaType == MediaType.CDROM) && hasDataTrack) + { + foreach(uint startAddress in decodedToc.Value.TrackDescriptors. + Where(t => t.POINT > 0 && t.POINT <= 0x99 && + ((TocControl)(t.CONTROL & 0x0D) == + TocControl.DataTrack || + (TocControl)(t.CONTROL & 0x0D) == + TocControl.DataTrackIncremental)). + Select(track => (uint)((track.PHOUR * 3600 * 75) + + (track.PMIN * 60 * 75) + + (track.PSEC * 75) + + track.PFRAME - 150) + 16)) + { + sense = dev.ReadCd(out cmdBuf, out _, startAddress, 2352, 1, MmcSectorTypes.AllTypes, false, + false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); + + if(sense || dev.Error) + continue; + + if(cmdBuf[0] != 0x00 || + cmdBuf[1] != 0xFF || + cmdBuf[2] != 0xFF || + cmdBuf[3] != 0xFF || + cmdBuf[4] != 0xFF || + cmdBuf[5] != 0xFF || + cmdBuf[6] != 0xFF || + cmdBuf[7] != 0xFF || + cmdBuf[8] != 0xFF || + cmdBuf[9] != 0xFF || + cmdBuf[10] != 0xFF || + cmdBuf[11] != 0x00 || + cmdBuf[15] != 0x02) + continue; + + AaruConsole.DebugWriteLine("Media detection", + "Disc has a mode 2 data track, setting as CD-ROM XA."); + + mediaType = MediaType.CDROMXA; + + break; + } + } + } + + if(secondSessionFirstTrack != 0 && + decodedToc?.TrackDescriptors.Any(t => t.POINT == secondSessionFirstTrack) == true) + { + FullTOC.TrackDataDescriptor secondSessionFirstTrackTrack = + decodedToc.Value.TrackDescriptors.First(t => t.POINT == secondSessionFirstTrack); + + uint firstSectorSecondSessionFirstTrack = + (uint)((secondSessionFirstTrackTrack.PHOUR * 3600 * 75) + + (secondSessionFirstTrackTrack.PMIN * 60 * 75) + (secondSessionFirstTrackTrack.PSEC * 75) + + secondSessionFirstTrackTrack.PFRAME - 150); + + sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack, 2352, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + + if(!sense && + !dev.Error) + { + firstTrackSecondSession = cmdBuf; + } + else + { sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack, 2352, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcSectorTypes.Cdda, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) - { firstTrackSecondSession = cmdBuf; - } - else - { - sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack, 2352, 1, - MmcSectorTypes.Cdda, false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + } - if(!sense && - !dev.Error) - firstTrackSecondSession = cmdBuf; - } + sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack - 1, 2352, 3, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + if(!sense && + !dev.Error) + { + firstTrackSecondSessionAudio = cmdBuf; + } + else + { sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack - 1, 2352, 3, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) - { firstTrackSecondSessionAudio = cmdBuf; - } - else - { - sense = dev.ReadCd(out cmdBuf, out _, firstSectorSecondSessionFirstTrack - 1, 2352, 3, - MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + } + } - if(!sense && - !dev.Error) - firstTrackSecondSessionAudio = cmdBuf; + videoNowColorFrame = new byte[9 * 2352]; + + for(int i = 0; i < 9; i++) + { + sense = dev.ReadCd(out cmdBuf, out _, (uint)i, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + dev.Timeout, out _); + + if(sense || dev.Error) + { + sense = dev.ReadCd(out cmdBuf, out _, (uint)i, 2352, 1, MmcSectorTypes.Cdda, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, + dev.Timeout, out _); + + if(sense || !dev.Error) + { + videoNowColorFrame = null; + + break; } } - videoNowColorFrame = new byte[9 * 2352]; + Array.Copy(cmdBuf, 0, videoNowColorFrame, i * 2352, 2352); + } - for(int i = 0; i < 9; i++) + FullTOC.TrackDataDescriptor? firstTrack = decodedToc?.TrackDescriptors.FirstOrDefault(t => t.POINT == 1); + + if(firstTrack?.POINT == 1) + { + uint firstTrackSector = (uint)((firstTrack.Value.PHOUR * 3600 * 75) + + (firstTrack.Value.PMIN * 60 * 75) + (firstTrack.Value.PSEC * 75) + + firstTrack.Value.PFRAME - 150); + + // Check for hidden data before start of track 1 + if(firstTrackSector > 0) { - sense = dev.ReadCd(out cmdBuf, out _, (uint)i, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, + sense = dev.ReadCd(out sector0, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); - if(sense || dev.Error) + if(!dev.Error && + !sense) { - sense = dev.ReadCd(out cmdBuf, out _, (uint)i, 2352, 1, MmcSectorTypes.Cdda, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, - dev.Timeout, out _); + hiddenTrack = true; - if(sense || !dev.Error) + hiddenData = IsData(sector0); + + if(hiddenData) { - videoNowColorFrame = null; + sense = dev.ReadCd(out byte[] sector16, out _, 16, 2352, 1, MmcSectorTypes.AllTypes, false, + false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); - break; + if(!sense && + IsCdi(sector0, sector16)) + { + mediaType = MediaType.CDIREADY; + + AaruConsole.DebugWriteLine("Media detection", + "Disc has a hidden CD-i track in track 1's pregap, setting as CD-i Ready."); + + return; + } } - } - - Array.Copy(cmdBuf, 0, videoNowColorFrame, i * 2352, 2352); - } - - FullTOC.TrackDataDescriptor? firstTrack = decodedToc?.TrackDescriptors.FirstOrDefault(t => t.POINT == 1); - - if(firstTrack?.POINT == 1) - { - uint firstTrackSector = (uint)((firstTrack.Value.PHOUR * 3600 * 75) + - (firstTrack.Value.PMIN * 60 * 75) + (firstTrack.Value.PSEC * 75) + - firstTrack.Value.PFRAME - 150); - - // Check for hidden data before start of track 1 - if(firstTrackSector > 0) - { - sense = dev.ReadCd(out sector0, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, - dev.Timeout, out _); - - if(!dev.Error && - !sense) + else { - hiddenTrack = true; - - hiddenData = IsData(sector0); + hiddenData = IsScrambledData(sector0, 0, out int combinedOffset); if(hiddenData) { - sense = dev.ReadCd(out byte[] sector16, out _, 16, 2352, 1, MmcSectorTypes.AllTypes, false, - false, true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + int sectorsForOffset = combinedOffset / 2352; + + if(sectorsForOffset < 0) + sectorsForOffset *= -1; + + if(combinedOffset % 2352 != 0) + sectorsForOffset++; + + int lba0 = 0; + int lba16 = 16; + + if(combinedOffset < 0) + { + lba0 -= sectorsForOffset; + lba16 -= sectorsForOffset; + } + + sense = dev.ReadCd(out sector0, out _, (uint)lba0, 2352, (uint)sectorsForOffset + 1, + MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); - if(!sense && - IsCdi(sector0, sector16)) + // Drive does not support reading negative sectors? + if(sense && lba0 < 0) + { + dev.ReadCd(out sector0, out _, 0, 2352, 2, MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); + + sector0 = DescrambleAndFixOffset(sector0, combinedOffset, sectorsForOffset); + } + else + sector0 = DescrambleAndFixOffset(sector0, combinedOffset, sectorsForOffset); + + dev.ReadCd(out byte[] sector16, out _, (uint)lba16, 2352, (uint)sectorsForOffset + 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, + true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + + sector16 = DescrambleAndFixOffset(sector16, combinedOffset, sectorsForOffset); + + if(IsCdi(sector0, sector16)) { mediaType = MediaType.CDIREADY; @@ -721,142 +777,173 @@ namespace Aaru.Core.Media.Detection return; } } - else - { - hiddenData = IsScrambledData(sector0, 0, out int combinedOffset); - - if(hiddenData) - { - int sectorsForOffset = combinedOffset / 2352; - - if(sectorsForOffset < 0) - sectorsForOffset *= -1; - - if(combinedOffset % 2352 != 0) - sectorsForOffset++; - - int lba0 = 0; - int lba16 = 16; - - if(combinedOffset < 0) - { - lba0 -= sectorsForOffset; - lba16 -= sectorsForOffset; - } - - sense = dev.ReadCd(out sector0, out _, (uint)lba0, 2352, (uint)sectorsForOffset + 1, - MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); - - // Drive does not support reading negative sectors? - if(sense && lba0 < 0) - { - dev.ReadCd(out sector0, out _, 0, 2352, 2, MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); - - sector0 = DescrambleAndFixOffset(sector0, combinedOffset, sectorsForOffset); - } - else - sector0 = DescrambleAndFixOffset(sector0, combinedOffset, sectorsForOffset); - - dev.ReadCd(out byte[] sector16, out _, (uint)lba16, 2352, (uint)sectorsForOffset + 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, - true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); - - sector16 = DescrambleAndFixOffset(sector16, combinedOffset, sectorsForOffset); - - if(IsCdi(sector0, sector16)) - { - mediaType = MediaType.CDIREADY; - - AaruConsole.DebugWriteLine("Media detection", - "Disc has a hidden CD-i track in track 1's pregap, setting as CD-i Ready."); - - return; - } - } - } } } } + } - sector0 = null; + sector0 = null; - switch(mediaType) + switch(mediaType) + { + case MediaType.CD: + case MediaType.CDDA: + case MediaType.CDPLUS: + case MediaType.CDROM: + case MediaType.CDROMXA: { - case MediaType.CD: - case MediaType.CDDA: - case MediaType.CDPLUS: - case MediaType.CDROM: - case MediaType.CDROMXA: + sense = dev.ReadCd(out cmdBuf, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + dev.Timeout, out _); + + if(!sense && + !dev.Error) { - sense = dev.ReadCd(out cmdBuf, out _, 0, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + sector0 = new byte[2048]; + Array.Copy(cmdBuf, 16, sector0, 0, 2048); + + sense = dev.ReadCd(out cmdBuf, out _, 1, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); + + if(!sense && + !dev.Error) + { + sector1 = new byte[2048]; + Array.Copy(cmdBuf, 16, sector1, 0, 2048); + } + + sense = dev.ReadCd(out cmdBuf, out _, 4200, 2352, 1, MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); + + if(!sense && + !dev.Error) + { + playdia1 = new byte[2048]; + Array.Copy(cmdBuf, 24, playdia1, 0, 2048); + } + + sense = dev.ReadCd(out cmdBuf, out _, 4201, 2352, 1, MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); + + if(!sense && + !dev.Error) + { + playdia2 = new byte[2048]; + Array.Copy(cmdBuf, 24, playdia2, 0, 2048); + } + + if(startOfFirstDataTrack != uint.MaxValue) + { + sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2352, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, + true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + + if(!sense && + !dev.Error) + { + firstDataSectorNotZero = new byte[2048]; + Array.Copy(cmdBuf, 16, firstDataSectorNotZero, 0, 2048); + } + + sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2352, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, + true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + + if(!sense && + !dev.Error) + { + secondDataSectorNotZero = new byte[2048]; + Array.Copy(cmdBuf, 16, secondDataSectorNotZero, 0, 2048); + } + } + + var ps2Ms = new MemoryStream(); + + for(uint p = 0; p < 12; p++) + { + sense = dev.ReadCd(out cmdBuf, out _, p, 2352, 1, MmcSectorTypes.AllTypes, false, false, + true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); + + if(sense || dev.Error) + break; + + ps2Ms.Write(cmdBuf, cmdBuf[0x0F] == 0x02 ? 24 : 16, 2048); + } + + if(ps2Ms.Length == 0x6000) + ps2BootSectors = ps2Ms.ToArray(); + } + else + { + sense = dev.ReadCd(out cmdBuf, out _, 0, 2324, 1, MmcSectorTypes.Mode2, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) { sector0 = new byte[2048]; - Array.Copy(cmdBuf, 16, sector0, 0, 2048); + Array.Copy(cmdBuf, 0, sector0, 0, 2048); - sense = dev.ReadCd(out cmdBuf, out _, 1, 2352, 1, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); + sense = dev.ReadCd(out cmdBuf, out _, 1, 2324, 1, MmcSectorTypes.Mode2, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, + dev.Timeout, out _); if(!sense && !dev.Error) { sector1 = new byte[2048]; - Array.Copy(cmdBuf, 16, sector1, 0, 2048); + Array.Copy(cmdBuf, 1, sector0, 0, 2048); } - sense = dev.ReadCd(out cmdBuf, out _, 4200, 2352, 1, MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + sense = dev.ReadCd(out cmdBuf, out _, 4200, 2324, 1, MmcSectorTypes.Mode2, false, false, + false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) { playdia1 = new byte[2048]; - Array.Copy(cmdBuf, 24, playdia1, 0, 2048); + Array.Copy(cmdBuf, 0, playdia1, 0, 2048); } - sense = dev.ReadCd(out cmdBuf, out _, 4201, 2352, 1, MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + sense = dev.ReadCd(out cmdBuf, out _, 4201, 2324, 1, MmcSectorTypes.Mode2, false, false, + false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) { playdia2 = new byte[2048]; - Array.Copy(cmdBuf, 24, playdia2, 0, 2048); + Array.Copy(cmdBuf, 0, playdia2, 0, 2048); } if(startOfFirstDataTrack != uint.MaxValue) { - sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2352, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, - true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2324, 1, + MmcSectorTypes.Mode2, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) { firstDataSectorNotZero = new byte[2048]; - Array.Copy(cmdBuf, 16, firstDataSectorNotZero, 0, 2048); + Array.Copy(cmdBuf, 0, firstDataSectorNotZero, 0, 2048); } - sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2352, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, - true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2324, 1, + MmcSectorTypes.Mode2, false, false, false, MmcHeaderCodes.None, true, + false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) { secondDataSectorNotZero = new byte[2048]; - Array.Copy(cmdBuf, 16, secondDataSectorNotZero, 0, 2048); + Array.Copy(cmdBuf, 0, secondDataSectorNotZero, 0, 2048); } } @@ -864,14 +951,14 @@ namespace Aaru.Core.Media.Detection for(uint p = 0; p < 12; p++) { - sense = dev.ReadCd(out cmdBuf, out _, p, 2352, 1, MmcSectorTypes.AllTypes, false, false, - true, MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, + sense = dev.ReadCd(out cmdBuf, out _, p, 2324, 1, MmcSectorTypes.Mode2, false, false, + false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(sense || dev.Error) break; - ps2Ms.Write(cmdBuf, cmdBuf[0x0F] == 0x02 ? 24 : 16, 2048); + ps2Ms.Write(cmdBuf, 0, 2048); } if(ps2Ms.Length == 0x6000) @@ -879,182 +966,120 @@ namespace Aaru.Core.Media.Detection } else { - sense = dev.ReadCd(out cmdBuf, out _, 0, 2324, 1, MmcSectorTypes.Mode2, false, false, false, + sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 1, MmcSectorTypes.Mode1, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) { - sector0 = new byte[2048]; - Array.Copy(cmdBuf, 0, sector0, 0, 2048); + sector0 = cmdBuf; - sense = dev.ReadCd(out cmdBuf, out _, 1, 2324, 1, MmcSectorTypes.Mode2, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, - dev.Timeout, out _); - - if(!sense && - !dev.Error) - { - sector1 = new byte[2048]; - Array.Copy(cmdBuf, 1, sector0, 0, 2048); - } - - sense = dev.ReadCd(out cmdBuf, out _, 4200, 2324, 1, MmcSectorTypes.Mode2, false, false, + sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 1, MmcSectorTypes.Mode1, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) - { - playdia1 = new byte[2048]; - Array.Copy(cmdBuf, 0, playdia1, 0, 2048); - } + sector1 = cmdBuf; - sense = dev.ReadCd(out cmdBuf, out _, 4201, 2324, 1, MmcSectorTypes.Mode2, false, false, + sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 12, MmcSectorTypes.Mode1, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(!sense && !dev.Error) - { - playdia2 = new byte[2048]; - Array.Copy(cmdBuf, 0, playdia2, 0, 2048); - } + ps2BootSectors = cmdBuf; if(startOfFirstDataTrack != uint.MaxValue) { - sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2324, 1, - MmcSectorTypes.Mode2, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2048, 1, + MmcSectorTypes.Mode1, false, false, false, MmcHeaderCodes.None, + true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, + out _); if(!sense && !dev.Error) - { - firstDataSectorNotZero = new byte[2048]; - Array.Copy(cmdBuf, 0, firstDataSectorNotZero, 0, 2048); - } + firstDataSectorNotZero = cmdBuf; - sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2324, 1, - MmcSectorTypes.Mode2, false, false, false, MmcHeaderCodes.None, true, - false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2048, 1, + MmcSectorTypes.Mode1, false, false, false, MmcHeaderCodes.None, + true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, + out _); if(!sense && !dev.Error) - { - secondDataSectorNotZero = new byte[2048]; - Array.Copy(cmdBuf, 0, secondDataSectorNotZero, 0, 2048); - } + secondDataSectorNotZero = cmdBuf; } - - var ps2Ms = new MemoryStream(); - - for(uint p = 0; p < 12; p++) - { - sense = dev.ReadCd(out cmdBuf, out _, p, 2324, 1, MmcSectorTypes.Mode2, false, false, - false, MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); - - if(sense || dev.Error) - break; - - ps2Ms.Write(cmdBuf, 0, 2048); - } - - if(ps2Ms.Length == 0x6000) - ps2BootSectors = ps2Ms.ToArray(); } else { - sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 1, MmcSectorTypes.Mode1, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, - dev.Timeout, out _); - - if(!sense && - !dev.Error) - { - sector0 = cmdBuf; - - sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 1, MmcSectorTypes.Mode1, false, false, - false, MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); - - if(!sense && - !dev.Error) - sector1 = cmdBuf; - - sense = dev.ReadCd(out cmdBuf, out _, 0, 2048, 12, MmcSectorTypes.Mode1, false, false, - false, MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); - - if(!sense && - !dev.Error) - ps2BootSectors = cmdBuf; - - if(startOfFirstDataTrack != uint.MaxValue) - { - sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack, 2048, 1, - MmcSectorTypes.Mode1, false, false, false, MmcHeaderCodes.None, - true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, - out _); - - if(!sense && - !dev.Error) - firstDataSectorNotZero = cmdBuf; - - sense = dev.ReadCd(out cmdBuf, out _, startOfFirstDataTrack + 1, 2048, 1, - MmcSectorTypes.Mode1, false, false, false, MmcHeaderCodes.None, - true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, - out _); - - if(!sense && - !dev.Error) - secondDataSectorNotZero = cmdBuf; - } - } - else - { - goto case MediaType.DVDROM; - } + goto case MediaType.DVDROM; } } - - break; } - // TODO: Check for CD-i Ready - case MediaType.CDI: break; - case MediaType.DVDROM: - case MediaType.HDDVDROM: - case MediaType.BDROM: - case MediaType.UHDBD: - case MediaType.Unknown: - if(mediaType == MediaType.BDROM || - mediaType == MediaType.UHDBD) - { - sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.DiscInformation, 0, dev.Timeout, out _); + break; + } - if(!sense) - blurayDi = DI.Decode(cmdBuf); - } + // TODO: Check for CD-i Ready + case MediaType.CDI: break; + case MediaType.DVDROM: + case MediaType.HDDVDROM: + case MediaType.BDROM: + case MediaType.UHDBD: + case MediaType.Unknown: + if(mediaType == MediaType.BDROM || + mediaType == MediaType.UHDBD) + { + sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.DiscInformation, 0, dev.Timeout, out _); - sense = dev.Read16(out cmdBuf, out _, 0, false, false, false, 0, 2048, 0, 1, false, dev.Timeout, + if(!sense) + blurayDi = DI.Decode(cmdBuf); + } + + sense = dev.Read16(out cmdBuf, out _, 0, false, false, false, 0, 2048, 0, 1, false, dev.Timeout, + out _); + + if(!sense && + !dev.Error) + { + sector0 = cmdBuf; + + sense = dev.Read16(out cmdBuf, out _, 0, false, false, false, 1, 2048, 0, 1, false, dev.Timeout, out _); + if(!sense && + !dev.Error) + sector1 = cmdBuf; + + sense = dev.Read16(out cmdBuf, out _, 0, false, false, false, 0, 2048, 0, 12, false, + dev.Timeout, out _); + + if(!sense && + !dev.Error && + cmdBuf.Length == 0x6000) + ps2BootSectors = cmdBuf; + } + else + { + sense = dev.Read12(out cmdBuf, out _, 0, false, false, false, false, 0, 2048, 0, 1, false, + dev.Timeout, out _); + if(!sense && !dev.Error) { sector0 = cmdBuf; - sense = dev.Read16(out cmdBuf, out _, 0, false, false, false, 1, 2048, 0, 1, false, dev.Timeout, - out _); + sense = dev.Read12(out cmdBuf, out _, 0, false, false, false, false, 1, 2048, 0, 1, false, + dev.Timeout, out _); if(!sense && !dev.Error) sector1 = cmdBuf; - sense = dev.Read16(out cmdBuf, out _, 0, false, false, false, 0, 2048, 0, 12, false, + sense = dev.Read12(out cmdBuf, out _, 0, false, false, false, false, 0, 2048, 0, 12, false, dev.Timeout, out _); if(!sense && @@ -1064,7 +1089,7 @@ namespace Aaru.Core.Media.Detection } else { - sense = dev.Read12(out cmdBuf, out _, 0, false, false, false, false, 0, 2048, 0, 1, false, + sense = dev.Read10(out cmdBuf, out _, 0, false, false, false, false, 0, 2048, 0, 1, dev.Timeout, out _); if(!sense && @@ -1072,14 +1097,14 @@ namespace Aaru.Core.Media.Detection { sector0 = cmdBuf; - sense = dev.Read12(out cmdBuf, out _, 0, false, false, false, false, 1, 2048, 0, 1, false, + sense = dev.Read10(out cmdBuf, out _, 0, false, false, false, false, 1, 2048, 0, 1, dev.Timeout, out _); if(!sense && !dev.Error) sector1 = cmdBuf; - sense = dev.Read12(out cmdBuf, out _, 0, false, false, false, false, 0, 2048, 0, 12, false, + sense = dev.Read10(out cmdBuf, out _, 0, false, false, false, false, 0, 2048, 0, 12, dev.Timeout, out _); if(!sense && @@ -1089,725 +1114,274 @@ namespace Aaru.Core.Media.Detection } else { - sense = dev.Read10(out cmdBuf, out _, 0, false, false, false, false, 0, 2048, 0, 1, - dev.Timeout, out _); + sense = dev.Read6(out cmdBuf, out _, 0, 2048, 1, dev.Timeout, out _); if(!sense && !dev.Error) { sector0 = cmdBuf; - sense = dev.Read10(out cmdBuf, out _, 0, false, false, false, false, 1, 2048, 0, 1, - dev.Timeout, out _); + sense = dev.Read6(out cmdBuf, out _, 1, 2048, 1, dev.Timeout, out _); if(!sense && !dev.Error) sector1 = cmdBuf; - sense = dev.Read10(out cmdBuf, out _, 0, false, false, false, false, 0, 2048, 0, 12, - dev.Timeout, out _); + sense = dev.Read6(out cmdBuf, out _, 0, 2048, 12, dev.Timeout, out _); if(!sense && !dev.Error && cmdBuf.Length == 0x6000) ps2BootSectors = cmdBuf; } - else - { - sense = dev.Read6(out cmdBuf, out _, 0, 2048, 1, dev.Timeout, out _); - - if(!sense && - !dev.Error) - { - sector0 = cmdBuf; - - sense = dev.Read6(out cmdBuf, out _, 1, 2048, 1, dev.Timeout, out _); - - if(!sense && - !dev.Error) - sector1 = cmdBuf; - - sense = dev.Read6(out cmdBuf, out _, 0, 2048, 12, dev.Timeout, out _); - - if(!sense && - !dev.Error && - cmdBuf.Length == 0x6000) - ps2BootSectors = cmdBuf; - } - } } } + } - if(mediaType == MediaType.DVDROM) - { - sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, - out _); - - if(!sense) - { - PFI.PhysicalFormatInformation? pfi = PFI.Decode(cmdBuf, mediaType); - - if(pfi != null) - { - switch(pfi.Value.DiskCategory) - { - case DiskCategory.DVDPR: - mediaType = MediaType.DVDPR; - - break; - case DiskCategory.DVDPRDL: - mediaType = MediaType.DVDPRDL; - - break; - case DiskCategory.DVDPRW: - mediaType = MediaType.DVDPRW; - - break; - case DiskCategory.DVDPRWDL: - mediaType = MediaType.DVDPRWDL; - - break; - case DiskCategory.DVDR: - mediaType = pfi.Value.PartVersion >= 6 ? MediaType.DVDRDL : MediaType.DVDR; - - break; - case DiskCategory.DVDRAM: - mediaType = MediaType.DVDRAM; - - break; - case DiskCategory.DVDRW: - mediaType = pfi.Value.PartVersion >= 15 ? MediaType.DVDRWDL : MediaType.DVDRW; - - break; - case DiskCategory.HDDVDR: - mediaType = MediaType.HDDVDR; - - break; - case DiskCategory.HDDVDRAM: - mediaType = MediaType.HDDVDRAM; - - break; - case DiskCategory.HDDVDROM: - mediaType = MediaType.HDDVDROM; - - break; - case DiskCategory.HDDVDRW: - mediaType = MediaType.HDDVDRW; - - break; - case DiskCategory.Nintendo: - mediaType = pfi.Value.DiscSize == DVDSize.Eighty ? MediaType.GOD - : MediaType.WOD; - - break; - case DiskCategory.UMD: - mediaType = MediaType.UMD; - - break; - } - } - } - - sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DiscManufacturingInformation, 0, - dev.Timeout, out _); - - if(!sense) - { - if(DMI.IsXbox(cmdBuf)) - { - AaruConsole.DebugWriteLine("Media detection", - "Found Xbox DMI, setting disc type to Xbox Game Disc (XGD)."); - - mediaType = MediaType.XGD; - - return; - } - - if(DMI.IsXbox360(cmdBuf)) - { - // All XGD3 all have the same number of blocks - if(blocks == 25063 || // Locked (or non compatible drive) - blocks == 4229664 || // Xtreme unlock - blocks == 4246304) // Wxripper unlock - { - AaruConsole.DebugWriteLine("Media detection", - "Found Xbox 360 DMI with {0} blocks, setting disc type to Xbox 360 Game Disc 3 (XGD3)."); - - mediaType = MediaType.XGD3; - - return; - } - - AaruConsole.DebugWriteLine("Media detection", - "Found Xbox 360 DMI with {0} blocks, setting disc type to Xbox 360 Game Disc 2 (XGD2)."); - - mediaType = MediaType.XGD2; - - return; - } - } - } - - break; - - // Recordables will be checked for PhotoCD only - case MediaType.CDR: - // Check if ISO9660 - sense = dev.Read12(out byte[] isoSector, out _, 0, false, false, false, false, 16, 2048, 0, 1, - false, dev.Timeout, out _); - - // Sector 16 reads, and contains "CD001" magic? - if(sense || - isoSector[1] != 0x43 || - isoSector[2] != 0x44 || - isoSector[3] != 0x30 || - isoSector[4] != 0x30 || - isoSector[5] != 0x31) - return; - - // From sectors 16 to 31 - uint isoSectorPosition = 16; - - while(isoSectorPosition < 32) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, isoSectorPosition, 2048, - 0, 1, false, dev.Timeout, out _); - - // If sector cannot be read, break here - if(sense) - break; - - // If sector does not contain "CD001" magic, break - if(isoSector[1] != 0x43 || - isoSector[2] != 0x44 || - isoSector[3] != 0x30 || - isoSector[4] != 0x30 || - isoSector[5] != 0x31) - break; - - // If it is PVD or end of descriptor chain, break - if(isoSector[0] == 1 || - isoSector[0] == 255) - break; - - isoSectorPosition++; - } - - // If it's not an ISO9660 PVD, return - if(isoSector[0] != 1 || - isoSector[1] != 0x43 || - isoSector[2] != 0x44 || - isoSector[3] != 0x30 || - isoSector[4] != 0x30 || - isoSector[5] != 0x31) - return; - - uint rootStart = BitConverter.ToUInt32(isoSector, 158); - uint rootLength = BitConverter.ToUInt32(isoSector, 166); - - if(rootStart == 0 || - rootLength == 0) - return; - - rootLength /= 2048; - - try - { - using var rootMs = new MemoryStream(); - - for(uint i = 0; i < rootLength; i++) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, rootStart + i, 2048, - 0, 1, false, dev.Timeout, out _); - - if(sense) - break; - - rootMs.Write(isoSector, 0, 2048); - } - - isoSector = rootMs.ToArray(); - } - catch - { - return; - } - - if(isoSector.Length < 2048) - return; - - int rootPos = 0; - uint pcdStart = 0; - uint pcdLength = 0; - - while(isoSector[rootPos] > 0 && - rootPos < isoSector.Length && - rootPos + isoSector[rootPos] <= isoSector.Length) - { - int nameLen = isoSector[rootPos + 32]; - byte[] tmpName = new byte[nameLen]; - Array.Copy(isoSector, rootPos + 33, tmpName, 0, nameLen); - string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); - - if(name.EndsWith(";1", StringComparison.InvariantCulture)) - name = name.Substring(0, name.Length - 2); - - if(name == "PHOTO_CD" && - (isoSector[rootPos + 25] & 0x02) == 0x02) - { - pcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2); - pcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048; - } - - rootPos += isoSector[rootPos]; - } - - if(pcdLength > 0) - { - try - { - using var pcdMs = new MemoryStream(); - - for(uint i = 0; i < pcdLength; i++) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, pcdStart + i, - 2048, 0, 1, false, dev.Timeout, out _); - - if(sense) - break; - - pcdMs.Write(isoSector, 0, 2048); - } - - isoSector = pcdMs.ToArray(); - } - catch - { - return; - } - - if(isoSector.Length < 2048) - return; - - for(int pi = 0; pi < pcdLength; pi++) - { - int pcdPos = pi * 2048; - uint infoPos = 0; - - while(isoSector[pcdPos] > 0 && - pcdPos < isoSector.Length && - pcdPos + isoSector[pcdPos] <= isoSector.Length) - { - int nameLen = isoSector[pcdPos + 32]; - byte[] tmpName = new byte[nameLen]; - Array.Copy(isoSector, pcdPos + 33, tmpName, 0, nameLen); - string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); - - if(name.EndsWith(";1", StringComparison.InvariantCulture)) - name = name.Substring(0, name.Length - 2); - - if(name == "INFO.PCD") - { - infoPos = BitConverter.ToUInt32(isoSector, pcdPos + 2); - - break; - } - - pcdPos += isoSector[pcdPos]; - } - - if(infoPos > 0) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, infoPos, 2048, - 0, 1, false, dev.Timeout, out _); - - if(sense) - break; - - byte[] systemId = new byte[8]; - Array.Copy(isoSector, 0, systemId, 0, 8); - - string id = StringHandlers.CToString(systemId).TrimEnd(); - - switch(id) - { - case "PHOTO_CD": - mediaType = MediaType.PCD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Photo CD description file, setting disc type to Photo CD."); - - return; - } - } - } - } - - break; - - // Other recordables will not be checked - case MediaType.CDRW: - case MediaType.CDMRW: - case MediaType.DDCDR: - case MediaType.DDCDRW: - case MediaType.DVDR: - case MediaType.DVDRW: - case MediaType.DVDPR: - case MediaType.DVDPRW: - case MediaType.DVDPRWDL: - case MediaType.DVDRDL: - case MediaType.DVDPRDL: - case MediaType.DVDRAM: - case MediaType.DVDRWDL: - case MediaType.DVDDownload: - case MediaType.HDDVDRAM: - case MediaType.HDDVDR: - case MediaType.HDDVDRW: - case MediaType.HDDVDRDL: - case MediaType.HDDVDRWDL: - case MediaType.BDR: - case MediaType.BDRE: - case MediaType.BDRXL: - case MediaType.BDREXL: return; - } - - if(sector0 == null) - return; - - switch(mediaType) - { - case MediaType.CD: - case MediaType.CDDA: - case MediaType.CDPLUS: - case MediaType.CDROM: - case MediaType.CDROMXA: - // TODO: Pippin requires interpreting Apple Partition Map, reading HFS and checking for Pippin signatures + if(mediaType == MediaType.DVDROM) { - if(CD.DecodeIPBin(sector0).HasValue) + sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, + out _); + + if(!sense) { - mediaType = MediaType.MEGACD; + PFI.PhysicalFormatInformation? pfi = PFI.Decode(cmdBuf, mediaType); - AaruConsole.DebugWriteLine("Media detection", - "Found Mega/Sega CD IP.BIN, setting disc type to Mega CD."); - - return; - } - - if(Saturn.DecodeIPBin(sector0).HasValue) - { - mediaType = MediaType.SATURNCD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Sega Saturn IP.BIN, setting disc type to Saturn CD."); - - return; - } - - // Are GDR detectable ??? - if(Dreamcast.DecodeIPBin(sector0).HasValue) - { - mediaType = MediaType.GDROM; - - AaruConsole.DebugWriteLine("Media detection", - "Found Sega Dreamcast IP.BIN, setting disc type to GD-ROM."); - - return; - } - - if(ps2BootSectors is { Length: 0x6000 }) - { - // The decryption key is applied as XOR. As first byte is originally always NULL, it gives us the key :) - byte decryptByte = ps2BootSectors[0]; - - for(int i = 0; i < 0x6000; i++) - ps2BootSectors[i] ^= decryptByte; - - string ps2BootSectorsHash = Sha256Context.Data(ps2BootSectors, out _); - - AaruConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}", - ps2BootSectorsHash); - - if(ps2BootSectorsHash == PS2_PAL_HASH || - ps2BootSectorsHash == PS2_NTSC_HASH || - ps2BootSectorsHash == PS2_JAPANESE_HASH) + if(pfi != null) { - mediaType = MediaType.PS2CD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Sony PlayStation 2 boot sectors, setting disc type to PS2 CD."); - - goto hasPs2CdBoot; - } - } - - if(sector0 != null) - { - byte[] syncBytes = new byte[7]; - Array.Copy(sector0, 0, syncBytes, 0, 7); - - if(_operaId.SequenceEqual(syncBytes)) - { - mediaType = MediaType.ThreeDO; - - AaruConsole.DebugWriteLine("Media detection", - "Found Opera filesystem, setting disc type to 3DO."); - - return; - } - - if(_fmTownsBootId.SequenceEqual(syncBytes)) - { - mediaType = MediaType.FMTOWNS; - - AaruConsole.DebugWriteLine("Media detection", - "Found FM-Towns boot, setting disc type to FM-Towns."); - - return; - } - } - - if(playdia1 != null && - playdia2 != null) - { - byte[] pd1 = new byte[_playdiaCopyright.Length]; - byte[] pd2 = new byte[_playdiaCopyright.Length]; - - Array.Copy(playdia1, 38, pd1, 0, pd1.Length); - Array.Copy(playdia2, 0, pd2, 0, pd1.Length); - - if(_playdiaCopyright.SequenceEqual(pd1) && - _playdiaCopyright.SequenceEqual(pd2)) - { - mediaType = MediaType.Playdia; - - AaruConsole.DebugWriteLine("Media detection", - "Found Playdia copyright, setting disc type to Playdia."); - - return; - } - } - - if(secondDataSectorNotZero != null) - { - byte[] pce = new byte[_pcEngineSignature.Length]; - Array.Copy(secondDataSectorNotZero, 32, pce, 0, pce.Length); - - if(_pcEngineSignature.SequenceEqual(pce)) - { - mediaType = MediaType.SuperCDROM2; - - AaruConsole.DebugWriteLine("Media detection", - "Found PC-Engine CD signature, setting disc type to Super CD-ROM²."); - - return; - } - } - - if(firstDataSectorNotZero != null) - { - byte[] pcfx = new byte[_pcFxSignature.Length]; - Array.Copy(firstDataSectorNotZero, 0, pcfx, 0, pcfx.Length); - - if(_pcFxSignature.SequenceEqual(pcfx)) - { - mediaType = MediaType.PCFX; - - AaruConsole.DebugWriteLine("Media detection", - "Found PC-FX copyright, setting disc type to PC-FX."); - - return; - } - } - - if(firstTrackSecondSessionAudio != null) - { - byte[] jaguar = new byte[_atariSignature.Length]; - - for(int i = 0; i + jaguar.Length <= firstTrackSecondSessionAudio.Length; i += 2) - { - Array.Copy(firstTrackSecondSessionAudio, i, jaguar, 0, jaguar.Length); - - if(!_atariSignature.SequenceEqual(jaguar)) - continue; - - mediaType = MediaType.JaguarCD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Atari signature, setting disc type to Jaguar CD."); - - break; - } - } - - if(firstTrackSecondSession?.Length >= 2336) - { - byte[] milcd = new byte[2048]; - Array.Copy(firstTrackSecondSession, 24, milcd, 0, 2048); - - if(Dreamcast.DecodeIPBin(milcd).HasValue) - { - mediaType = MediaType.MilCD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Sega Dreamcast IP.BIN on second session, setting disc type to MilCD."); - - return; - } - } - - // TODO: Detect black and white VideoNow - // TODO: Detect VideoNow XP - if(IsVideoNowColor(videoNowColorFrame)) - { - mediaType = MediaType.VideoNowColor; - - AaruConsole.DebugWriteLine("Media detection", - "Found VideoNow! Color frame, setting disc type to VideoNow Color."); - - return; - } - - // Check CD+G, CD+EG and CD+MIDI - if(mediaType == MediaType.CDDA) - { - sense = dev.ReadCd(out byte[] subBuf, out _, 150, 96, 8, MmcSectorTypes.Cdda, false, false, - false, MmcHeaderCodes.None, false, false, MmcErrorField.None, - MmcSubchannel.Raw, dev.Timeout, out _); - - if(!sense) - { - bool cdg = false; - bool cdeg = false; - bool cdmidi = false; - - for(int i = 0; i < 8; i++) + switch(pfi.Value.DiskCategory) { - byte[] tmpSub = new byte[96]; - Array.Copy(subBuf, i * 96, tmpSub, 0, 96); - DetectRwPackets(tmpSub, out bool cdgPacket, out bool cdegPacket, out bool cdmidiPacket); + case DiskCategory.DVDPR: + mediaType = MediaType.DVDPR; - if(cdgPacket) - cdg = true; + break; + case DiskCategory.DVDPRDL: + mediaType = MediaType.DVDPRDL; - if(cdegPacket) - cdeg = true; + break; + case DiskCategory.DVDPRW: + mediaType = MediaType.DVDPRW; - if(cdmidiPacket) - cdmidi = true; + break; + case DiskCategory.DVDPRWDL: + mediaType = MediaType.DVDPRWDL; + + break; + case DiskCategory.DVDR: + mediaType = pfi.Value.PartVersion >= 6 ? MediaType.DVDRDL : MediaType.DVDR; + + break; + case DiskCategory.DVDRAM: + mediaType = MediaType.DVDRAM; + + break; + case DiskCategory.DVDRW: + mediaType = pfi.Value.PartVersion >= 15 ? MediaType.DVDRWDL : MediaType.DVDRW; + + break; + case DiskCategory.HDDVDR: + mediaType = MediaType.HDDVDR; + + break; + case DiskCategory.HDDVDRAM: + mediaType = MediaType.HDDVDRAM; + + break; + case DiskCategory.HDDVDROM: + mediaType = MediaType.HDDVDROM; + + break; + case DiskCategory.HDDVDRW: + mediaType = MediaType.HDDVDRW; + + break; + case DiskCategory.Nintendo: + mediaType = pfi.Value.DiscSize == DVDSize.Eighty ? MediaType.GOD + : MediaType.WOD; + + break; + case DiskCategory.UMD: + mediaType = MediaType.UMD; + + break; } + } + } - if(cdeg) + sense = dev.ReadDiscStructure(out cmdBuf, out _, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DiscManufacturingInformation, 0, + dev.Timeout, out _); + + if(!sense) + { + if(DMI.IsXbox(cmdBuf)) + { + AaruConsole.DebugWriteLine("Media detection", + "Found Xbox DMI, setting disc type to Xbox Game Disc (XGD)."); + + mediaType = MediaType.XGD; + + return; + } + + if(DMI.IsXbox360(cmdBuf)) + { + // All XGD3 all have the same number of blocks + if(blocks == 25063 || // Locked (or non compatible drive) + blocks == 4229664 || // Xtreme unlock + blocks == 4246304) // Wxripper unlock { - mediaType = MediaType.CDEG; - AaruConsole.DebugWriteLine("Media detection", - "Found enhanced graphics RW packet, setting disc type to CD+EG."); + "Found Xbox 360 DMI with {0} blocks, setting disc type to Xbox 360 Game Disc 3 (XGD3)."); + + mediaType = MediaType.XGD3; return; } - if(cdg) - { - mediaType = MediaType.CDG; + AaruConsole.DebugWriteLine("Media detection", + "Found Xbox 360 DMI with {0} blocks, setting disc type to Xbox 360 Game Disc 2 (XGD2)."); - AaruConsole.DebugWriteLine("Media detection", - "Found graphics RW packet, setting disc type to CD+G."); + mediaType = MediaType.XGD2; - return; - } - - if(cdmidi) - { - mediaType = MediaType.CDMIDI; - - AaruConsole.DebugWriteLine("Media detection", - "Found MIDI RW packet, setting disc type to CD+MIDI."); - - return; - } + return; } } + } - // If it has a PS2 boot area it can still be PS1, so check for SYSTEM.CNF below - hasPs2CdBoot: + break; - // Check if ISO9660 - sense = dev.Read12(out byte[] isoSector, out _, 0, false, false, false, false, 16, 2048, 0, 1, - false, dev.Timeout, out _); + // Recordables will be checked for PhotoCD only + case MediaType.CDR: + // Check if ISO9660 + sense = dev.Read12(out byte[] isoSector, out _, 0, false, false, false, false, 16, 2048, 0, 1, + false, dev.Timeout, out _); - // Sector 16 reads, and contains "CD001" magic? - if(sense || - isoSector[1] != 0x43 || + // Sector 16 reads, and contains "CD001" magic? + if(sense || + isoSector[1] != 0x43 || + isoSector[2] != 0x44 || + isoSector[3] != 0x30 || + isoSector[4] != 0x30 || + isoSector[5] != 0x31) + return; + + // From sectors 16 to 31 + uint isoSectorPosition = 16; + + while(isoSectorPosition < 32) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, isoSectorPosition, 2048, + 0, 1, false, dev.Timeout, out _); + + // If sector cannot be read, break here + if(sense) + break; + + // If sector does not contain "CD001" magic, break + if(isoSector[1] != 0x43 || isoSector[2] != 0x44 || isoSector[3] != 0x30 || isoSector[4] != 0x30 || isoSector[5] != 0x31) - return; + break; - // From sectors 16 to 31 - uint isoSectorPosition = 16; + // If it is PVD or end of descriptor chain, break + if(isoSector[0] == 1 || + isoSector[0] == 255) + break; - while(isoSectorPosition < 32) + isoSectorPosition++; + } + + // If it's not an ISO9660 PVD, return + if(isoSector[0] != 1 || + isoSector[1] != 0x43 || + isoSector[2] != 0x44 || + isoSector[3] != 0x30 || + isoSector[4] != 0x30 || + isoSector[5] != 0x31) + return; + + uint rootStart = BitConverter.ToUInt32(isoSector, 158); + uint rootLength = BitConverter.ToUInt32(isoSector, 166); + + if(rootStart == 0 || + rootLength == 0) + return; + + rootLength /= 2048; + + try + { + using var rootMs = new MemoryStream(); + + for(uint i = 0; i < rootLength; i++) { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, isoSectorPosition, 2048, + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, rootStart + i, 2048, 0, 1, false, dev.Timeout, out _); - // If sector cannot be read, break here if(sense) break; - // If sector does not contain "CD001" magic, break - if(isoSector[1] != 0x43 || - isoSector[2] != 0x44 || - isoSector[3] != 0x30 || - isoSector[4] != 0x30 || - isoSector[5] != 0x31) - break; - - // If it is PVD or end of descriptor chain, break - if(isoSector[0] == 1 || - isoSector[0] == 255) - break; - - isoSectorPosition++; + rootMs.Write(isoSector, 0, 2048); } - // If it's not an ISO9660 PVD, return - if(isoSector[0] != 1 || - isoSector[1] != 0x43 || - isoSector[2] != 0x44 || - isoSector[3] != 0x30 || - isoSector[4] != 0x30 || - isoSector[5] != 0x31) - return; + isoSector = rootMs.ToArray(); + } + catch + { + return; + } - uint rootStart = BitConverter.ToUInt32(isoSector, 158); - uint rootLength = BitConverter.ToUInt32(isoSector, 166); + if(isoSector.Length < 2048) + return; - if(rootStart == 0 || - rootLength == 0) - return; + int rootPos = 0; + uint pcdStart = 0; + uint pcdLength = 0; - rootLength /= 2048; + while(isoSector[rootPos] > 0 && + rootPos < isoSector.Length && + rootPos + isoSector[rootPos] <= isoSector.Length) + { + int nameLen = isoSector[rootPos + 32]; + byte[] tmpName = new byte[nameLen]; + Array.Copy(isoSector, rootPos + 33, tmpName, 0, nameLen); + string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); + if(name.EndsWith(";1", StringComparison.InvariantCulture)) + name = name.Substring(0, name.Length - 2); + + if(name == "PHOTO_CD" && + (isoSector[rootPos + 25] & 0x02) == 0x02) + { + pcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2); + pcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048; + } + + rootPos += isoSector[rootPos]; + } + + if(pcdLength > 0) + { try { - using var rootMs = new MemoryStream(); + using var pcdMs = new MemoryStream(); - for(uint i = 0; i < rootLength; i++) + for(uint i = 0; i < pcdLength; i++) { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, rootStart + i, 2048, - 0, 1, false, dev.Timeout, out _); + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, pcdStart + i, + 2048, 0, 1, false, dev.Timeout, out _); if(sense) break; - rootMs.Write(isoSector, 0, 2048); + pcdMs.Write(isoSector, 0, 2048); } - isoSector = rootMs.ToArray(); + isoSector = pcdMs.ToArray(); } catch { @@ -1817,390 +1391,37 @@ namespace Aaru.Core.Media.Detection if(isoSector.Length < 2048) return; - List rootEntries = new List(); - uint ngcdIplStart = 0; - uint ngcdIplLength = 0; - uint vcdStart = 0; - uint vcdLength = 0; - uint pcdStart = 0; - uint pcdLength = 0; - uint ps1Start = 0; - uint ps1Length = 0; - - for(int ri = 0; ri < rootLength; ri++) + for(int pi = 0; pi < pcdLength; pi++) { - int rootPos = ri * 2048; + int pcdPos = pi * 2048; + uint infoPos = 0; - while(rootPos < isoSector.Length && - isoSector[rootPos] > 0 && - rootPos + isoSector[rootPos] <= isoSector.Length) + while(isoSector[pcdPos] > 0 && + pcdPos < isoSector.Length && + pcdPos + isoSector[pcdPos] <= isoSector.Length) { - int nameLen = isoSector[rootPos + 32]; + int nameLen = isoSector[pcdPos + 32]; byte[] tmpName = new byte[nameLen]; - Array.Copy(isoSector, rootPos + 33, tmpName, 0, nameLen); + Array.Copy(isoSector, pcdPos + 33, tmpName, 0, nameLen); string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); if(name.EndsWith(";1", StringComparison.InvariantCulture)) name = name.Substring(0, name.Length - 2); - rootEntries.Add(name); - - switch(name) + if(name == "INFO.PCD") { - case "IPL.TXT": - ngcdIplStart = BitConverter.ToUInt32(isoSector, rootPos + 2); - ngcdIplLength = BitConverter.ToUInt32(isoSector, rootPos + 10); + infoPos = BitConverter.ToUInt32(isoSector, pcdPos + 2); - break; - - case "VCD" when (isoSector[rootPos + 25] & 0x02) == 0x02: - case "SVCD" when (isoSector[rootPos + 25] & 0x02) == 0x02: - case "HQVCD" when (isoSector[rootPos + 25] & 0x02) == 0x02: - vcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2); - vcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048; - - break; - case "PHOTO_CD" when (isoSector[rootPos + 25] & 0x02) == 0x02: - pcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2); - pcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048; - - break; - case "SYSTEM.CNF": - ps1Start = BitConverter.ToUInt32(isoSector, rootPos + 2); - ps1Length = BitConverter.ToUInt32(isoSector, rootPos + 10); - - break; - } - - rootPos += isoSector[rootPos]; - } - } - - if(rootEntries.Count == 0) - return; - - if(rootEntries.Contains("CD32.TM")) - { - mediaType = MediaType.CD32; - - AaruConsole.DebugWriteLine("Media detection", - "Found CD32.TM file in root, setting disc type to Amiga CD32."); - - return; - } - - if(rootEntries.Contains("CDTV.TM")) - { - mediaType = MediaType.CDTV; - - AaruConsole.DebugWriteLine("Media detection", - "Found CDTV.TM file in root, setting disc type to Commodore CDTV."); - - return; - } - - // "IPL.TXT" length - if(ngcdIplLength > 0) - { - uint ngcdSectors = ngcdIplLength / 2048; - - if(ngcdIplLength % 2048 > 0) - ngcdSectors++; - - string iplTxt; - - // Read "IPL.TXT" - try - { - using var ngcdMs = new MemoryStream(); - - for(uint i = 0; i < ngcdSectors; i++) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, - ngcdIplStart + i, 2048, 0, 1, false, dev.Timeout, out _); - - if(sense) - break; - - ngcdMs.Write(isoSector, 0, 2048); - } - - isoSector = ngcdMs.ToArray(); - iplTxt = Encoding.ASCII.GetString(isoSector); - } - catch - { - iplTxt = null; - } - - // Check "IPL.TXT" lines - if(iplTxt != null) - { - using var sr = new StringReader(iplTxt); - - bool correctNeoGeoCd = true; - int lineNumber = 0; - - while(sr.Peek() > 0) - { - string line = sr.ReadLine(); - - // End of file - if(line is null || - line.Length == 0) - { - if(lineNumber == 0) - correctNeoGeoCd = false; - - break; - } - - // Split line by comma - string[] split = line.Split(','); - - // Empty line - if(split.Length == 0) - continue; - - // More than 3 entries - if(split.Length != 3) - { - if(line[0] < 0x20) - break; - - correctNeoGeoCd = false; - - break; - } - - // Split filename - string[] split2 = split[0].Split('.'); - - // Filename must have two parts, name and extension - if(split2.Length != 2) - { - correctNeoGeoCd = false; - - break; - } - - // Name must be smaller or equal to 8 characters - if(split2[0].Length > 8) - { - correctNeoGeoCd = false; - - break; - } - - // Extension must be smaller or equal to 8 characters - if(split2[1].Length > 3) - { - correctNeoGeoCd = false; - - break; - } - - // Second part must be a single digit - if(split[1].Length != 1 || - !byte.TryParse(split[1], out _)) - { - correctNeoGeoCd = false; - - break; - } - - // Third part must be bigger or equal to 1 and smaller or equal to 8 - if(split[2].Length < 1 || - split[2].Length > 8) - { - correctNeoGeoCd = false; - - break; - } - - try - { - _ = Convert.ToUInt32(split[2], 16); - } - catch - { - correctNeoGeoCd = false; - - break; - } - - lineNumber++; - } - - if(correctNeoGeoCd) - { - mediaType = MediaType.NeoGeoCD; - - AaruConsole.DebugWriteLine("Media detection", - "Found correct IPL.TXT file in root, setting disc type to Neo Geo CD."); - - return; - } - } - } - - if(vcdLength > 0) - { - try - { - using var vcdMs = new MemoryStream(); - - for(uint i = 0; i < vcdLength; i++) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, vcdStart + i, - 2048, 0, 1, false, dev.Timeout, out _); - - if(sense) - break; - - vcdMs.Write(isoSector, 0, 2048); - } - - isoSector = vcdMs.ToArray(); - } - catch - { - return; - } - - if(isoSector.Length < 2048) - return; - - uint infoPos = 0; - - for(int vi = 0; vi < vcdLength; vi++) - { - int vcdPos = vi * 2048; - - while(vcdPos < isoSector.Length && - isoSector[vcdPos] > 0 && - vcdPos + isoSector[vcdPos] <= isoSector.Length) - { - int nameLen = isoSector[vcdPos + 32]; - byte[] tmpName = new byte[nameLen]; - Array.Copy(isoSector, vcdPos + 33, tmpName, 0, nameLen); - string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); - - if(name.EndsWith(";1", StringComparison.InvariantCulture)) - name = name.Substring(0, name.Length - 2); - - if(name == "INFO.VCD" || - name == "INFO.SVD") - { - infoPos = BitConverter.ToUInt32(isoSector, vcdPos + 2); - - break; - } - - vcdPos += isoSector[vcdPos]; - } - } - - if(infoPos > 0) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, infoPos, 2048, 0, 1, - false, dev.Timeout, out _); - - if(sense) break; - - byte[] systemId = new byte[8]; - Array.Copy(isoSector, 0, systemId, 0, 8); - - string id = StringHandlers.CToString(systemId).TrimEnd(); - - switch(id) - { - case "VIDEO_CD": - mediaType = MediaType.VCD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Video CD description file, setting disc type to Video CD."); - - return; - case "SUPERVCD": - mediaType = MediaType.SVCD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Super Video CD description file, setting disc type to Super Video CD."); - - break; - case "HQ-VCD": - mediaType = MediaType.CVD; - - AaruConsole.DebugWriteLine("Media detection", - "Found China Video Disc description file, setting disc type to China Video Disc."); - - break; - } - } - } - - if(pcdLength > 0) - { - try - { - using var pcdMs = new MemoryStream(); - - for(uint i = 0; i < pcdLength; i++) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, pcdStart + i, - 2048, 0, 1, false, dev.Timeout, out _); - - if(sense) - break; - - pcdMs.Write(isoSector, 0, 2048); } - isoSector = pcdMs.ToArray(); - } - catch - { - return; - } - - if(isoSector.Length < 2048) - return; - - uint infoPos = 0; - - for(int pi = 0; pi < pcdLength; pi++) - { - int pcdPos = pi * 2048; - - while(pcdPos < isoSector.Length && - isoSector[pcdPos] > 0 && - pcdPos + isoSector[pcdPos] <= isoSector.Length) - { - int nameLen = isoSector[pcdPos + 32]; - byte[] tmpName = new byte[nameLen]; - Array.Copy(isoSector, pcdPos + 33, tmpName, 0, nameLen); - string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); - - if(name.EndsWith(";1", StringComparison.InvariantCulture)) - name = name.Substring(0, name.Length - 2); - - if(name == "INFO.PCD") - { - infoPos = BitConverter.ToUInt32(isoSector, pcdPos + 2); - - break; - } - - pcdPos += isoSector[pcdPos]; - } + pcdPos += isoSector[pcdPos]; } if(infoPos > 0) { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, infoPos, 2048, 0, 1, - false, dev.Timeout, out _); + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, infoPos, 2048, + 0, 1, false, dev.Timeout, out _); if(sense) break; @@ -2222,318 +1443,1096 @@ namespace Aaru.Core.Media.Detection } } } - - // "SYSTEM.CNF" length - if(ps1Length > 0) - { - uint ps1Sectors = ps1Length / 2048; - - if(ps1Length % 2048 > 0) - ps1Sectors++; - - string ps1Txt; - - // Read "SYSTEM.CNF" - try - { - using var ps1Ms = new MemoryStream(); - - for(uint i = 0; i < ps1Sectors; i++) - { - sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, ps1Start + i, - 2048, 0, 1, false, dev.Timeout, out _); - - if(sense) - break; - - ps1Ms.Write(isoSector, 0, 2048); - } - - isoSector = ps1Ms.ToArray(); - ps1Txt = Encoding.ASCII.GetString(isoSector); - } - catch - { - ps1Txt = null; - } - - // Check "SYSTEM.CNF" lines - if(ps1Txt != null) - { - using var sr = new StringReader(ps1Txt); - - string ps1BootFile = null; - string ps2BootFile = null; - - while(sr.Peek() > 0) - { - string line = sr.ReadLine(); - - // End of file - if(line is null || - line.Length == 0) - break; - - line = line.Replace(" ", ""); - - if(line.StartsWith("BOOT=cdrom:", StringComparison.InvariantCultureIgnoreCase)) - { - ps1BootFile = line.Substring(11); - - if(ps1BootFile.StartsWith('\\')) - ps1BootFile = ps1BootFile.Substring(1); - - if(ps1BootFile.EndsWith(";1", StringComparison.InvariantCultureIgnoreCase)) - ps1BootFile = ps1BootFile.Substring(0, ps1BootFile.Length - 2); - - break; - } - - if(line.StartsWith("BOOT2=cdrom0:", StringComparison.InvariantCultureIgnoreCase)) - { - ps2BootFile = line.Substring(13); - - if(ps2BootFile.StartsWith('\\')) - ps2BootFile = ps2BootFile.Substring(1); - - if(ps2BootFile.EndsWith(";1", StringComparison.InvariantCultureIgnoreCase)) - ps2BootFile = ps2BootFile.Substring(0, ps2BootFile.Length - 2); - - break; - } - } - - if(ps1BootFile != null && - rootEntries.Contains(ps1BootFile.ToUpperInvariant())) - { - mediaType = MediaType.PS1CD; - - AaruConsole.DebugWriteLine("Media detection", - "Found correct SYSTEM.CNF file in root pointing to existing file in root, setting disc type to PlayStation CD."); - } - - if(ps2BootFile != null && - rootEntries.Contains(ps2BootFile.ToUpperInvariant())) - { - mediaType = MediaType.PS2CD; - - AaruConsole.DebugWriteLine("Media detection", - "Found correct SYSTEM.CNF file in root pointing to existing file in root, setting disc type to PlayStation 2 CD."); - } - } - } - - break; } - // TODO: Check for CD-i Ready - case MediaType.CDI: break; - case MediaType.DVDROM: - case MediaType.HDDVDROM: - case MediaType.BDROM: - case MediaType.UHDBD: - case MediaType.Unknown: - // TODO: Nuon requires reading the filesystem, searching for a file called "/NUON/NUON.RUN" - if(ps2BootSectors is { Length: 0x6000 }) - { - // The decryption key is applied as XOR. As first byte is originally always NULL, it gives us the key :) - byte decryptByte = ps2BootSectors[0]; + break; - for(int i = 0; i < 0x6000; i++) - ps2BootSectors[i] ^= decryptByte; - - string ps2BootSectorsHash = Sha256Context.Data(ps2BootSectors, out _); - - AaruConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}", - ps2BootSectorsHash); - - if(ps2BootSectorsHash == PS2_PAL_HASH || - ps2BootSectorsHash == PS2_NTSC_HASH || - ps2BootSectorsHash == PS2_JAPANESE_HASH) - { - AaruConsole.DebugWriteLine("Media detection", - "Found Sony PlayStation 2 boot sectors, setting disc type to PS2 DVD."); - - mediaType = MediaType.PS2DVD; - } - } - - if(sector1 != null) - { - byte[] tmp = new byte[_ps3Id.Length]; - Array.Copy(sector1, 0, tmp, 0, tmp.Length); - - if(tmp.SequenceEqual(_ps3Id)) - switch(mediaType) - { - case MediaType.BDROM: - AaruConsole.DebugWriteLine("Media detection", - "Found Sony PlayStation 3 boot sectors, setting disc type to PS3 Blu-ray."); - - mediaType = MediaType.PS3BD; - - break; - case MediaType.DVDROM: - AaruConsole.DebugWriteLine("Media detection", - "Found Sony PlayStation 3 boot sectors, setting disc type to PS3 DVD."); - - mediaType = MediaType.PS3DVD; - - break; - } - - tmp = new byte[_ps4Id.Length]; - Array.Copy(sector1, 512, tmp, 0, tmp.Length); - - if(tmp.SequenceEqual(_ps4Id) && - mediaType == MediaType.BDROM) - { - mediaType = MediaType.PS4BD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Sony PlayStation 4 boot sectors, setting disc type to PS4 Blu-ray."); - } - } - - if(blurayDi != null && - blurayDi?.Units?.Length > 0 && - blurayDi?.Units[0].DiscTypeIdentifier != null) - { - string blurayType = StringHandlers.CToString(blurayDi?.Units[0].DiscTypeIdentifier); - - switch(blurayType) - { - case "XG4": - AaruConsole.DebugWriteLine("Media detection", - "Blu-ray type set to \"XG4\", setting disc type to Xbox One Disc (XGD4)."); - - mediaType = MediaType.XGD4; - - break; - - // TODO: PS5 - case "BDU": - if(sector1 != null) - { - byte[] tmp = new byte[_ps5Id.Length]; - Array.Copy(sector1, 1024, tmp, 0, tmp.Length); - - if(tmp.SequenceEqual(_ps5Id)) - { - mediaType = MediaType.PS5BD; - - AaruConsole.DebugWriteLine("Media detection", - "Found Sony PlayStation 5 boot sectors, setting disc type to PS5 Ultra HD Blu-ray."); - - break; - } - } - - AaruConsole.DebugWriteLine("Media detection", - "Blu-ray type set to \"BDU\", setting disc type to Ultra HD Blu-ray."); - - mediaType = MediaType.UHDBD; - - break; - } - } - - break; - } + // Other recordables will not be checked + case MediaType.CDRW: + case MediaType.CDMRW: + case MediaType.DDCDR: + case MediaType.DDCDRW: + case MediaType.DVDR: + case MediaType.DVDRW: + case MediaType.DVDPR: + case MediaType.DVDPRW: + case MediaType.DVDPRWDL: + case MediaType.DVDRDL: + case MediaType.DVDPRDL: + case MediaType.DVDRAM: + case MediaType.DVDRWDL: + case MediaType.DVDDownload: + case MediaType.HDDVDRAM: + case MediaType.HDDVDR: + case MediaType.HDDVDRW: + case MediaType.HDDVDRDL: + case MediaType.HDDVDRWDL: + case MediaType.BDR: + case MediaType.BDRE: + case MediaType.BDRXL: + case MediaType.BDREXL: return; } - static void DetectRwPackets(byte[] subchannel, out bool cdgPacket, out bool cdegPacket, out bool cdmidiPacket) + if(sector0 == null) + return; + + switch(mediaType) { - cdgPacket = false; - cdegPacket = false; - cdmidiPacket = false; - - byte[] cdSubRwPack1 = new byte[24]; - byte[] cdSubRwPack2 = new byte[24]; - byte[] cdSubRwPack3 = new byte[24]; - byte[] cdSubRwPack4 = new byte[24]; - - int i = 0; - - for(int j = 0; j < 24; j++) - cdSubRwPack1[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack2[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack3[j] = (byte)(subchannel[i++] & 0x3F); - - for(int j = 0; j < 24; j++) - cdSubRwPack4[j] = (byte)(subchannel[i++] & 0x3F); - - switch(cdSubRwPack1[0]) + case MediaType.CD: + case MediaType.CDDA: + case MediaType.CDPLUS: + case MediaType.CDROM: + case MediaType.CDROMXA: + // TODO: Pippin requires interpreting Apple Partition Map, reading HFS and checking for Pippin signatures { - case 0x08: - case 0x09: - cdgPacket = true; + if(CD.DecodeIPBin(sector0).HasValue) + { + mediaType = MediaType.MEGACD; - break; - case 0x0A: - cdegPacket = true; + AaruConsole.DebugWriteLine("Media detection", + "Found Mega/Sega CD IP.BIN, setting disc type to Mega CD."); - break; - case 0x38: - cdmidiPacket = true; + return; + } - break; + if(Saturn.DecodeIPBin(sector0).HasValue) + { + mediaType = MediaType.SATURNCD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Sega Saturn IP.BIN, setting disc type to Saturn CD."); + + return; + } + + // Are GDR detectable ??? + if(Dreamcast.DecodeIPBin(sector0).HasValue) + { + mediaType = MediaType.GDROM; + + AaruConsole.DebugWriteLine("Media detection", + "Found Sega Dreamcast IP.BIN, setting disc type to GD-ROM."); + + return; + } + + if(ps2BootSectors is { Length: 0x6000 }) + { + // The decryption key is applied as XOR. As first byte is originally always NULL, it gives us the key :) + byte decryptByte = ps2BootSectors[0]; + + for(int i = 0; i < 0x6000; i++) + ps2BootSectors[i] ^= decryptByte; + + string ps2BootSectorsHash = Sha256Context.Data(ps2BootSectors, out _); + + AaruConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}", + ps2BootSectorsHash); + + if(ps2BootSectorsHash == PS2_PAL_HASH || + ps2BootSectorsHash == PS2_NTSC_HASH || + ps2BootSectorsHash == PS2_JAPANESE_HASH) + { + mediaType = MediaType.PS2CD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Sony PlayStation 2 boot sectors, setting disc type to PS2 CD."); + + goto hasPs2CdBoot; + } + } + + if(sector0 != null) + { + byte[] syncBytes = new byte[7]; + Array.Copy(sector0, 0, syncBytes, 0, 7); + + if(_operaId.SequenceEqual(syncBytes)) + { + mediaType = MediaType.ThreeDO; + + AaruConsole.DebugWriteLine("Media detection", + "Found Opera filesystem, setting disc type to 3DO."); + + return; + } + + if(_fmTownsBootId.SequenceEqual(syncBytes)) + { + mediaType = MediaType.FMTOWNS; + + AaruConsole.DebugWriteLine("Media detection", + "Found FM-Towns boot, setting disc type to FM-Towns."); + + return; + } + } + + if(playdia1 != null && + playdia2 != null) + { + byte[] pd1 = new byte[_playdiaCopyright.Length]; + byte[] pd2 = new byte[_playdiaCopyright.Length]; + + Array.Copy(playdia1, 38, pd1, 0, pd1.Length); + Array.Copy(playdia2, 0, pd2, 0, pd1.Length); + + if(_playdiaCopyright.SequenceEqual(pd1) && + _playdiaCopyright.SequenceEqual(pd2)) + { + mediaType = MediaType.Playdia; + + AaruConsole.DebugWriteLine("Media detection", + "Found Playdia copyright, setting disc type to Playdia."); + + return; + } + } + + if(secondDataSectorNotZero != null) + { + byte[] pce = new byte[_pcEngineSignature.Length]; + Array.Copy(secondDataSectorNotZero, 32, pce, 0, pce.Length); + + if(_pcEngineSignature.SequenceEqual(pce)) + { + mediaType = MediaType.SuperCDROM2; + + AaruConsole.DebugWriteLine("Media detection", + "Found PC-Engine CD signature, setting disc type to Super CD-ROM²."); + + return; + } + } + + if(firstDataSectorNotZero != null) + { + byte[] pcfx = new byte[_pcFxSignature.Length]; + Array.Copy(firstDataSectorNotZero, 0, pcfx, 0, pcfx.Length); + + if(_pcFxSignature.SequenceEqual(pcfx)) + { + mediaType = MediaType.PCFX; + + AaruConsole.DebugWriteLine("Media detection", + "Found PC-FX copyright, setting disc type to PC-FX."); + + return; + } + } + + if(firstTrackSecondSessionAudio != null) + { + byte[] jaguar = new byte[_atariSignature.Length]; + + for(int i = 0; i + jaguar.Length <= firstTrackSecondSessionAudio.Length; i += 2) + { + Array.Copy(firstTrackSecondSessionAudio, i, jaguar, 0, jaguar.Length); + + if(!_atariSignature.SequenceEqual(jaguar)) + continue; + + mediaType = MediaType.JaguarCD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Atari signature, setting disc type to Jaguar CD."); + + break; + } + } + + if(firstTrackSecondSession?.Length >= 2336) + { + byte[] milcd = new byte[2048]; + Array.Copy(firstTrackSecondSession, 24, milcd, 0, 2048); + + if(Dreamcast.DecodeIPBin(milcd).HasValue) + { + mediaType = MediaType.MilCD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Sega Dreamcast IP.BIN on second session, setting disc type to MilCD."); + + return; + } + } + + // TODO: Detect black and white VideoNow + // TODO: Detect VideoNow XP + if(IsVideoNowColor(videoNowColorFrame)) + { + mediaType = MediaType.VideoNowColor; + + AaruConsole.DebugWriteLine("Media detection", + "Found VideoNow! Color frame, setting disc type to VideoNow Color."); + + return; + } + + // Check CD+G, CD+EG and CD+MIDI + if(mediaType == MediaType.CDDA) + { + sense = dev.ReadCd(out byte[] subBuf, out _, 150, 96, 8, MmcSectorTypes.Cdda, false, false, + false, MmcHeaderCodes.None, false, false, MmcErrorField.None, + MmcSubchannel.Raw, dev.Timeout, out _); + + if(!sense) + { + bool cdg = false; + bool cdeg = false; + bool cdmidi = false; + + for(int i = 0; i < 8; i++) + { + byte[] tmpSub = new byte[96]; + Array.Copy(subBuf, i * 96, tmpSub, 0, 96); + DetectRwPackets(tmpSub, out bool cdgPacket, out bool cdegPacket, out bool cdmidiPacket); + + if(cdgPacket) + cdg = true; + + if(cdegPacket) + cdeg = true; + + if(cdmidiPacket) + cdmidi = true; + } + + if(cdeg) + { + mediaType = MediaType.CDEG; + + AaruConsole.DebugWriteLine("Media detection", + "Found enhanced graphics RW packet, setting disc type to CD+EG."); + + return; + } + + if(cdg) + { + mediaType = MediaType.CDG; + + AaruConsole.DebugWriteLine("Media detection", + "Found graphics RW packet, setting disc type to CD+G."); + + return; + } + + if(cdmidi) + { + mediaType = MediaType.CDMIDI; + + AaruConsole.DebugWriteLine("Media detection", + "Found MIDI RW packet, setting disc type to CD+MIDI."); + + return; + } + } + } + + // If it has a PS2 boot area it can still be PS1, so check for SYSTEM.CNF below + hasPs2CdBoot: + + // Check if ISO9660 + sense = dev.Read12(out byte[] isoSector, out _, 0, false, false, false, false, 16, 2048, 0, 1, + false, dev.Timeout, out _); + + // Sector 16 reads, and contains "CD001" magic? + if(sense || + isoSector[1] != 0x43 || + isoSector[2] != 0x44 || + isoSector[3] != 0x30 || + isoSector[4] != 0x30 || + isoSector[5] != 0x31) + return; + + // From sectors 16 to 31 + uint isoSectorPosition = 16; + + while(isoSectorPosition < 32) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, isoSectorPosition, 2048, + 0, 1, false, dev.Timeout, out _); + + // If sector cannot be read, break here + if(sense) + break; + + // If sector does not contain "CD001" magic, break + if(isoSector[1] != 0x43 || + isoSector[2] != 0x44 || + isoSector[3] != 0x30 || + isoSector[4] != 0x30 || + isoSector[5] != 0x31) + break; + + // If it is PVD or end of descriptor chain, break + if(isoSector[0] == 1 || + isoSector[0] == 255) + break; + + isoSectorPosition++; + } + + // If it's not an ISO9660 PVD, return + if(isoSector[0] != 1 || + isoSector[1] != 0x43 || + isoSector[2] != 0x44 || + isoSector[3] != 0x30 || + isoSector[4] != 0x30 || + isoSector[5] != 0x31) + return; + + uint rootStart = BitConverter.ToUInt32(isoSector, 158); + uint rootLength = BitConverter.ToUInt32(isoSector, 166); + + if(rootStart == 0 || + rootLength == 0) + return; + + rootLength /= 2048; + + try + { + using var rootMs = new MemoryStream(); + + for(uint i = 0; i < rootLength; i++) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, rootStart + i, 2048, + 0, 1, false, dev.Timeout, out _); + + if(sense) + break; + + rootMs.Write(isoSector, 0, 2048); + } + + isoSector = rootMs.ToArray(); + } + catch + { + return; + } + + if(isoSector.Length < 2048) + return; + + List rootEntries = new List(); + uint ngcdIplStart = 0; + uint ngcdIplLength = 0; + uint vcdStart = 0; + uint vcdLength = 0; + uint pcdStart = 0; + uint pcdLength = 0; + uint ps1Start = 0; + uint ps1Length = 0; + + for(int ri = 0; ri < rootLength; ri++) + { + int rootPos = ri * 2048; + + while(rootPos < isoSector.Length && + isoSector[rootPos] > 0 && + rootPos + isoSector[rootPos] <= isoSector.Length) + { + int nameLen = isoSector[rootPos + 32]; + byte[] tmpName = new byte[nameLen]; + Array.Copy(isoSector, rootPos + 33, tmpName, 0, nameLen); + string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); + + if(name.EndsWith(";1", StringComparison.InvariantCulture)) + name = name.Substring(0, name.Length - 2); + + rootEntries.Add(name); + + switch(name) + { + case "IPL.TXT": + ngcdIplStart = BitConverter.ToUInt32(isoSector, rootPos + 2); + ngcdIplLength = BitConverter.ToUInt32(isoSector, rootPos + 10); + + break; + + case "VCD" when (isoSector[rootPos + 25] & 0x02) == 0x02: + case "SVCD" when (isoSector[rootPos + 25] & 0x02) == 0x02: + case "HQVCD" when (isoSector[rootPos + 25] & 0x02) == 0x02: + vcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2); + vcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048; + + break; + case "PHOTO_CD" when (isoSector[rootPos + 25] & 0x02) == 0x02: + pcdStart = BitConverter.ToUInt32(isoSector, rootPos + 2); + pcdLength = BitConverter.ToUInt32(isoSector, rootPos + 10) / 2048; + + break; + case "SYSTEM.CNF": + ps1Start = BitConverter.ToUInt32(isoSector, rootPos + 2); + ps1Length = BitConverter.ToUInt32(isoSector, rootPos + 10); + + break; + } + + rootPos += isoSector[rootPos]; + } + } + + if(rootEntries.Count == 0) + return; + + if(rootEntries.Contains("CD32.TM")) + { + mediaType = MediaType.CD32; + + AaruConsole.DebugWriteLine("Media detection", + "Found CD32.TM file in root, setting disc type to Amiga CD32."); + + return; + } + + if(rootEntries.Contains("CDTV.TM")) + { + mediaType = MediaType.CDTV; + + AaruConsole.DebugWriteLine("Media detection", + "Found CDTV.TM file in root, setting disc type to Commodore CDTV."); + + return; + } + + // "IPL.TXT" length + if(ngcdIplLength > 0) + { + uint ngcdSectors = ngcdIplLength / 2048; + + if(ngcdIplLength % 2048 > 0) + ngcdSectors++; + + string iplTxt; + + // Read "IPL.TXT" + try + { + using var ngcdMs = new MemoryStream(); + + for(uint i = 0; i < ngcdSectors; i++) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, + ngcdIplStart + i, 2048, 0, 1, false, dev.Timeout, out _); + + if(sense) + break; + + ngcdMs.Write(isoSector, 0, 2048); + } + + isoSector = ngcdMs.ToArray(); + iplTxt = Encoding.ASCII.GetString(isoSector); + } + catch + { + iplTxt = null; + } + + // Check "IPL.TXT" lines + if(iplTxt != null) + { + using var sr = new StringReader(iplTxt); + + bool correctNeoGeoCd = true; + int lineNumber = 0; + + while(sr.Peek() > 0) + { + string line = sr.ReadLine(); + + // End of file + if(line is null || + line.Length == 0) + { + if(lineNumber == 0) + correctNeoGeoCd = false; + + break; + } + + // Split line by comma + string[] split = line.Split(','); + + // Empty line + if(split.Length == 0) + continue; + + // More than 3 entries + if(split.Length != 3) + { + if(line[0] < 0x20) + break; + + correctNeoGeoCd = false; + + break; + } + + // Split filename + string[] split2 = split[0].Split('.'); + + // Filename must have two parts, name and extension + if(split2.Length != 2) + { + correctNeoGeoCd = false; + + break; + } + + // Name must be smaller or equal to 8 characters + if(split2[0].Length > 8) + { + correctNeoGeoCd = false; + + break; + } + + // Extension must be smaller or equal to 8 characters + if(split2[1].Length > 3) + { + correctNeoGeoCd = false; + + break; + } + + // Second part must be a single digit + if(split[1].Length != 1 || + !byte.TryParse(split[1], out _)) + { + correctNeoGeoCd = false; + + break; + } + + // Third part must be bigger or equal to 1 and smaller or equal to 8 + if(split[2].Length < 1 || + split[2].Length > 8) + { + correctNeoGeoCd = false; + + break; + } + + try + { + _ = Convert.ToUInt32(split[2], 16); + } + catch + { + correctNeoGeoCd = false; + + break; + } + + lineNumber++; + } + + if(correctNeoGeoCd) + { + mediaType = MediaType.NeoGeoCD; + + AaruConsole.DebugWriteLine("Media detection", + "Found correct IPL.TXT file in root, setting disc type to Neo Geo CD."); + + return; + } + } + } + + if(vcdLength > 0) + { + try + { + using var vcdMs = new MemoryStream(); + + for(uint i = 0; i < vcdLength; i++) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, vcdStart + i, + 2048, 0, 1, false, dev.Timeout, out _); + + if(sense) + break; + + vcdMs.Write(isoSector, 0, 2048); + } + + isoSector = vcdMs.ToArray(); + } + catch + { + return; + } + + if(isoSector.Length < 2048) + return; + + uint infoPos = 0; + + for(int vi = 0; vi < vcdLength; vi++) + { + int vcdPos = vi * 2048; + + while(vcdPos < isoSector.Length && + isoSector[vcdPos] > 0 && + vcdPos + isoSector[vcdPos] <= isoSector.Length) + { + int nameLen = isoSector[vcdPos + 32]; + byte[] tmpName = new byte[nameLen]; + Array.Copy(isoSector, vcdPos + 33, tmpName, 0, nameLen); + string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); + + if(name.EndsWith(";1", StringComparison.InvariantCulture)) + name = name.Substring(0, name.Length - 2); + + if(name == "INFO.VCD" || + name == "INFO.SVD") + { + infoPos = BitConverter.ToUInt32(isoSector, vcdPos + 2); + + break; + } + + vcdPos += isoSector[vcdPos]; + } + } + + if(infoPos > 0) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, infoPos, 2048, 0, 1, + false, dev.Timeout, out _); + + if(sense) + break; + + byte[] systemId = new byte[8]; + Array.Copy(isoSector, 0, systemId, 0, 8); + + string id = StringHandlers.CToString(systemId).TrimEnd(); + + switch(id) + { + case "VIDEO_CD": + mediaType = MediaType.VCD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Video CD description file, setting disc type to Video CD."); + + return; + case "SUPERVCD": + mediaType = MediaType.SVCD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Super Video CD description file, setting disc type to Super Video CD."); + + break; + case "HQ-VCD": + mediaType = MediaType.CVD; + + AaruConsole.DebugWriteLine("Media detection", + "Found China Video Disc description file, setting disc type to China Video Disc."); + + break; + } + } + } + + if(pcdLength > 0) + { + try + { + using var pcdMs = new MemoryStream(); + + for(uint i = 0; i < pcdLength; i++) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, pcdStart + i, + 2048, 0, 1, false, dev.Timeout, out _); + + if(sense) + break; + + pcdMs.Write(isoSector, 0, 2048); + } + + isoSector = pcdMs.ToArray(); + } + catch + { + return; + } + + if(isoSector.Length < 2048) + return; + + uint infoPos = 0; + + for(int pi = 0; pi < pcdLength; pi++) + { + int pcdPos = pi * 2048; + + while(pcdPos < isoSector.Length && + isoSector[pcdPos] > 0 && + pcdPos + isoSector[pcdPos] <= isoSector.Length) + { + int nameLen = isoSector[pcdPos + 32]; + byte[] tmpName = new byte[nameLen]; + Array.Copy(isoSector, pcdPos + 33, tmpName, 0, nameLen); + string name = StringHandlers.CToString(tmpName).ToUpperInvariant(); + + if(name.EndsWith(";1", StringComparison.InvariantCulture)) + name = name.Substring(0, name.Length - 2); + + if(name == "INFO.PCD") + { + infoPos = BitConverter.ToUInt32(isoSector, pcdPos + 2); + + break; + } + + pcdPos += isoSector[pcdPos]; + } + } + + if(infoPos > 0) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, infoPos, 2048, 0, 1, + false, dev.Timeout, out _); + + if(sense) + break; + + byte[] systemId = new byte[8]; + Array.Copy(isoSector, 0, systemId, 0, 8); + + string id = StringHandlers.CToString(systemId).TrimEnd(); + + switch(id) + { + case "PHOTO_CD": + mediaType = MediaType.PCD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Photo CD description file, setting disc type to Photo CD."); + + return; + } + } + } + + // "SYSTEM.CNF" length + if(ps1Length > 0) + { + uint ps1Sectors = ps1Length / 2048; + + if(ps1Length % 2048 > 0) + ps1Sectors++; + + string ps1Txt; + + // Read "SYSTEM.CNF" + try + { + using var ps1Ms = new MemoryStream(); + + for(uint i = 0; i < ps1Sectors; i++) + { + sense = dev.Read12(out isoSector, out _, 0, false, false, false, false, ps1Start + i, + 2048, 0, 1, false, dev.Timeout, out _); + + if(sense) + break; + + ps1Ms.Write(isoSector, 0, 2048); + } + + isoSector = ps1Ms.ToArray(); + ps1Txt = Encoding.ASCII.GetString(isoSector); + } + catch + { + ps1Txt = null; + } + + // Check "SYSTEM.CNF" lines + if(ps1Txt != null) + { + using var sr = new StringReader(ps1Txt); + + string ps1BootFile = null; + string ps2BootFile = null; + + while(sr.Peek() > 0) + { + string line = sr.ReadLine(); + + // End of file + if(line is null || + line.Length == 0) + break; + + line = line.Replace(" ", ""); + + if(line.StartsWith("BOOT=cdrom:", StringComparison.InvariantCultureIgnoreCase)) + { + ps1BootFile = line.Substring(11); + + if(ps1BootFile.StartsWith('\\')) + ps1BootFile = ps1BootFile.Substring(1); + + if(ps1BootFile.EndsWith(";1", StringComparison.InvariantCultureIgnoreCase)) + ps1BootFile = ps1BootFile.Substring(0, ps1BootFile.Length - 2); + + break; + } + + if(line.StartsWith("BOOT2=cdrom0:", StringComparison.InvariantCultureIgnoreCase)) + { + ps2BootFile = line.Substring(13); + + if(ps2BootFile.StartsWith('\\')) + ps2BootFile = ps2BootFile.Substring(1); + + if(ps2BootFile.EndsWith(";1", StringComparison.InvariantCultureIgnoreCase)) + ps2BootFile = ps2BootFile.Substring(0, ps2BootFile.Length - 2); + + break; + } + } + + if(ps1BootFile != null && + rootEntries.Contains(ps1BootFile.ToUpperInvariant())) + { + mediaType = MediaType.PS1CD; + + AaruConsole.DebugWriteLine("Media detection", + "Found correct SYSTEM.CNF file in root pointing to existing file in root, setting disc type to PlayStation CD."); + } + + if(ps2BootFile != null && + rootEntries.Contains(ps2BootFile.ToUpperInvariant())) + { + mediaType = MediaType.PS2CD; + + AaruConsole.DebugWriteLine("Media detection", + "Found correct SYSTEM.CNF file in root pointing to existing file in root, setting disc type to PlayStation 2 CD."); + } + } + } + + break; } - switch(cdSubRwPack2[0]) - { - case 0x08: - case 0x09: - cdgPacket = true; + // TODO: Check for CD-i Ready + case MediaType.CDI: break; + case MediaType.DVDROM: + case MediaType.HDDVDROM: + case MediaType.BDROM: + case MediaType.UHDBD: + case MediaType.Unknown: + // TODO: Nuon requires reading the filesystem, searching for a file called "/NUON/NUON.RUN" + if(ps2BootSectors is { Length: 0x6000 }) + { + // The decryption key is applied as XOR. As first byte is originally always NULL, it gives us the key :) + byte decryptByte = ps2BootSectors[0]; - break; - case 0x0A: - cdegPacket = true; + for(int i = 0; i < 0x6000; i++) + ps2BootSectors[i] ^= decryptByte; - break; - case 0x38: - cdmidiPacket = true; + string ps2BootSectorsHash = Sha256Context.Data(ps2BootSectors, out _); - break; - } + AaruConsole.DebugWriteLine("Media-info Command", "PlayStation 2 boot sectors SHA256: {0}", + ps2BootSectorsHash); - switch(cdSubRwPack3[0]) - { - case 0x08: - case 0x09: - cdgPacket = true; + if(ps2BootSectorsHash == PS2_PAL_HASH || + ps2BootSectorsHash == PS2_NTSC_HASH || + ps2BootSectorsHash == PS2_JAPANESE_HASH) + { + AaruConsole.DebugWriteLine("Media detection", + "Found Sony PlayStation 2 boot sectors, setting disc type to PS2 DVD."); - break; - case 0x0A: - cdegPacket = true; + mediaType = MediaType.PS2DVD; + } + } - break; - case 0x38: - cdmidiPacket = true; + if(sector1 != null) + { + byte[] tmp = new byte[_ps3Id.Length]; + Array.Copy(sector1, 0, tmp, 0, tmp.Length); - break; - } + if(tmp.SequenceEqual(_ps3Id)) + switch(mediaType) + { + case MediaType.BDROM: + AaruConsole.DebugWriteLine("Media detection", + "Found Sony PlayStation 3 boot sectors, setting disc type to PS3 Blu-ray."); - switch(cdSubRwPack4[0]) - { - case 0x08: - case 0x09: - cdgPacket = true; + mediaType = MediaType.PS3BD; - break; - case 0x0A: - cdegPacket = true; + break; + case MediaType.DVDROM: + AaruConsole.DebugWriteLine("Media detection", + "Found Sony PlayStation 3 boot sectors, setting disc type to PS3 DVD."); - break; - case 0x38: - cdmidiPacket = true; + mediaType = MediaType.PS3DVD; - break; - } + break; + } + + tmp = new byte[_ps4Id.Length]; + Array.Copy(sector1, 512, tmp, 0, tmp.Length); + + if(tmp.SequenceEqual(_ps4Id) && + mediaType == MediaType.BDROM) + { + mediaType = MediaType.PS4BD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Sony PlayStation 4 boot sectors, setting disc type to PS4 Blu-ray."); + } + } + + if(blurayDi != null && + blurayDi?.Units?.Length > 0 && + blurayDi?.Units[0].DiscTypeIdentifier != null) + { + string blurayType = StringHandlers.CToString(blurayDi?.Units[0].DiscTypeIdentifier); + + switch(blurayType) + { + case "XG4": + AaruConsole.DebugWriteLine("Media detection", + "Blu-ray type set to \"XG4\", setting disc type to Xbox One Disc (XGD4)."); + + mediaType = MediaType.XGD4; + + break; + + // TODO: PS5 + case "BDU": + if(sector1 != null) + { + byte[] tmp = new byte[_ps5Id.Length]; + Array.Copy(sector1, 1024, tmp, 0, tmp.Length); + + if(tmp.SequenceEqual(_ps5Id)) + { + mediaType = MediaType.PS5BD; + + AaruConsole.DebugWriteLine("Media detection", + "Found Sony PlayStation 5 boot sectors, setting disc type to PS5 Ultra HD Blu-ray."); + + break; + } + } + + AaruConsole.DebugWriteLine("Media detection", + "Blu-ray type set to \"BDU\", setting disc type to Ultra HD Blu-ray."); + + mediaType = MediaType.UHDBD; + + break; + } + } + + break; + } + } + + static void DetectRwPackets(byte[] subchannel, out bool cdgPacket, out bool cdegPacket, out bool cdmidiPacket) + { + cdgPacket = false; + cdegPacket = false; + cdmidiPacket = false; + + byte[] cdSubRwPack1 = new byte[24]; + byte[] cdSubRwPack2 = new byte[24]; + byte[] cdSubRwPack3 = new byte[24]; + byte[] cdSubRwPack4 = new byte[24]; + + int i = 0; + + for(int j = 0; j < 24; j++) + cdSubRwPack1[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack2[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack3[j] = (byte)(subchannel[i++] & 0x3F); + + for(int j = 0; j < 24; j++) + cdSubRwPack4[j] = (byte)(subchannel[i++] & 0x3F); + + switch(cdSubRwPack1[0]) + { + case 0x08: + case 0x09: + cdgPacket = true; + + break; + case 0x0A: + cdegPacket = true; + + break; + case 0x38: + cdmidiPacket = true; + + break; + } + + switch(cdSubRwPack2[0]) + { + case 0x08: + case 0x09: + cdgPacket = true; + + break; + case 0x0A: + cdegPacket = true; + + break; + case 0x38: + cdmidiPacket = true; + + break; + } + + switch(cdSubRwPack3[0]) + { + case 0x08: + case 0x09: + cdgPacket = true; + + break; + case 0x0A: + cdegPacket = true; + + break; + case 0x38: + cdmidiPacket = true; + + break; + } + + switch(cdSubRwPack4[0]) + { + case 0x08: + case 0x09: + cdgPacket = true; + + break; + case 0x0A: + cdegPacket = true; + + break; + case 0x38: + cdmidiPacket = true; + + break; } } } \ No newline at end of file diff --git a/Aaru.Core/Media/Info/CompactDisc.cs b/Aaru.Core/Media/Info/CompactDisc.cs index 2207efe23..2ab6fafe9 100644 --- a/Aaru.Core/Media/Info/CompactDisc.cs +++ b/Aaru.Core/Media/Info/CompactDisc.cs @@ -43,301 +43,300 @@ using Aaru.Decoders.CD; using Aaru.Devices; using Device = Aaru.Database.Models.Device; -namespace Aaru.Core.Media.Info +namespace Aaru.Core.Media.Info; + +/// Core operations for retrieving information about CD based media +public static class CompactDisc { - /// Core operations for retrieving information about CD based media - public static class CompactDisc + /// Gets the offset bytes from a Compact Disc + /// Offset entry from database + /// Device entry from database + /// Debug + /// Opened device + /// Detected disk type + /// Dump log if applicable + /// Disc track list + /// UpdateStatus event + /// Drive offset + /// Combined offset + /// Set to true if drive supports PLEXTOR READ CD-DA vendor command + /// true if offset could be found, false otherwise + [SuppressMessage("ReSharper", "TooWideLocalVariableScope")] + public static void GetOffset(CdOffset cdOffset, Device dbDev, bool debug, Aaru.Devices.Device dev, + MediaType dskType, DumpLog dumpLog, Track[] tracks, + UpdateStatusHandler updateStatus, out int? driveOffset, out int? combinedOffset, + out bool supportsPlextorReadCdDa) { - /// Gets the offset bytes from a Compact Disc - /// Offset entry from database - /// Device entry from database - /// Debug - /// Opened device - /// Detected disk type - /// Dump log if applicable - /// Disc track list - /// UpdateStatus event - /// Drive offset - /// Combined offset - /// Set to true if drive supports PLEXTOR READ CD-DA vendor command - /// true if offset could be found, false otherwise - [SuppressMessage("ReSharper", "TooWideLocalVariableScope")] - public static void GetOffset(CdOffset cdOffset, Device dbDev, bool debug, Aaru.Devices.Device dev, - MediaType dskType, DumpLog dumpLog, Track[] tracks, - UpdateStatusHandler updateStatus, out int? driveOffset, out int? combinedOffset, - out bool supportsPlextorReadCdDa) + byte[] cmdBuf; + bool sense; + int minute; + int second; + int frame; + byte[] sectorSync; + byte[] tmpBuf; + int lba; + int diff; + Track dataTrack = default; + Track audioTrack = default; + bool offsetFound = false; + const uint sectorSize = 2352; + driveOffset = cdOffset?.Offset * 4; + combinedOffset = null; + supportsPlextorReadCdDa = false; + + if(dskType != MediaType.VideoNowColor) { - byte[] cmdBuf; - bool sense; - int minute; - int second; - int frame; - byte[] sectorSync; - byte[] tmpBuf; - int lba; - int diff; - Track dataTrack = default; - Track audioTrack = default; - bool offsetFound = false; - const uint sectorSize = 2352; - driveOffset = cdOffset?.Offset * 4; - combinedOffset = null; - supportsPlextorReadCdDa = false; - - if(dskType != MediaType.VideoNowColor) + if(tracks.Any(t => t.Type != TrackType.Audio)) { - if(tracks.Any(t => t.Type != TrackType.Audio)) + dataTrack = tracks.FirstOrDefault(t => t.Type != TrackType.Audio); + + if(dataTrack != null) { - dataTrack = tracks.FirstOrDefault(t => t.Type != TrackType.Audio); - - if(dataTrack != null) + // Build sync + sectorSync = new byte[] { - // Build sync - sectorSync = new byte[] + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 + }; + + tmpBuf = new byte[sectorSync.Length]; + + // Ensure to be out of the pregap, or multi-session discs give funny values + uint wantedLba = (uint)(dataTrack.StartSector + 151); + + // Plextor READ CDDA + if(dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || + dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || + dev.Manufacturer.ToLowerInvariant() == "plextor") + { + sense = dev.PlextorReadCdDa(out cmdBuf, out _, wantedLba, sectorSize, 3, + PlextorSubchannel.None, dev.Timeout, out _); + + if(!sense && + !dev.Error) { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 - }; + supportsPlextorReadCdDa = true; - tmpBuf = new byte[sectorSync.Length]; - - // Ensure to be out of the pregap, or multi-session discs give funny values - uint wantedLba = (uint)(dataTrack.StartSector + 151); - - // Plextor READ CDDA - if(dbDev?.ATAPI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || - dbDev?.SCSI?.RemovableMedias?.Any(d => d.SupportsPlextorReadCDDA == true) == true || - dev.Manufacturer.ToLowerInvariant() == "plextor") - { - sense = dev.PlextorReadCdDa(out cmdBuf, out _, wantedLba, sectorSize, 3, - PlextorSubchannel.None, dev.Timeout, out _); - - if(!sense && - !dev.Error) + for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++) { - supportsPlextorReadCdDa = true; + Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length); - for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++) - { - Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length); + if(!tmpBuf.SequenceEqual(sectorSync)) + continue; - if(!tmpBuf.SequenceEqual(sectorSync)) - continue; + // De-scramble M and S + minute = cmdBuf[i + 12] ^ 0x01; + second = cmdBuf[i + 13] ^ 0x80; + frame = cmdBuf[i + 14]; - // De-scramble M and S - minute = cmdBuf[i + 12] ^ 0x01; - second = cmdBuf[i + 13] ^ 0x80; - frame = cmdBuf[i + 14]; + // Convert to binary + minute = (minute / 16 * 10) + (minute & 0x0F); + second = (second / 16 * 10) + (second & 0x0F); + frame = (frame / 16 * 10) + (frame & 0x0F); - // Convert to binary - minute = (minute / 16 * 10) + (minute & 0x0F); - second = (second / 16 * 10) + (second & 0x0F); - frame = (frame / 16 * 10) + (frame & 0x0F); + // Calculate the first found LBA + lba = (minute * 60 * 75) + (second * 75) + frame - 150; - // Calculate the first found LBA - lba = (minute * 60 * 75) + (second * 75) + frame - 150; + // Calculate the difference between the found LBA and the requested one + diff = (int)wantedLba - lba; - // Calculate the difference between the found LBA and the requested one - diff = (int)wantedLba - lba; + combinedOffset = i + (2352 * diff); + offsetFound = true; - combinedOffset = i + (2352 * diff); - offsetFound = true; - - break; - } + break; } } + } - if(!offsetFound && - (debug || dbDev?.ATAPI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true || - dbDev?.SCSI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true || - dbDev?.SCSI?.MultiMediaDevice?.TestedMedia?.Any(d => d.CanReadCdScrambled == true) == - true || dev.Manufacturer.ToLowerInvariant() == "hl-dt-st")) - { - sense = dev.ReadCd(out cmdBuf, out _, wantedLba, sectorSize, 3, MmcSectorTypes.Cdda, false, - false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); - - if(!sense && - !dev.Error) - { - // Clear cache - for(int i = 0; i < 63; i++) - { - sense = dev.ReadCd(out _, out _, (uint)(wantedLba + 3 + (16 * i)), sectorSize, 16, - MmcSectorTypes.AllTypes, false, false, false, - MmcHeaderCodes.None, true, false, MmcErrorField.None, - MmcSubchannel.None, dev.Timeout, out _); - - if(sense || dev.Error) - break; - } - - dev.ReadCd(out cmdBuf, out _, wantedLba, sectorSize, 3, MmcSectorTypes.Cdda, false, + if(!offsetFound && + (debug || dbDev?.ATAPI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true || + dbDev?.SCSI?.RemovableMedias?.Any(d => d.CanReadCdScrambled == true) == true || + dbDev?.SCSI?.MultiMediaDevice?.TestedMedia?.Any(d => d.CanReadCdScrambled == true) == + true || dev.Manufacturer.ToLowerInvariant() == "hl-dt-st")) + { + sense = dev.ReadCd(out cmdBuf, out _, wantedLba, sectorSize, 3, MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); - for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++) - { - Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length); - - if(!tmpBuf.SequenceEqual(sectorSync)) - continue; - - // De-scramble M and S - minute = cmdBuf[i + 12] ^ 0x01; - second = cmdBuf[i + 13] ^ 0x80; - frame = cmdBuf[i + 14]; - - // Convert to binary - minute = (minute / 16 * 10) + (minute & 0x0F); - second = (second / 16 * 10) + (second & 0x0F); - frame = (frame / 16 * 10) + (frame & 0x0F); - - // Calculate the first found LBA - lba = (minute * 60 * 75) + (second * 75) + frame - 150; - - // Calculate the difference between the found LBA and the requested one - diff = (int)wantedLba - lba; - - combinedOffset = i + (2352 * diff); - offsetFound = true; + if(!sense && + !dev.Error) + { + // Clear cache + for(int i = 0; i < 63; i++) + { + sense = dev.ReadCd(out _, out _, (uint)(wantedLba + 3 + (16 * i)), sectorSize, 16, + MmcSectorTypes.AllTypes, false, false, false, + MmcHeaderCodes.None, true, false, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); + if(sense || dev.Error) break; - } + } + + dev.ReadCd(out cmdBuf, out _, wantedLba, sectorSize, 3, MmcSectorTypes.Cdda, false, + false, false, MmcHeaderCodes.None, true, false, MmcErrorField.None, + MmcSubchannel.None, dev.Timeout, out _); + + for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++) + { + Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length); + + if(!tmpBuf.SequenceEqual(sectorSync)) + continue; + + // De-scramble M and S + minute = cmdBuf[i + 12] ^ 0x01; + second = cmdBuf[i + 13] ^ 0x80; + frame = cmdBuf[i + 14]; + + // Convert to binary + minute = (minute / 16 * 10) + (minute & 0x0F); + second = (second / 16 * 10) + (second & 0x0F); + frame = (frame / 16 * 10) + (frame & 0x0F); + + // Calculate the first found LBA + lba = (minute * 60 * 75) + (second * 75) + frame - 150; + + // Calculate the difference between the found LBA and the requested one + diff = (int)wantedLba - lba; + + combinedOffset = i + (2352 * diff); + offsetFound = true; + + break; } } } } - - if(offsetFound) - return; - - // Try to get another the offset some other way, we need an audio track just after a data track, same session - - for(int i = 1; i < tracks.Length; i++) - { - if(tracks[i - 1].Type == TrackType.Audio || - tracks[i].Type != TrackType.Audio) - continue; - - dataTrack = tracks[i - 1]; - audioTrack = tracks[i]; - - break; - } - - if(dataTrack is null || - audioTrack is null) - return; - - // Found them - sense = dev.ReadCd(out cmdBuf, out _, (uint)audioTrack.StartSector, sectorSize, 3, - MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, - MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); - - if(sense || dev.Error) - return; - - dataTrack.EndSector += 150; - - // Calculate MSF - minute = (int)dataTrack.EndSector / 4500; - second = ((int)dataTrack.EndSector - (minute * 4500)) / 75; - frame = (int)dataTrack.EndSector - (minute * 4500) - (second * 75); - - dataTrack.EndSector -= 150; - - // Convert to BCD - minute = ((minute / 10) << 4) + (minute % 10); - second = ((second / 10) << 4) + (second % 10); - frame = ((frame / 10) << 4) + (frame % 10); - - // Scramble M and S - minute ^= 0x01; - second ^= 0x80; - - // Build sync - sectorSync = new byte[] - { - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, (byte)minute, (byte)second, - (byte)frame - }; - - tmpBuf = new byte[sectorSync.Length]; - - for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++) - { - Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length); - - if(!tmpBuf.SequenceEqual(sectorSync)) - continue; - - combinedOffset = i + 2352; - offsetFound = true; - - break; - } - - if(offsetFound || audioTrack.Pregap <= 0) - return; - - sense = dev.ReadCd(out byte[] dataBuf, out _, (uint)dataTrack.EndSector, sectorSize, 1, - MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, - MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); - - if(sense || dev.Error) - return; - - for(int i = 0; i < dataBuf.Length; i++) - dataBuf[i] ^= Sector.ScrambleTable[i]; - - for(int i = 0; i < 2352; i++) - { - byte[] dataSide = new byte[2352 - i]; - byte[] audioSide = new byte[2352 - i]; - - Array.Copy(dataBuf, i, dataSide, 0, dataSide.Length); - Array.Copy(cmdBuf, 0, audioSide, 0, audioSide.Length); - - if(!dataSide.SequenceEqual(audioSide)) - continue; - - combinedOffset = audioSide.Length; - - break; - } } - else - { - byte[] videoNowColorFrame = new byte[9 * sectorSize]; - sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.AllTypes, false, false, true, - MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + if(offsetFound) + return; + + // Try to get another the offset some other way, we need an audio track just after a data track, same session + + for(int i = 1; i < tracks.Length; i++) + { + if(tracks[i - 1].Type == TrackType.Audio || + tracks[i].Type != TrackType.Audio) + continue; + + dataTrack = tracks[i - 1]; + audioTrack = tracks[i]; + + break; + } + + if(dataTrack is null || + audioTrack is null) + return; + + // Found them + sense = dev.ReadCd(out cmdBuf, out _, (uint)audioTrack.StartSector, sectorSize, 3, + MmcSectorTypes.Cdda, false, false, false, MmcHeaderCodes.None, true, false, + MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + + if(sense || dev.Error) + return; + + dataTrack.EndSector += 150; + + // Calculate MSF + minute = (int)dataTrack.EndSector / 4500; + second = ((int)dataTrack.EndSector - (minute * 4500)) / 75; + frame = (int)dataTrack.EndSector - (minute * 4500) - (second * 75); + + dataTrack.EndSector -= 150; + + // Convert to BCD + minute = ((minute / 10) << 4) + (minute % 10); + second = ((second / 10) << 4) + (second % 10); + frame = ((frame / 10) << 4) + (frame % 10); + + // Scramble M and S + minute ^= 0x01; + second ^= 0x80; + + // Build sync + sectorSync = new byte[] + { + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, (byte)minute, (byte)second, + (byte)frame + }; + + tmpBuf = new byte[sectorSync.Length]; + + for(int i = 0; i < cmdBuf.Length - sectorSync.Length; i++) + { + Array.Copy(cmdBuf, i, tmpBuf, 0, sectorSync.Length); + + if(!tmpBuf.SequenceEqual(sectorSync)) + continue; + + combinedOffset = i + 2352; + offsetFound = true; + + break; + } + + if(offsetFound || audioTrack.Pregap <= 0) + return; + + sense = dev.ReadCd(out byte[] dataBuf, out _, (uint)dataTrack.EndSector, sectorSize, 1, + MmcSectorTypes.AllTypes, false, false, true, MmcHeaderCodes.AllHeaders, true, true, + MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); + + if(sense || dev.Error) + return; + + for(int i = 0; i < dataBuf.Length; i++) + dataBuf[i] ^= Sector.ScrambleTable[i]; + + for(int i = 0; i < 2352; i++) + { + byte[] dataSide = new byte[2352 - i]; + byte[] audioSide = new byte[2352 - i]; + + Array.Copy(dataBuf, i, dataSide, 0, dataSide.Length); + Array.Copy(cmdBuf, 0, audioSide, 0, audioSide.Length); + + if(!dataSide.SequenceEqual(audioSide)) + continue; + + combinedOffset = audioSide.Length; + + break; + } + } + else + { + byte[] videoNowColorFrame = new byte[9 * sectorSize]; + + sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.AllTypes, false, false, true, + MmcHeaderCodes.AllHeaders, true, true, MmcErrorField.None, MmcSubchannel.None, + dev.Timeout, out _); + + if(sense || dev.Error) + { + sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.Cdda, false, false, true, + MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None, dev.Timeout, out _); if(sense || dev.Error) { - sense = dev.ReadCd(out cmdBuf, out _, 0, sectorSize, 9, MmcSectorTypes.Cdda, false, false, true, - MmcHeaderCodes.None, true, true, MmcErrorField.None, MmcSubchannel.None, - dev.Timeout, out _); + videoNowColorFrame = null; + } + } - if(sense || dev.Error) - { - videoNowColorFrame = null; - } - } - - if(videoNowColorFrame is null) - { - dumpLog?.WriteLine("Could not find VideoNow Color frame offset, dump may not be correct."); - updateStatus?.Invoke("Could not find VideoNow Color frame offset, dump may not be correct."); - } - else - { - combinedOffset = MMC.GetVideoNowColorOffset(videoNowColorFrame); - dumpLog?.WriteLine($"VideoNow Color frame is offset {combinedOffset} bytes."); - updateStatus?.Invoke($"VideoNow Color frame is offset {combinedOffset} bytes."); - } + if(videoNowColorFrame is null) + { + dumpLog?.WriteLine("Could not find VideoNow Color frame offset, dump may not be correct."); + updateStatus?.Invoke("Could not find VideoNow Color frame offset, dump may not be correct."); + } + else + { + combinedOffset = MMC.GetVideoNowColorOffset(videoNowColorFrame); + dumpLog?.WriteLine($"VideoNow Color frame is offset {combinedOffset} bytes."); + updateStatus?.Invoke($"VideoNow Color frame is offset {combinedOffset} bytes."); } } } diff --git a/Aaru.Core/Media/Info/ScsiInfo.cs b/Aaru.Core/Media/Info/ScsiInfo.cs index 50f389914..49e8ac642 100644 --- a/Aaru.Core/Media/Info/ScsiInfo.cs +++ b/Aaru.Core/Media/Info/ScsiInfo.cs @@ -51,98 +51,90 @@ using DMI = Aaru.Decoders.Xbox.DMI; using DVDDecryption = Aaru.Decryption.DVD.Dump; using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry; -namespace Aaru.Core.Media.Info +namespace Aaru.Core.Media.Info; + +/// Retrieves information from a SCSI device +public sealed class ScsiInfo { - /// Retrieves information from a SCSI device - public sealed class ScsiInfo + /// Initializes this class with the specific device, and fills in the information + /// Device + public ScsiInfo(Device dev) { - /// Initializes this class with the specific device, and fills in the information - /// Device - public ScsiInfo(Device dev) + if(dev.Type != DeviceType.SCSI && + dev.Type != DeviceType.ATAPI) + return; + + MediaType = MediaType.Unknown; + MediaInserted = false; + int resets = 0; + bool sense; + byte[] cmdBuf; + byte[] senseBuf; + bool containsFloppyPage = false; + int sessions = 1; + int firstTrackLastSession = 1; + + if(dev.IsRemovable) { - if(dev.Type != DeviceType.SCSI && - dev.Type != DeviceType.ATAPI) - return; + deviceGotReset: + sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); - MediaType = MediaType.Unknown; - MediaInserted = false; - int resets = 0; - bool sense; - byte[] cmdBuf; - byte[] senseBuf; - bool containsFloppyPage = false; - int sessions = 1; - int firstTrackLastSession = 1; - - if(dev.IsRemovable) + if(sense) { - deviceGotReset: - sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); + DecodedSense? decSense = Sense.Decode(senseBuf); - if(sense) + if(decSense.HasValue) { - DecodedSense? decSense = Sense.Decode(senseBuf); - - if(decSense.HasValue) + // Just retry, for 5 times + if(decSense?.ASC == 0x29) { - // Just retry, for 5 times - if(decSense?.ASC == 0x29) - { - resets++; + resets++; - if(resets < 5) - goto deviceGotReset; + if(resets < 5) + goto deviceGotReset; + } + + if(decSense?.ASC == 0x3A) + { + int leftRetries = 5; + + while(leftRetries > 0) + { + //AaruConsole.WriteLine("\rWaiting for drive to become ready"); + Thread.Sleep(2000); + sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); + + if(!sense) + break; + + leftRetries--; } - if(decSense?.ASC == 0x3A) + if(sense) { - int leftRetries = 5; + AaruConsole.ErrorWriteLine("Please insert media in drive"); - while(leftRetries > 0) - { - //AaruConsole.WriteLine("\rWaiting for drive to become ready"); - Thread.Sleep(2000); - sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); - - if(!sense) - break; - - leftRetries--; - } - - if(sense) - { - AaruConsole.ErrorWriteLine("Please insert media in drive"); - - return; - } + return; } - else if(decSense?.ASC == 0x04 && - decSense?.ASCQ == 0x01) + } + else if(decSense?.ASC == 0x04 && + decSense?.ASCQ == 0x01) + { + int leftRetries = 10; + + while(leftRetries > 0) { - int leftRetries = 10; + //AaruConsole.WriteLine("\rWaiting for drive to become ready"); + Thread.Sleep(2000); + sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); - while(leftRetries > 0) - { - //AaruConsole.WriteLine("\rWaiting for drive to become ready"); - Thread.Sleep(2000); - sense = dev.ScsiTestUnitReady(out senseBuf, dev.Timeout, out _); + if(!sense) + break; - if(!sense) - break; - - leftRetries--; - } - - if(sense) - { - AaruConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", - Sense.PrettifySense(senseBuf)); - - return; - } + leftRetries--; } - else + + if(sense) { AaruConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", Sense.PrettifySense(senseBuf)); @@ -152,1486 +144,1493 @@ namespace Aaru.Core.Media.Info } else { - AaruConsole.ErrorWriteLine("Unknown testing unit was ready."); + AaruConsole.ErrorWriteLine("Error testing unit was ready:\n{0}", + Sense.PrettifySense(senseBuf)); return; } } + else + { + AaruConsole.ErrorWriteLine("Unknown testing unit was ready."); + + return; + } } + } - MediaInserted = true; + MediaInserted = true; - DeviceInfo = new DeviceInfo(dev); + DeviceInfo = new DeviceInfo(dev); - byte scsiMediumType = 0; - byte scsiDensityCode = 0; + byte scsiMediumType = 0; + byte scsiDensityCode = 0; - if(DeviceInfo.ScsiMode.HasValue) - { - scsiMediumType = (byte)DeviceInfo.ScsiMode.Value.Header.MediumType; + if(DeviceInfo.ScsiMode.HasValue) + { + scsiMediumType = (byte)DeviceInfo.ScsiMode.Value.Header.MediumType; - if(DeviceInfo.ScsiMode?.Header.BlockDescriptors?.Length > 0) - scsiDensityCode = (byte)DeviceInfo.ScsiMode.Value.Header.BlockDescriptors[0].Density; + if(DeviceInfo.ScsiMode?.Header.BlockDescriptors?.Length > 0) + scsiDensityCode = (byte)DeviceInfo.ScsiMode.Value.Header.BlockDescriptors[0].Density; - if(DeviceInfo.ScsiMode.Value.Pages != null) - containsFloppyPage = DeviceInfo.ScsiMode.Value.Pages.Any(p => p.Page == 0x05); - } + if(DeviceInfo.ScsiMode.Value.Pages != null) + containsFloppyPage = DeviceInfo.ScsiMode.Value.Pages.Any(p => p.Page == 0x05); + } - Blocks = 0; - BlockSize = 0; + Blocks = 0; + BlockSize = 0; - switch(dev.ScsiType) - { - case PeripheralDeviceTypes.DirectAccess: - case PeripheralDeviceTypes.MultiMediaDevice: - case PeripheralDeviceTypes.OCRWDevice: - case PeripheralDeviceTypes.OpticalDevice: - case PeripheralDeviceTypes.SimplifiedDevice: - case PeripheralDeviceTypes.WriteOnceDevice: - sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(!sense) - { - ReadCapacity = cmdBuf; - - Blocks = (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & - 0xFFFFFFFF; - - BlockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); - } - - sense = dev.ReadCapacity16(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(!sense) - ReadCapacity16 = cmdBuf; - - if(ReadCapacity == null || - Blocks == 0xFFFFFFFF || - Blocks == 0) - { - if(ReadCapacity16 == null && - Blocks == 0) - if(dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) - { - AaruConsole.ErrorWriteLine("Unable to get media capacity"); - AaruConsole.ErrorWriteLine("{0}", Sense.PrettifySense(senseBuf)); - } - - if(ReadCapacity16 != null) - { - byte[] temp = new byte[8]; - - Array.Copy(cmdBuf, 0, temp, 0, 8); - Array.Reverse(temp); - Blocks = BitConverter.ToUInt64(temp, 0); - BlockSize = (uint)((cmdBuf[8] << 24) + (cmdBuf[9] << 16) + (cmdBuf[10] << 8) + cmdBuf[11]); - } - } - - if(Blocks != 0 && - BlockSize != 0) - Blocks++; - - break; - case PeripheralDeviceTypes.SequentialAccess: - byte[] medBuf; - - sense = dev.ReportDensitySupport(out byte[] seqBuf, out senseBuf, false, dev.Timeout, out _); - - if(!sense) - { - sense = dev.ReportDensitySupport(out medBuf, out senseBuf, true, dev.Timeout, out _); - - if(!sense && - !seqBuf.SequenceEqual(medBuf)) - { - DensitySupport = seqBuf; - DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf); - } - } - - sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _); - - if(!sense) - { - sense = dev.ReportDensitySupport(out medBuf, out senseBuf, true, true, dev.Timeout, out _); - - if(!sense && - !seqBuf.SequenceEqual(medBuf)) - { - MediaTypeSupport = medBuf; - MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf); - } - } - - // TODO: Get a machine where 16-byte CDBs don't get DID_ABORT - /* - sense = dev.ReadAttribute(out seqBuf, out senseBuf, ScsiAttributeAction.List, 0, dev.Timeout, out _); - if (sense) - AaruConsole.ErrorWriteLine("SCSI READ ATTRIBUTE:\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - { - DataFile.WriteTo("Media-Info command", outputPrefix, "_scsi_readattribute.bin", "SCSI READ ATTRIBUTE", seqBuf); - } - */ - break; - case PeripheralDeviceTypes.BridgingExpander - when dev.Model.StartsWith("MDM", StringComparison.Ordinal) || - dev.Model.StartsWith("MDH", StringComparison.Ordinal): - sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(!sense) - { - ReadCapacity = cmdBuf; - - Blocks = (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & - 0xFFFFFFFF; - - BlockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); - } - - break; - } - - if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) - { - sense = dev.GetConfiguration(out cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current, dev.Timeout, - out _); - - if(sense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ GET CONFIGURATION:\n{0}", - Sense.PrettifySense(senseBuf)); - - if(dev.IsUsb && - (scsiMediumType == 0x40 || scsiMediumType == 0x41 || scsiMediumType == 0x42)) - MediaType = MediaType.FlashDrive; - } - else - { - MmcConfiguration = cmdBuf; - Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); - - AaruConsole.DebugWriteLine("Media-Info command", "GET CONFIGURATION current profile is {0:X4}h", - ftr.CurrentProfile); - - switch(ftr.CurrentProfile) - { - case 0x0001: - MediaType = MediaType.GENERIC_HDD; - - break; - case 0x0002: - switch(scsiMediumType) - { - case 0x01: - MediaType = MediaType.PD650; - - break; - case 0x41: - switch(Blocks) - { - case 58620544: - MediaType = MediaType.REV120; - - break; - case 17090880: - MediaType = MediaType.REV35; - - break; - case 34185728: - MediaType = MediaType.REV70; - - break; - } - - break; - default: - MediaType = MediaType.Unknown; - - break; - } - - break; - case 0x0005: - MediaType = MediaType.CDMO; - - break; - case 0x0008: - MediaType = MediaType.CD; - - break; - case 0x0009: - MediaType = MediaType.CDR; - - break; - case 0x000A: - MediaType = MediaType.CDRW; - - break; - case 0x0010: - MediaType = MediaType.DVDROM; - - break; - case 0x0011: - MediaType = MediaType.DVDR; - - break; - case 0x0012: - MediaType = MediaType.DVDRAM; - - break; - case 0x0013: - case 0x0014: - MediaType = MediaType.DVDRW; - - break; - case 0x0015: - case 0x0016: - MediaType = MediaType.DVDRDL; - - break; - case 0x0017: - MediaType = MediaType.DVDRWDL; - - break; - case 0x0018: - MediaType = MediaType.DVDDownload; - - break; - case 0x001A: - MediaType = MediaType.DVDPRW; - - break; - case 0x001B: - MediaType = MediaType.DVDPR; - - break; - case 0x0020: - MediaType = MediaType.DDCD; - - break; - case 0x0021: - MediaType = MediaType.DDCDR; - - break; - case 0x0022: - MediaType = MediaType.DDCDRW; - - break; - case 0x002A: - MediaType = MediaType.DVDPRWDL; - - break; - case 0x002B: - MediaType = MediaType.DVDPRDL; - - break; - case 0x0040: - MediaType = MediaType.BDROM; - - break; - case 0x0041: - case 0x0042: - MediaType = MediaType.BDR; - - break; - case 0x0043: - MediaType = MediaType.BDRE; - - break; - case 0x0050: - MediaType = MediaType.HDDVDROM; - - break; - case 0x0051: - MediaType = MediaType.HDDVDR; - - break; - case 0x0052: - MediaType = MediaType.HDDVDRAM; - - break; - case 0x0053: - MediaType = MediaType.HDDVDRW; - - break; - case 0x0058: - MediaType = MediaType.HDDVDRDL; - - break; - case 0x005A: - MediaType = MediaType.HDDVDRWDL; - - break; - } - } - - if(MediaType == MediaType.PD650 && - Blocks == 1281856) - MediaType = MediaType.PD650_WORM; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.RecognizedFormatLayers, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Recognized Format Layers\n{0}", - Sense.PrettifySense(senseBuf)); - else - RecognizedFormatLayers = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.WriteProtectionStatus, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Write Protection Status\n{0}", - Sense.PrettifySense(senseBuf)); - else - WriteProtectionStatus = cmdBuf; - - // More like a drive information - /* - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Capability List\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_capabilitylist.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - */ - - #region All DVD and HD DVD types - if(MediaType == MediaType.DVDDownload || - MediaType == MediaType.DVDPR || - MediaType == MediaType.DVDPRDL || - MediaType == MediaType.DVDPRW || - MediaType == MediaType.DVDPRWDL || - MediaType == MediaType.DVDR || - MediaType == MediaType.DVDRAM || - MediaType == MediaType.DVDRDL || - MediaType == MediaType.DVDROM || - MediaType == MediaType.DVDRW || - MediaType == MediaType.DVDRWDL || - MediaType == MediaType.HDDVDR || - MediaType == MediaType.HDDVDRAM || - MediaType == MediaType.HDDVDRDL || - MediaType == MediaType.HDDVDROM || - MediaType == MediaType.HDDVDRW || - MediaType == MediaType.HDDVDRWDL) - { - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out _); - - if(sense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: PFI\n{0}", - Sense.PrettifySense(senseBuf)); - } - else - { - DvdPfi = cmdBuf; - DecodedPfi = PFI.Decode(cmdBuf, MediaType); - - if(DecodedPfi.HasValue) - if(MediaType == MediaType.DVDROM) - switch(DecodedPfi.Value.DiskCategory) - { - case DiskCategory.DVDPR: - MediaType = MediaType.DVDPR; - - break; - case DiskCategory.DVDPRDL: - MediaType = MediaType.DVDPRDL; - - break; - case DiskCategory.DVDPRW: - MediaType = MediaType.DVDPRW; - - break; - case DiskCategory.DVDPRWDL: - MediaType = MediaType.DVDPRWDL; - - break; - case DiskCategory.DVDR: - MediaType = DecodedPfi.Value.PartVersion >= 6 ? MediaType.DVDRDL - : MediaType.DVDR; - - break; - case DiskCategory.DVDRAM: - MediaType = MediaType.DVDRAM; - - break; - default: - MediaType = MediaType.DVDROM; - - break; - case DiskCategory.DVDRW: - MediaType = DecodedPfi.Value.PartVersion >= 15 ? MediaType.DVDRWDL - : MediaType.DVDRW; - - break; - case DiskCategory.HDDVDR: - MediaType = MediaType.HDDVDR; - - break; - case DiskCategory.HDDVDRAM: - MediaType = MediaType.HDDVDRAM; - - break; - case DiskCategory.HDDVDROM: - MediaType = MediaType.HDDVDROM; - - break; - case DiskCategory.HDDVDRW: - MediaType = MediaType.HDDVDRW; - - break; - case DiskCategory.Nintendo: - MediaType = DecodedPfi.Value.DiscSize == DVDSize.Eighty ? MediaType.GOD - : MediaType.WOD; - - break; - case DiskCategory.UMD: - MediaType = MediaType.UMD; - - break; - } - } - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DiscManufacturingInformation, 0, dev.Timeout, - out _); - - if(sense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DMI\n{0}", - Sense.PrettifySense(senseBuf)); - } - else - { - DvdDmi = cmdBuf; - - if(DMI.IsXbox(cmdBuf)) - { - MediaType = MediaType.XGD; - } - else if(DMI.IsXbox360(cmdBuf)) - { - MediaType = MediaType.XGD2; - - // All XGD3 all have the same number of blocks - if(Blocks == 25063 || // Locked (or non compatible drive) - Blocks == 4229664 || // Xtreme unlock - Blocks == 4246304) // Wxripper unlock - MediaType = MediaType.XGD3; - } - } - } - #endregion All DVD and HD DVD types - - #region DVD-ROM - if(MediaType == MediaType.DVDDownload || - MediaType == MediaType.DVDROM) - { - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.CopyrightInformation, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: CMI\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdCmi = cmdBuf; - } - #endregion DVD-ROM - - switch(MediaType) - { - #region DVD-ROM and HD DVD-ROM - case MediaType.DVDDownload: - case MediaType.DVDROM: - case MediaType.HDDVDROM: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.BurstCuttingArea, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: BCA\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdBca = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdAacs, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DVD AACS\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdAacs = cmdBuf; - - break; - #endregion DVD-ROM and HD DVD-ROM - - #region DVD-RAM and HD DVD-RAM - case MediaType.DVDRAM: - case MediaType.HDDVDRAM: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdramDds, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DDS\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdRamDds = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdramMediumStatus, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Medium Status\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdRamCartridgeStatus = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdramSpareAreaInformation, 0, dev.Timeout, - out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: SAI\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdRamSpareArea = cmdBuf; - - break; - #endregion DVD-RAM and HD DVD-RAM - - #region DVD-R and HD DVD-R - case MediaType.DVDR: - case MediaType.HDDVDR: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.LastBorderOutRmd, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Last-Out Border RMD\n{0}", - Sense.PrettifySense(senseBuf)); - else - LastBorderOutRmd = cmdBuf; - - break; - #endregion DVD-R and HD DVD-R - } - - var dvdDecrypt = new DVDDecryption(dev); - sense = dvdDecrypt.ReadBusKey(out cmdBuf, out senseBuf, CopyrightType.CSS, dev.Timeout, out _); + switch(dev.ScsiType) + { + case PeripheralDeviceTypes.DirectAccess: + case PeripheralDeviceTypes.MultiMediaDevice: + case PeripheralDeviceTypes.OCRWDevice: + case PeripheralDeviceTypes.OpticalDevice: + case PeripheralDeviceTypes.SimplifiedDevice: + case PeripheralDeviceTypes.WriteOnceDevice: + sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); if(!sense) { - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DiscKey, dvdDecrypt.Agid, dev.Timeout, out _); + ReadCapacity = cmdBuf; - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Disc Key\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdDiscKey = cmdBuf; + Blocks = (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & + 0xFFFFFFFF; - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.SectorCopyrightInformation, dvdDecrypt.Agid, - dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Sector CMI\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdSectorCmi = cmdBuf; + BlockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); } - #region Require drive authentication, won't work + sense = dev.ReadCapacity16(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(!sense) + ReadCapacity16 = cmdBuf; + + if(ReadCapacity == null || + Blocks == 0xFFFFFFFF || + Blocks == 0) + { + if(ReadCapacity16 == null && + Blocks == 0) + if(dev.ScsiType != PeripheralDeviceTypes.MultiMediaDevice) + { + AaruConsole.ErrorWriteLine("Unable to get media capacity"); + AaruConsole.ErrorWriteLine("{0}", Sense.PrettifySense(senseBuf)); + } + + if(ReadCapacity16 != null) + { + byte[] temp = new byte[8]; + + Array.Copy(cmdBuf, 0, temp, 0, 8); + Array.Reverse(temp); + Blocks = BitConverter.ToUInt64(temp, 0); + BlockSize = (uint)((cmdBuf[8] << 24) + (cmdBuf[9] << 16) + (cmdBuf[10] << 8) + cmdBuf[11]); + } + } + + if(Blocks != 0 && + BlockSize != 0) + Blocks++; + + break; + case PeripheralDeviceTypes.SequentialAccess: + byte[] medBuf; + + sense = dev.ReportDensitySupport(out byte[] seqBuf, out senseBuf, false, dev.Timeout, out _); + + if(!sense) + { + sense = dev.ReportDensitySupport(out medBuf, out senseBuf, true, dev.Timeout, out _); + + if(!sense && + !seqBuf.SequenceEqual(medBuf)) + { + DensitySupport = seqBuf; + DensitySupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeDensity(seqBuf); + } + } + + sense = dev.ReportDensitySupport(out seqBuf, out senseBuf, true, false, dev.Timeout, out _); + + if(!sense) + { + sense = dev.ReportDensitySupport(out medBuf, out senseBuf, true, true, dev.Timeout, out _); + + if(!sense && + !seqBuf.SequenceEqual(medBuf)) + { + MediaTypeSupport = medBuf; + MediaTypeSupportHeader = Decoders.SCSI.SSC.DensitySupport.DecodeMediumType(seqBuf); + } + } + + // TODO: Get a machine where 16-byte CDBs don't get DID_ABORT /* - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.MediaIdentifier, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Media ID\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_mediaid.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.MediaKeyBlock, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: MKB\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_mkb.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSVolId, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS Volume ID\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacsvolid.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSMediaSerial, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS Media Serial Number\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacssn.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSMediaId, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS Media ID\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacsmediaid.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSMKB, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS MKB\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacsmkb.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSLBAExtents, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS LBA Extents\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacslbaextents.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSMKBCPRM, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS CPRM MKB\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacscprmmkb.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSDataKeys, 0, dev.Timeout, out _); - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS Data Keys\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); - else - DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacsdatakeys.bin", "SCSI READ DISC STRUCTURE", cmdBuf); - */ - #endregion Require drive authentication, won't work - - #region DVD-R and DVD-RW - if(MediaType == MediaType.DVDR || - MediaType == MediaType.DVDRW || - MediaType == MediaType.DVDRDL || - MediaType == MediaType.DVDRWDL) - { - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PreRecordedInfo, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Pre-Recorded Info\n{0}", - Sense.PrettifySense(senseBuf)); - else - { - DvdPreRecordedInfo = cmdBuf; - - DecodedDvdPrePitInformation = PRI.Decode(cmdBuf); - } - } - #endregion DVD-R and DVD-RW - - switch(MediaType) - { - #region DVD-R, DVD-RW and HD DVD-R - case MediaType.DVDR: - case MediaType.DVDRW: - case MediaType.DVDRDL: - case MediaType.DVDRWDL: - case MediaType.HDDVDR: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdrMediaIdentifier, 0, dev.Timeout, - out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DVD-R Media ID\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdrMediaIdentifier = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdrPhysicalInformation, 0, dev.Timeout, - out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DVD-R PFI\n{0}", - Sense.PrettifySense(senseBuf)); - else - { - DvdrPhysicalInformation = cmdBuf; - - DecodedDvdrPfi = PFI.Decode(cmdBuf, MediaType); - } - - break; - #endregion DVD-R, DVD-RW and HD DVD-R - - #region All DVD+ - case MediaType.DVDPR: - case MediaType.DVDPRDL: - case MediaType.DVDPRW: - case MediaType.DVDPRWDL: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.Adip, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: ADIP\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdPlusAdip = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.Dcb, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DCB\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdPlusDcb = cmdBuf; - - break; - #endregion All DVD+ - - #region HD DVD-ROM - case MediaType.HDDVDROM: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.HddvdCopyrightInformation, 0, dev.Timeout, - out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: HD DVD CMI\n{0}", - Sense.PrettifySense(senseBuf)); - else - HddvdCopyrightInformation = cmdBuf; - - break; - #endregion HD DVD-ROM - } - - #region HD DVD-R - if(MediaType == MediaType.HDDVDR) - { - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.HddvdrMediumStatus, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: HD DVD-R Medium Status\n{0}", - Sense.PrettifySense(senseBuf)); - else - HddvdrMediumStatus = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.HddvdrLastRmd, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Last RMD\n{0}", - Sense.PrettifySense(senseBuf)); - else - HddvdrLastRmd = cmdBuf; - } - #endregion HD DVD-R - - #region DVD-R DL, DVD-RW DL, DVD+R DL, DVD+RW DL - if(MediaType == MediaType.DVDPRDL || - MediaType == MediaType.DVDRDL || - MediaType == MediaType.DVDRWDL || - MediaType == MediaType.DVDPRWDL) - { - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DvdrLayerCapacity, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Layer Capacity\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdrLayerCapacity = cmdBuf; - } - #endregion DVD-R DL, DVD-RW DL, DVD+R DL, DVD+RW DL - - switch(MediaType) - { - #region DVD-R DL - case MediaType.DVDRDL: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.MiddleZoneStart, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Middle Zone Start\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdrDlMiddleZoneStart = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.JumpIntervalSize, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Jump Interval Size\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdrDlJumpIntervalSize = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.ManualLayerJumpStartLba, 0, dev.Timeout, - out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Manual Layer Jump Start LBA\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdrDlManualLayerJumpStartLba = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.RemapAnchorPoint, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Remap Anchor Point\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdrDlRemapAnchorPoint = cmdBuf; - - break; - #endregion DVD-R DL - - #region All Blu-ray - case MediaType.BDR: - case MediaType.BDRE: - case MediaType.BDROM: - case MediaType.UHDBD: - case MediaType.BDRXL: - case MediaType.BDREXL: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.DiscInformation, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DI\n{0}", - Sense.PrettifySense(senseBuf)); - else - BlurayDiscInformation = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.Pac, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: PAC\n{0}", - Sense.PrettifySense(senseBuf)); - else - BlurayPac = cmdBuf; - - break; - #endregion All Blu-ray - } - - switch(MediaType) - { - #region BD-ROM only - case MediaType.BDROM: - case MediaType.UHDBD: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdBurstCuttingArea, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: BCA\n{0}", - Sense.PrettifySense(senseBuf)); - else - BlurayBurstCuttingArea = cmdBuf; - - break; - #endregion BD-ROM only - - #region Writable Blu-ray only - case MediaType.BDR: - case MediaType.BDRE: - case MediaType.BDRXL: - case MediaType.BDREXL: - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdDds, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DDS\n{0}", - Sense.PrettifySense(senseBuf)); - else - BlurayDds = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.CartridgeStatus, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Cartridge Status\n{0}", - Sense.PrettifySense(senseBuf)); - else - BlurayCartridgeStatus = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.BdSpareAreaInformation, 0, dev.Timeout, - out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", - "READ DISC STRUCTURE: Spare Area Information\n{0}", - Sense.PrettifySense(senseBuf)); - else - BluraySpareAreaInformation = cmdBuf; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, - MmcDiscStructureFormat.RawDfl, 0, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Raw DFL\n{0}", - Sense.PrettifySense(senseBuf)); - else - BlurayRawDfl = cmdBuf; - - sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, - MmcDiscInformationDataTypes.TrackResources, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC INFORMATION 001b\n{0}", - Sense.PrettifySense(senseBuf)); - else - BlurayTrackResources = cmdBuf; - - sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, - MmcDiscInformationDataTypes.PowResources, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC INFORMATION 010b\n{0}", - Sense.PrettifySense(senseBuf)); - else - BlurayPowResources = cmdBuf; - - break; - #endregion Writable Blu-ray only - - #region CDs - case MediaType.CD: - case MediaType.CDR: - case MediaType.CDROM: - case MediaType.CDRW: - case MediaType.Unknown: - // We discarded all discs that falsify a TOC before requesting a real TOC - // No TOC, no CD (or an empty one) - bool tocSense = dev.ReadTocPmaAtip(out cmdBuf, out senseBuf, false, 0, 0, dev.Timeout, out _); - - if(tocSense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: TOC\n{0}", - Sense.PrettifySense(senseBuf)); - } - else - { - Toc = cmdBuf; - DecodedToc = TOC.Decode(cmdBuf); - - // As we have a TOC we know it is a CD - if(MediaType == MediaType.Unknown) - MediaType = MediaType.CD; - } - - // ATIP exists on blank CDs - sense = dev.ReadAtip(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: ATIP\n{0}", - Sense.PrettifySense(senseBuf)); - } - else - { - Atip = cmdBuf; - DecodedAtip = ATIP.Decode(cmdBuf); - - if(DecodedAtip != null) - - // Only CD-R and CD-RW have ATIP - MediaType = DecodedAtip.DiscType ? MediaType.CDRW : MediaType.CDR; - else - Atip = null; - } - - // We got a TOC, get information about a recorded/mastered CD - if(!tocSense) - { - sense = dev.ReadSessionInfo(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: Session info\n{0}", - Sense.PrettifySense(senseBuf)); - } - else if(cmdBuf.Length > 4) - { - Session = cmdBuf; - DecodedSession = Decoders.CD.Session.Decode(cmdBuf); - - if(DecodedSession.HasValue) - { - sessions = DecodedSession.Value.LastCompleteSession; - firstTrackLastSession = DecodedSession.Value.TrackDescriptors[0].TrackNumber; - } - } - - sense = dev.ReadRawToc(out cmdBuf, out senseBuf, 1, dev.Timeout, out _); - - if(sense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: Raw TOC\n{0}", - Sense.PrettifySense(senseBuf)); - } - else if(cmdBuf.Length > 4) - { - RawToc = cmdBuf; - - FullToc = FullTOC.Decode(cmdBuf); - } - - sense = dev.ReadPma(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: PMA\n{0}", - Sense.PrettifySense(senseBuf)); - else if(cmdBuf.Length > 4) - Pma = cmdBuf; - - sense = dev.ReadCdText(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: CD-TEXT\n{0}", - Sense.PrettifySense(senseBuf)); - } - else if(cmdBuf.Length > 4) - { - CdTextLeadIn = cmdBuf; - DecodedCdTextLeadIn = CDTextOnLeadIn.Decode(cmdBuf); - } - - sense = dev.ReadMcn(out string mcn, out _, out _, dev.Timeout, out _); - - if(!sense && - mcn != null && - mcn != "0000000000000") - Mcn = mcn; - - Isrcs = new Dictionary(); - - for(byte i = DecodedToc.Value.FirstTrack; i <= DecodedToc.Value.LastTrack; i++) - { - sense = dev.ReadIsrc(i, out string isrc, out _, out _, dev.Timeout, out _); - - if(!sense && - isrc != null && - isrc != "000000000000") - Isrcs.Add(i, isrc); - } - - if(Isrcs.Count == 0) - Isrcs = null; - } - - break; - #endregion CDs - } - - #region Nintendo - if(MediaType == MediaType.Unknown && - Blocks > 0) - { - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out _); - - if(sense) - { - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: PFI\n{0}", - Sense.PrettifySense(senseBuf)); - } - else - { - DvdPfi = cmdBuf; - PFI.PhysicalFormatInformation? nintendoPfi = PFI.Decode(cmdBuf, MediaType); - - if(nintendoPfi != null) - { - AaruConsole.WriteLine("PFI:\n{0}", PFI.Prettify(cmdBuf, MediaType)); - - if(nintendoPfi.Value.DiskCategory == DiskCategory.Nintendo && - nintendoPfi.Value.PartVersion == 15) - switch(nintendoPfi.Value.DiscSize) - { - case DVDSize.Eighty: - MediaType = MediaType.GOD; - - break; - case DVDSize.OneTwenty: - MediaType = MediaType.WOD; - - break; - } - } - } - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.DiscManufacturingInformation, 0, dev.Timeout, - out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DMI\n{0}", - Sense.PrettifySense(senseBuf)); - else - DvdDmi = cmdBuf; - } - #endregion Nintendo + sense = dev.ReadAttribute(out seqBuf, out senseBuf, ScsiAttributeAction.List, 0, dev.Timeout, out _); + if (sense) + AaruConsole.ErrorWriteLine("SCSI READ ATTRIBUTE:\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + { + DataFile.WriteTo("Media-Info command", outputPrefix, "_scsi_readattribute.bin", "SCSI READ ATTRIBUTE", seqBuf); } + */ + break; + case PeripheralDeviceTypes.BridgingExpander + when dev.Model.StartsWith("MDM", StringComparison.Ordinal) || + dev.Model.StartsWith("MDH", StringComparison.Ordinal): + sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); - sense = dev.ReadMediaSerialNumber(out cmdBuf, out senseBuf, dev.Timeout, out _); + if(!sense) + { + ReadCapacity = cmdBuf; + + Blocks = (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & + 0xFFFFFFFF; + + BlockSize = (uint)((cmdBuf[5] << 24) + (cmdBuf[5] << 16) + (cmdBuf[6] << 8) + cmdBuf[7]); + } + + break; + } + + if(dev.ScsiType == PeripheralDeviceTypes.MultiMediaDevice) + { + sense = dev.GetConfiguration(out cmdBuf, out senseBuf, 0, MmcGetConfigurationRt.Current, dev.Timeout, + out _); if(sense) { - AaruConsole.DebugWriteLine("Media-Info command", "READ MEDIA SERIAL NUMBER\n{0}", + AaruConsole.DebugWriteLine("Media-Info command", "READ GET CONFIGURATION:\n{0}", Sense.PrettifySense(senseBuf)); + + if(dev.IsUsb && + (scsiMediumType == 0x40 || scsiMediumType == 0x41 || scsiMediumType == 0x42)) + MediaType = MediaType.FlashDrive; } else { - if(cmdBuf.Length >= 4) - MediaSerialNumber = cmdBuf; + MmcConfiguration = cmdBuf; + Features.SeparatedFeatures ftr = Features.Separate(cmdBuf); + + AaruConsole.DebugWriteLine("Media-Info command", "GET CONFIGURATION current profile is {0:X4}h", + ftr.CurrentProfile); + + switch(ftr.CurrentProfile) + { + case 0x0001: + MediaType = MediaType.GENERIC_HDD; + + break; + case 0x0002: + switch(scsiMediumType) + { + case 0x01: + MediaType = MediaType.PD650; + + break; + case 0x41: + switch(Blocks) + { + case 58620544: + MediaType = MediaType.REV120; + + break; + case 17090880: + MediaType = MediaType.REV35; + + break; + case 34185728: + MediaType = MediaType.REV70; + + break; + } + + break; + default: + MediaType = MediaType.Unknown; + + break; + } + + break; + case 0x0005: + MediaType = MediaType.CDMO; + + break; + case 0x0008: + MediaType = MediaType.CD; + + break; + case 0x0009: + MediaType = MediaType.CDR; + + break; + case 0x000A: + MediaType = MediaType.CDRW; + + break; + case 0x0010: + MediaType = MediaType.DVDROM; + + break; + case 0x0011: + MediaType = MediaType.DVDR; + + break; + case 0x0012: + MediaType = MediaType.DVDRAM; + + break; + case 0x0013: + case 0x0014: + MediaType = MediaType.DVDRW; + + break; + case 0x0015: + case 0x0016: + MediaType = MediaType.DVDRDL; + + break; + case 0x0017: + MediaType = MediaType.DVDRWDL; + + break; + case 0x0018: + MediaType = MediaType.DVDDownload; + + break; + case 0x001A: + MediaType = MediaType.DVDPRW; + + break; + case 0x001B: + MediaType = MediaType.DVDPR; + + break; + case 0x0020: + MediaType = MediaType.DDCD; + + break; + case 0x0021: + MediaType = MediaType.DDCDR; + + break; + case 0x0022: + MediaType = MediaType.DDCDRW; + + break; + case 0x002A: + MediaType = MediaType.DVDPRWDL; + + break; + case 0x002B: + MediaType = MediaType.DVDPRDL; + + break; + case 0x0040: + MediaType = MediaType.BDROM; + + break; + case 0x0041: + case 0x0042: + MediaType = MediaType.BDR; + + break; + case 0x0043: + MediaType = MediaType.BDRE; + + break; + case 0x0050: + MediaType = MediaType.HDDVDROM; + + break; + case 0x0051: + MediaType = MediaType.HDDVDR; + + break; + case 0x0052: + MediaType = MediaType.HDDVDRAM; + + break; + case 0x0053: + MediaType = MediaType.HDDVDRW; + + break; + case 0x0058: + MediaType = MediaType.HDDVDRDL; + + break; + case 0x005A: + MediaType = MediaType.HDDVDRWDL; + + break; + } + } + + if(MediaType == MediaType.PD650 && + Blocks == 1281856) + MediaType = MediaType.PD650_WORM; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.RecognizedFormatLayers, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Recognized Format Layers\n{0}", + Sense.PrettifySense(senseBuf)); + else + RecognizedFormatLayers = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.WriteProtectionStatus, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Write Protection Status\n{0}", + Sense.PrettifySense(senseBuf)); + else + WriteProtectionStatus = cmdBuf; + + // More like a drive information + /* + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.CapabilityList, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Capability List\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_capabilitylist.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + */ + + #region All DVD and HD DVD types + if(MediaType == MediaType.DVDDownload || + MediaType == MediaType.DVDPR || + MediaType == MediaType.DVDPRDL || + MediaType == MediaType.DVDPRW || + MediaType == MediaType.DVDPRWDL || + MediaType == MediaType.DVDR || + MediaType == MediaType.DVDRAM || + MediaType == MediaType.DVDRDL || + MediaType == MediaType.DVDROM || + MediaType == MediaType.DVDRW || + MediaType == MediaType.DVDRWDL || + MediaType == MediaType.HDDVDR || + MediaType == MediaType.HDDVDRAM || + MediaType == MediaType.HDDVDRDL || + MediaType == MediaType.HDDVDROM || + MediaType == MediaType.HDDVDRW || + MediaType == MediaType.HDDVDRWDL) + { + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out _); + + if(sense) + { + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: PFI\n{0}", + Sense.PrettifySense(senseBuf)); + } + else + { + DvdPfi = cmdBuf; + DecodedPfi = PFI.Decode(cmdBuf, MediaType); + + if(DecodedPfi.HasValue) + if(MediaType == MediaType.DVDROM) + switch(DecodedPfi.Value.DiskCategory) + { + case DiskCategory.DVDPR: + MediaType = MediaType.DVDPR; + + break; + case DiskCategory.DVDPRDL: + MediaType = MediaType.DVDPRDL; + + break; + case DiskCategory.DVDPRW: + MediaType = MediaType.DVDPRW; + + break; + case DiskCategory.DVDPRWDL: + MediaType = MediaType.DVDPRWDL; + + break; + case DiskCategory.DVDR: + MediaType = DecodedPfi.Value.PartVersion >= 6 ? MediaType.DVDRDL + : MediaType.DVDR; + + break; + case DiskCategory.DVDRAM: + MediaType = MediaType.DVDRAM; + + break; + default: + MediaType = MediaType.DVDROM; + + break; + case DiskCategory.DVDRW: + MediaType = DecodedPfi.Value.PartVersion >= 15 ? MediaType.DVDRWDL + : MediaType.DVDRW; + + break; + case DiskCategory.HDDVDR: + MediaType = MediaType.HDDVDR; + + break; + case DiskCategory.HDDVDRAM: + MediaType = MediaType.HDDVDRAM; + + break; + case DiskCategory.HDDVDROM: + MediaType = MediaType.HDDVDROM; + + break; + case DiskCategory.HDDVDRW: + MediaType = MediaType.HDDVDRW; + + break; + case DiskCategory.Nintendo: + MediaType = DecodedPfi.Value.DiscSize == DVDSize.Eighty ? MediaType.GOD + : MediaType.WOD; + + break; + case DiskCategory.UMD: + MediaType = MediaType.UMD; + + break; + } + } + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DiscManufacturingInformation, 0, dev.Timeout, + out _); + + if(sense) + { + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DMI\n{0}", + Sense.PrettifySense(senseBuf)); + } + else + { + DvdDmi = cmdBuf; + + if(DMI.IsXbox(cmdBuf)) + { + MediaType = MediaType.XGD; + } + else if(DMI.IsXbox360(cmdBuf)) + { + MediaType = MediaType.XGD2; + + // All XGD3 all have the same number of blocks + if(Blocks == 25063 || // Locked (or non compatible drive) + Blocks == 4229664 || // Xtreme unlock + Blocks == 4246304) // Wxripper unlock + MediaType = MediaType.XGD3; + } + } + } + #endregion All DVD and HD DVD types + + #region DVD-ROM + if(MediaType == MediaType.DVDDownload || + MediaType == MediaType.DVDROM) + { + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.CopyrightInformation, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: CMI\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdCmi = cmdBuf; + } + #endregion DVD-ROM + + switch(MediaType) + { + #region DVD-ROM and HD DVD-ROM + case MediaType.DVDDownload: + case MediaType.DVDROM: + case MediaType.HDDVDROM: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.BurstCuttingArea, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: BCA\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdBca = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdAacs, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DVD AACS\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdAacs = cmdBuf; + + break; + #endregion DVD-ROM and HD DVD-ROM + + #region DVD-RAM and HD DVD-RAM + case MediaType.DVDRAM: + case MediaType.HDDVDRAM: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdramDds, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DDS\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdRamDds = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdramMediumStatus, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Medium Status\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdRamCartridgeStatus = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdramSpareAreaInformation, 0, dev.Timeout, + out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: SAI\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdRamSpareArea = cmdBuf; + + break; + #endregion DVD-RAM and HD DVD-RAM + + #region DVD-R and HD DVD-R + case MediaType.DVDR: + case MediaType.HDDVDR: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.LastBorderOutRmd, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Last-Out Border RMD\n{0}", + Sense.PrettifySense(senseBuf)); + else + LastBorderOutRmd = cmdBuf; + + break; + #endregion DVD-R and HD DVD-R + } + + var dvdDecrypt = new DVDDecryption(dev); + sense = dvdDecrypt.ReadBusKey(out cmdBuf, out senseBuf, CopyrightType.CSS, dev.Timeout, out _); + + if(!sense) + { + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DiscKey, dvdDecrypt.Agid, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Disc Key\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdDiscKey = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.SectorCopyrightInformation, dvdDecrypt.Agid, + dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Sector CMI\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdSectorCmi = cmdBuf; + } + + #region Require drive authentication, won't work + /* + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.MediaIdentifier, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Media ID\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_mediaid.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.MediaKeyBlock, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: MKB\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_dvd_mkb.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSVolId, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS Volume ID\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacsvolid.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSMediaSerial, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS Media Serial Number\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacssn.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSMediaId, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS Media ID\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacsmediaid.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSMKB, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS MKB\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacsmkb.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSLBAExtents, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS LBA Extents\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacslbaextents.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSMKBCPRM, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS CPRM MKB\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacscprmmkb.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.DVD, 0, 0, MmcDiscStructureFormat.AACSDataKeys, 0, dev.Timeout, out _); + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: AACS Data Keys\n{0}", Decoders.SCSI.Sense.PrettifySense(senseBuf)); + else + DataFile.WriteTo("Media-Info command", outputPrefix, "_readdiscstructure_aacsdatakeys.bin", "SCSI READ DISC STRUCTURE", cmdBuf); + */ + #endregion Require drive authentication, won't work + + #region DVD-R and DVD-RW + if(MediaType == MediaType.DVDR || + MediaType == MediaType.DVDRW || + MediaType == MediaType.DVDRDL || + MediaType == MediaType.DVDRWDL) + { + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PreRecordedInfo, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Pre-Recorded Info\n{0}", + Sense.PrettifySense(senseBuf)); + else + { + DvdPreRecordedInfo = cmdBuf; + + DecodedDvdPrePitInformation = PRI.Decode(cmdBuf); + } + } + #endregion DVD-R and DVD-RW + + switch(MediaType) + { + #region DVD-R, DVD-RW and HD DVD-R + case MediaType.DVDR: + case MediaType.DVDRW: + case MediaType.DVDRDL: + case MediaType.DVDRWDL: + case MediaType.HDDVDR: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdrMediaIdentifier, 0, dev.Timeout, + out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DVD-R Media ID\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdrMediaIdentifier = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdrPhysicalInformation, 0, dev.Timeout, + out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DVD-R PFI\n{0}", + Sense.PrettifySense(senseBuf)); + else + { + DvdrPhysicalInformation = cmdBuf; + + DecodedDvdrPfi = PFI.Decode(cmdBuf, MediaType); + } + + break; + #endregion DVD-R, DVD-RW and HD DVD-R + + #region All DVD+ + case MediaType.DVDPR: + case MediaType.DVDPRDL: + case MediaType.DVDPRW: + case MediaType.DVDPRWDL: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.Adip, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: ADIP\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdPlusAdip = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.Dcb, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DCB\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdPlusDcb = cmdBuf; + + break; + #endregion All DVD+ + + #region HD DVD-ROM + case MediaType.HDDVDROM: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.HddvdCopyrightInformation, 0, dev.Timeout, + out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: HD DVD CMI\n{0}", + Sense.PrettifySense(senseBuf)); + else + HddvdCopyrightInformation = cmdBuf; + + break; + #endregion HD DVD-ROM + } + + #region HD DVD-R + if(MediaType == MediaType.HDDVDR) + { + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.HddvdrMediumStatus, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: HD DVD-R Medium Status\n{0}", + Sense.PrettifySense(senseBuf)); + else + HddvdrMediumStatus = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.HddvdrLastRmd, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Last RMD\n{0}", + Sense.PrettifySense(senseBuf)); + else + HddvdrLastRmd = cmdBuf; + } + #endregion HD DVD-R + + #region DVD-R DL, DVD-RW DL, DVD+R DL, DVD+RW DL + if(MediaType == MediaType.DVDPRDL || + MediaType == MediaType.DVDRDL || + MediaType == MediaType.DVDRWDL || + MediaType == MediaType.DVDPRWDL) + { + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DvdrLayerCapacity, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Layer Capacity\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdrLayerCapacity = cmdBuf; + } + #endregion DVD-R DL, DVD-RW DL, DVD+R DL, DVD+RW DL + + switch(MediaType) + { + #region DVD-R DL + case MediaType.DVDRDL: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.MiddleZoneStart, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Middle Zone Start\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdrDlMiddleZoneStart = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.JumpIntervalSize, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Jump Interval Size\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdrDlJumpIntervalSize = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.ManualLayerJumpStartLba, 0, dev.Timeout, + out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Manual Layer Jump Start LBA\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdrDlManualLayerJumpStartLba = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.RemapAnchorPoint, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Remap Anchor Point\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdrDlRemapAnchorPoint = cmdBuf; + + break; + #endregion DVD-R DL + + #region All Blu-ray + case MediaType.BDR: + case MediaType.BDRE: + case MediaType.BDROM: + case MediaType.UHDBD: + case MediaType.BDRXL: + case MediaType.BDREXL: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.DiscInformation, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DI\n{0}", + Sense.PrettifySense(senseBuf)); + else + BlurayDiscInformation = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.Pac, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: PAC\n{0}", + Sense.PrettifySense(senseBuf)); + else + BlurayPac = cmdBuf; + + break; + #endregion All Blu-ray } switch(MediaType) { - #region Xbox - case MediaType.XGD: - case MediaType.XGD2: - case MediaType.XGD3: - // We need to get INQUIRY to know if it is a Kreon drive - sense = dev.ScsiInquiry(out byte[] inqBuffer, out senseBuf); + #region BD-ROM only + case MediaType.BDROM: + case MediaType.UHDBD: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdBurstCuttingArea, 0, dev.Timeout, out _); - if(!sense) + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: BCA\n{0}", + Sense.PrettifySense(senseBuf)); + else + BlurayBurstCuttingArea = cmdBuf; + + break; + #endregion BD-ROM only + + #region Writable Blu-ray only + case MediaType.BDR: + case MediaType.BDRE: + case MediaType.BDRXL: + case MediaType.BDREXL: + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdDds, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DDS\n{0}", + Sense.PrettifySense(senseBuf)); + else + BlurayDds = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.CartridgeStatus, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Cartridge Status\n{0}", + Sense.PrettifySense(senseBuf)); + else + BlurayCartridgeStatus = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.BdSpareAreaInformation, 0, dev.Timeout, + out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", + "READ DISC STRUCTURE: Spare Area Information\n{0}", + Sense.PrettifySense(senseBuf)); + else + BluraySpareAreaInformation = cmdBuf; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Bd, 0, 0, + MmcDiscStructureFormat.RawDfl, 0, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: Raw DFL\n{0}", + Sense.PrettifySense(senseBuf)); + else + BlurayRawDfl = cmdBuf; + + sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, + MmcDiscInformationDataTypes.TrackResources, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC INFORMATION 001b\n{0}", + Sense.PrettifySense(senseBuf)); + else + BlurayTrackResources = cmdBuf; + + sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, + MmcDiscInformationDataTypes.PowResources, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC INFORMATION 010b\n{0}", + Sense.PrettifySense(senseBuf)); + else + BlurayPowResources = cmdBuf; + + break; + #endregion Writable Blu-ray only + + #region CDs + case MediaType.CD: + case MediaType.CDR: + case MediaType.CDROM: + case MediaType.CDRW: + case MediaType.Unknown: + // We discarded all discs that falsify a TOC before requesting a real TOC + // No TOC, no CD (or an empty one) + bool tocSense = dev.ReadTocPmaAtip(out cmdBuf, out senseBuf, false, 0, 0, dev.Timeout, out _); + + if(tocSense) { - Inquiry? inq = Inquiry.Decode(inqBuffer); + AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: TOC\n{0}", + Sense.PrettifySense(senseBuf)); + } + else + { + Toc = cmdBuf; + DecodedToc = TOC.Decode(cmdBuf); - if(inq?.KreonPresent == true) + // As we have a TOC we know it is a CD + if(MediaType == MediaType.Unknown) + MediaType = MediaType.CD; + } + + // ATIP exists on blank CDs + sense = dev.ReadAtip(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: ATIP\n{0}", + Sense.PrettifySense(senseBuf)); + } + else + { + Atip = cmdBuf; + DecodedAtip = ATIP.Decode(cmdBuf); + + if(DecodedAtip != null) + + // Only CD-R and CD-RW have ATIP + MediaType = DecodedAtip.DiscType ? MediaType.CDRW : MediaType.CDR; + else + Atip = null; + } + + // We got a TOC, get information about a recorded/mastered CD + if(!tocSense) + { + sense = dev.ReadSessionInfo(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) { - sense = dev.KreonExtractSs(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - AaruConsole.DebugWriteLine("Media-Info command", "KREON EXTRACT SS:\n{0}", - Sense.PrettifySense(senseBuf)); - else - XboxSecuritySector = cmdBuf; - - DecodedXboxSecuritySector = SS.Decode(cmdBuf); - - // Get video partition size - AaruConsole.DebugWriteLine("Dump-media command", "Getting video partition size"); - sense = dev.KreonLock(out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.ErrorWriteLine("Cannot lock drive, not continuing."); - - break; - } - - sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.ErrorWriteLine("Cannot get disc capacity."); - - break; - } - - ulong totalSize = - (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & - 0xFFFFFFFF; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); - - if(sense) - { - AaruConsole.ErrorWriteLine("Cannot get PFI."); - - break; - } - - AaruConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", - totalSize); - - ulong l0Video = PFI.Decode(cmdBuf, MediaType).Value.Layer0EndPSN - - PFI.Decode(cmdBuf, MediaType).Value.DataAreaStartPSN + 1; - - ulong l1Video = totalSize - l0Video + 1; - - // Get game partition size - AaruConsole.DebugWriteLine("Dump-media command", "Getting game partition size"); - sense = dev.KreonUnlockXtreme(out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); - - break; - } - - sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.ErrorWriteLine("Cannot get disc capacity."); - - return; - } - - ulong gameSize = - ((ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & - 0xFFFFFFFF) + 1; - - AaruConsole.DebugWriteLine("Dump-media command", "Game partition total size: {0} sectors", - gameSize); - - // Get middle zone size - AaruConsole.DebugWriteLine("Dump-media command", "Getting middle zone size"); - sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); - - break; - } - - sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); - - if(sense) - { - AaruConsole.ErrorWriteLine("Cannot get disc capacity."); - - break; - } - - totalSize = (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & - 0xFFFFFFFF; - - sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, - MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); - - if(sense) - { - AaruConsole.ErrorWriteLine("Cannot get PFI."); - - break; - } - - AaruConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", - totalSize); - - ulong middleZone = - totalSize - (PFI.Decode(cmdBuf, MediaType).Value.Layer0EndPSN - - PFI.Decode(cmdBuf, MediaType).Value.DataAreaStartPSN + 1) - gameSize + 1; - - totalSize = l0Video + l1Video + (middleZone * 2) + gameSize; - ulong layerBreak = l0Video + middleZone + (gameSize / 2); - - XgdInfo = new XgdInfo - { - L0Video = l0Video, - L1Video = l1Video, - MiddleZone = middleZone, - GameSize = gameSize, - TotalSize = totalSize, - LayerBreak = layerBreak - }; + AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: Session info\n{0}", + Sense.PrettifySense(senseBuf)); } + else if(cmdBuf.Length > 4) + { + Session = cmdBuf; + DecodedSession = Decoders.CD.Session.Decode(cmdBuf); + + if(DecodedSession.HasValue) + { + sessions = DecodedSession.Value.LastCompleteSession; + firstTrackLastSession = DecodedSession.Value.TrackDescriptors[0].TrackNumber; + } + } + + sense = dev.ReadRawToc(out cmdBuf, out senseBuf, 1, dev.Timeout, out _); + + if(sense) + { + AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: Raw TOC\n{0}", + Sense.PrettifySense(senseBuf)); + } + else if(cmdBuf.Length > 4) + { + RawToc = cmdBuf; + + FullToc = FullTOC.Decode(cmdBuf); + } + + sense = dev.ReadPma(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: PMA\n{0}", + Sense.PrettifySense(senseBuf)); + else if(cmdBuf.Length > 4) + Pma = cmdBuf; + + sense = dev.ReadCdText(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.DebugWriteLine("Media-Info command", "READ TOC/PMA/ATIP: CD-TEXT\n{0}", + Sense.PrettifySense(senseBuf)); + } + else if(cmdBuf.Length > 4) + { + CdTextLeadIn = cmdBuf; + DecodedCdTextLeadIn = CDTextOnLeadIn.Decode(cmdBuf); + } + + sense = dev.ReadMcn(out string mcn, out _, out _, dev.Timeout, out _); + + if(!sense && + mcn != null && + mcn != "0000000000000") + Mcn = mcn; + + Isrcs = new Dictionary(); + + for(byte i = DecodedToc.Value.FirstTrack; i <= DecodedToc.Value.LastTrack; i++) + { + sense = dev.ReadIsrc(i, out string isrc, out _, out _, dev.Timeout, out _); + + if(!sense && + isrc != null && + isrc != "000000000000") + Isrcs.Add(i, isrc); + } + + if(Isrcs.Count == 0) + Isrcs = null; } break; - #endregion Xbox - - case MediaType.Unknown: - MediaType = MediaTypeFromDevice.GetFromScsi((byte)dev.ScsiType, dev.Manufacturer, dev.Model, - scsiMediumType, scsiDensityCode, Blocks, BlockSize, - dev.IsUsb, true); - - break; + #endregion CDs } + #region Nintendo if(MediaType == MediaType.Unknown && - dev.IsUsb && - containsFloppyPage) - MediaType = MediaType.FlashDrive; - - if(MediaType == MediaType.Unknown && - !dev.IsRemovable) - MediaType = MediaType.GENERIC_HDD; - - if(DeviceInfo.ScsiType != PeripheralDeviceTypes.MultiMediaDevice || - (dev.IsUsb && (scsiMediumType == 0x40 || scsiMediumType == 0x41 || scsiMediumType == 0x42))) - return; - - sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, MmcDiscInformationDataTypes.DiscInformation, - dev.Timeout, out _); - - if(sense) + Blocks > 0) { - AaruConsole.DebugWriteLine("Media-Info command", "READ DISC INFORMATION 000b\n{0}", - Sense.PrettifySense(senseBuf)); + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, dev.Timeout, out _); + + if(sense) + { + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: PFI\n{0}", + Sense.PrettifySense(senseBuf)); + } + else + { + DvdPfi = cmdBuf; + PFI.PhysicalFormatInformation? nintendoPfi = PFI.Decode(cmdBuf, MediaType); + + if(nintendoPfi != null) + { + AaruConsole.WriteLine("PFI:\n{0}", PFI.Prettify(cmdBuf, MediaType)); + + if(nintendoPfi.Value.DiskCategory == DiskCategory.Nintendo && + nintendoPfi.Value.PartVersion == 15) + switch(nintendoPfi.Value.DiscSize) + { + case DVDSize.Eighty: + MediaType = MediaType.GOD; + + break; + case DVDSize.OneTwenty: + MediaType = MediaType.WOD; + + break; + } + } + } + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.DiscManufacturingInformation, 0, dev.Timeout, + out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC STRUCTURE: DMI\n{0}", + Sense.PrettifySense(senseBuf)); + else + DvdDmi = cmdBuf; } - else - { - DiscInformation = cmdBuf; - DecodedDiscInformation = Decoders.SCSI.MMC.DiscInformation.Decode000b(cmdBuf); - - if(DecodedDiscInformation.HasValue) - if(MediaType == MediaType.CD) - switch(DecodedDiscInformation.Value.DiscType) - { - case 0x10: - MediaType = MediaType.CDI; - - break; - case 0x20: - MediaType = MediaType.CDROMXA; - - break; - } - } - - MediaType tmpType = MediaType; - MMC.DetectDiscType(ref tmpType, sessions, FullToc, dev, out _, out _, firstTrackLastSession, Blocks); - - MediaType = tmpType; + #endregion Nintendo } - /// Decoded DVD Pre-Recorded Information - public PRI.PreRecordedInformation? DecodedDvdPrePitInformation { get; } - /// Decoded recordable DVD Physical Format Information - public PFI.PhysicalFormatInformation? DecodedDvdrPfi { get; } - /// Raw media serial number - public byte[] MediaSerialNumber { get; } - /// Raw Xbox security sectors - public byte[] XboxSecuritySector { get; } - /// Decoded Xbox security sectors - public SS.SecuritySector? DecodedXboxSecuritySector { get; } - /// Information about an XGD, XGD2 or XGD3 media - public XgdInfo XgdInfo { get; } - /// MMC drive raw GET CONFIGURATION - public byte[] MmcConfiguration { get; } - /// Raw recognized format layers - public byte[] RecognizedFormatLayers { get; } - /// Raw write protection status - public byte[] WriteProtectionStatus { get; } - /// Raw DVD Physical Format Information - public byte[] DvdPfi { get; } - /// Decoded DVD Physical Format Information - public PFI.PhysicalFormatInformation? DecodedPfi { get; } - /// Raw DVD Disc Manufacturing Information - public byte[] DvdDmi { get; } - /// Raw DVD Copyright Management Information - public byte[] DvdCmi { get; } - /// Raw DVD Burst Cutting Area - public byte[] DvdBca { get; } - /// Raw DVD AACS information - public byte[] DvdAacs { get; } - /// Raw DVD-RAM Disc Definition Structure - public byte[] DvdRamDds { get; } - /// Raw DVD-RAM Cartridge Status - public byte[] DvdRamCartridgeStatus { get; } - /// Raw DVD-RAM Spare Area Information - public byte[] DvdRamSpareArea { get; } - /// Raw DVD-R(W) Last Border-Out RMD - public byte[] LastBorderOutRmd { get; } - /// Raw DVD-R(W) Pre-Recorded Information - public byte[] DvdPreRecordedInfo { get; } - /// Raw DVD-R Media ID - public byte[] DvdrMediaIdentifier { get; } - /// Raw recordable DVD Physical Format Information - public byte[] DvdrPhysicalInformation { get; } - /// Raw DVD+R(W) ADIP - public byte[] DvdPlusAdip { get; } - /// Raw DVD+R(W) Disc Control Blocks - public byte[] DvdPlusDcb { get; } - /// Raw HD DVD Copyright Management Information - public byte[] HddvdCopyrightInformation { get; } - /// Raw HD DVD-R Medium Status - public byte[] HddvdrMediumStatus { get; } - /// Raw HD DVD-R(W) Last Border-Out RMD - public byte[] HddvdrLastRmd { get; } - /// Raw DVD-R(W) Layer Capacity - public byte[] DvdrLayerCapacity { get; } - /// Raw DVD-R DL Middle Zone start - public byte[] DvdrDlMiddleZoneStart { get; } - /// Raw DVD-R DL Jump Interval size - public byte[] DvdrDlJumpIntervalSize { get; } - /// Raw DVD-R DL Manual Layer Jump Start LBA - public byte[] DvdrDlManualLayerJumpStartLba { get; } - /// Raw DVD-R DL Remap Anchor Point - public byte[] DvdrDlRemapAnchorPoint { get; } - /// Raw Blu-ray Disc Information - public byte[] BlurayDiscInformation { get; } - /// Raw Blu-ray PAC - public byte[] BlurayPac { get; } - /// Raw Blu-ray Burst Cutting Area - public byte[] BlurayBurstCuttingArea { get; } - /// Raw Blu-ray Disc Definition Structure - public byte[] BlurayDds { get; } - /// Raw Blu-ray Cartridge Status - public byte[] BlurayCartridgeStatus { get; } - /// Raw Blu-ray Spare Area Information - public byte[] BluraySpareAreaInformation { get; } - /// Raw Blu-ray DFL - public byte[] BlurayRawDfl { get; } - /// Raw Blu-ray Pseudo OverWrite Resources - public byte[] BlurayPowResources { get; } - /// Raw READ TOC response - public byte[] Toc { get; } - /// Raw READ ATIP response - public byte[] Atip { get; } - /// Raw READ DISC INFORMATION response - public byte[] DiscInformation { get; } - /// Raw READ SESSION response - public byte[] Session { get; } - /// Raw READ FULL TOC response - public byte[] RawToc { get; } - /// Raw READ PMA response - public byte[] Pma { get; } - /// Raw Lead-In's CD-TEXT response - public byte[] CdTextLeadIn { get; } - /// Decoded READ TOC response - public TOC.CDTOC? DecodedToc { get; } - /// Decoded READ ATIP response - public ATIP.CDATIP DecodedAtip { get; } - /// Decoded READ SESSION response - public Session.CDSessionInfo? DecodedSession { get; } - /// Decoded READ FULL TOC response - public FullTOC.CDFullTOC? FullToc { get; } - /// Decoded Lead-In CD-TEXT response - public CDTextOnLeadIn.CDText? DecodedCdTextLeadIn { get; } - /// Raw Blu-ray track resources - public byte[] BlurayTrackResources { get; } - /// Decoded Blu-ray Disc Information - public DiscInformation.StandardDiscInformation? DecodedDiscInformation { get; } - /// Decoded Media Catalogue Number - public string Mcn { get; } - /// List of decoded track ISRCs - public Dictionary Isrcs { get; } - /// Set if media is inserted in drive - public bool MediaInserted { get; } - /// Detected media type - public MediaType MediaType { get; } - /// Device information - public DeviceInfo DeviceInfo { get; } - /// Raw READ CAPACITY(10) response - public byte[] ReadCapacity { get; } - /// Number of blocks in media - public ulong Blocks { get; } - /// Logical block size - public uint BlockSize { get; } - /// Raw READ CAPACITY(16) response - public byte[] ReadCapacity16 { get; } - /// Raw SSC Density support - public byte[] DensitySupport { get; } - /// Decoded SSC Density support - public DensitySupport.DensitySupportHeader? DensitySupportHeader { get; } - /// Raw SSC media support - public byte[] MediaTypeSupport { get; } - /// Decoded SSC media support - public DensitySupport.MediaTypeSupportHeader? MediaTypeSupportHeader { get; } - /// Raw data from DVD sector Copyright Management Information - public byte[] DvdSectorCmi { get; } - /// Raw DVD Disc Key - public byte[] DvdDiscKey { get; } + sense = dev.ReadMediaSerialNumber(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.DebugWriteLine("Media-Info command", "READ MEDIA SERIAL NUMBER\n{0}", + Sense.PrettifySense(senseBuf)); + } + else + { + if(cmdBuf.Length >= 4) + MediaSerialNumber = cmdBuf; + } + + switch(MediaType) + { + #region Xbox + case MediaType.XGD: + case MediaType.XGD2: + case MediaType.XGD3: + // We need to get INQUIRY to know if it is a Kreon drive + sense = dev.ScsiInquiry(out byte[] inqBuffer, out senseBuf); + + if(!sense) + { + Inquiry? inq = Inquiry.Decode(inqBuffer); + + if(inq?.KreonPresent == true) + { + sense = dev.KreonExtractSs(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + AaruConsole.DebugWriteLine("Media-Info command", "KREON EXTRACT SS:\n{0}", + Sense.PrettifySense(senseBuf)); + else + XboxSecuritySector = cmdBuf; + + DecodedXboxSecuritySector = SS.Decode(cmdBuf); + + // Get video partition size + AaruConsole.DebugWriteLine("Dump-media command", "Getting video partition size"); + sense = dev.KreonLock(out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.ErrorWriteLine("Cannot lock drive, not continuing."); + + break; + } + + sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.ErrorWriteLine("Cannot get disc capacity."); + + break; + } + + ulong totalSize = + (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & + 0xFFFFFFFF; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); + + if(sense) + { + AaruConsole.ErrorWriteLine("Cannot get PFI."); + + break; + } + + AaruConsole.DebugWriteLine("Dump-media command", "Video partition total size: {0} sectors", + totalSize); + + ulong l0Video = PFI.Decode(cmdBuf, MediaType).Value.Layer0EndPSN - + PFI.Decode(cmdBuf, MediaType).Value.DataAreaStartPSN + 1; + + ulong l1Video = totalSize - l0Video + 1; + + // Get game partition size + AaruConsole.DebugWriteLine("Dump-media command", "Getting game partition size"); + sense = dev.KreonUnlockXtreme(out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); + + break; + } + + sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.ErrorWriteLine("Cannot get disc capacity."); + + return; + } + + ulong gameSize = + ((ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & + 0xFFFFFFFF) + 1; + + AaruConsole.DebugWriteLine("Dump-media command", "Game partition total size: {0} sectors", + gameSize); + + // Get middle zone size + AaruConsole.DebugWriteLine("Dump-media command", "Getting middle zone size"); + sense = dev.KreonUnlockWxripper(out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.ErrorWriteLine("Cannot unlock drive, not continuing."); + + break; + } + + sense = dev.ReadCapacity(out cmdBuf, out senseBuf, dev.Timeout, out _); + + if(sense) + { + AaruConsole.ErrorWriteLine("Cannot get disc capacity."); + + break; + } + + totalSize = (ulong)((cmdBuf[0] << 24) + (cmdBuf[1] << 16) + (cmdBuf[2] << 8) + cmdBuf[3]) & + 0xFFFFFFFF; + + sense = dev.ReadDiscStructure(out cmdBuf, out senseBuf, MmcDiscStructureMediaType.Dvd, 0, 0, + MmcDiscStructureFormat.PhysicalInformation, 0, 0, out _); + + if(sense) + { + AaruConsole.ErrorWriteLine("Cannot get PFI."); + + break; + } + + AaruConsole.DebugWriteLine("Dump-media command", "Unlocked total size: {0} sectors", + totalSize); + + ulong middleZone = + totalSize - (PFI.Decode(cmdBuf, MediaType).Value.Layer0EndPSN - + PFI.Decode(cmdBuf, MediaType).Value.DataAreaStartPSN + 1) - gameSize + 1; + + totalSize = l0Video + l1Video + (middleZone * 2) + gameSize; + ulong layerBreak = l0Video + middleZone + (gameSize / 2); + + XgdInfo = new XgdInfo + { + L0Video = l0Video, + L1Video = l1Video, + MiddleZone = middleZone, + GameSize = gameSize, + TotalSize = totalSize, + LayerBreak = layerBreak + }; + } + } + + break; + #endregion Xbox + + case MediaType.Unknown: + MediaType = MediaTypeFromDevice.GetFromScsi((byte)dev.ScsiType, dev.Manufacturer, dev.Model, + scsiMediumType, scsiDensityCode, Blocks, BlockSize, + dev.IsUsb, true); + + break; + } + + if(MediaType == MediaType.Unknown && + dev.IsUsb && + containsFloppyPage) + MediaType = MediaType.FlashDrive; + + if(MediaType == MediaType.Unknown && + !dev.IsRemovable) + MediaType = MediaType.GENERIC_HDD; + + if(DeviceInfo.ScsiType != PeripheralDeviceTypes.MultiMediaDevice || + (dev.IsUsb && (scsiMediumType == 0x40 || scsiMediumType == 0x41 || scsiMediumType == 0x42))) + return; + + sense = dev.ReadDiscInformation(out cmdBuf, out senseBuf, MmcDiscInformationDataTypes.DiscInformation, + dev.Timeout, out _); + + if(sense) + { + AaruConsole.DebugWriteLine("Media-Info command", "READ DISC INFORMATION 000b\n{0}", + Sense.PrettifySense(senseBuf)); + } + else + { + DiscInformation = cmdBuf; + DecodedDiscInformation = Decoders.SCSI.MMC.DiscInformation.Decode000b(cmdBuf); + + if(DecodedDiscInformation.HasValue) + if(MediaType == MediaType.CD) + switch(DecodedDiscInformation.Value.DiscType) + { + case 0x10: + MediaType = MediaType.CDI; + + break; + case 0x20: + MediaType = MediaType.CDROMXA; + + break; + } + } + + MediaType tmpType = MediaType; + MMC.DetectDiscType(ref tmpType, sessions, FullToc, dev, out _, out _, firstTrackLastSession, Blocks); + + MediaType = tmpType; } + + /// Decoded DVD Pre-Recorded Information + public PRI.PreRecordedInformation? DecodedDvdPrePitInformation { get; } + /// Decoded recordable DVD Physical Format Information + public PFI.PhysicalFormatInformation? DecodedDvdrPfi { get; } + /// Raw media serial number + public byte[] MediaSerialNumber { get; } + /// Raw Xbox security sectors + public byte[] XboxSecuritySector { get; } + /// Decoded Xbox security sectors + public SS.SecuritySector? DecodedXboxSecuritySector { get; } + /// Information about an XGD, XGD2 or XGD3 media + public XgdInfo XgdInfo { get; } + /// MMC drive raw GET CONFIGURATION + public byte[] MmcConfiguration { get; } + /// Raw recognized format layers + public byte[] RecognizedFormatLayers { get; } + /// Raw write protection status + public byte[] WriteProtectionStatus { get; } + /// Raw DVD Physical Format Information + public byte[] DvdPfi { get; } + /// Decoded DVD Physical Format Information + public PFI.PhysicalFormatInformation? DecodedPfi { get; } + /// Raw DVD Disc Manufacturing Information + public byte[] DvdDmi { get; } + /// Raw DVD Copyright Management Information + public byte[] DvdCmi { get; } + /// Raw DVD Burst Cutting Area + public byte[] DvdBca { get; } + /// Raw DVD AACS information + public byte[] DvdAacs { get; } + /// Raw DVD-RAM Disc Definition Structure + public byte[] DvdRamDds { get; } + /// Raw DVD-RAM Cartridge Status + public byte[] DvdRamCartridgeStatus { get; } + /// Raw DVD-RAM Spare Area Information + public byte[] DvdRamSpareArea { get; } + /// Raw DVD-R(W) Last Border-Out RMD + public byte[] LastBorderOutRmd { get; } + /// Raw DVD-R(W) Pre-Recorded Information + public byte[] DvdPreRecordedInfo { get; } + /// Raw DVD-R Media ID + public byte[] DvdrMediaIdentifier { get; } + /// Raw recordable DVD Physical Format Information + public byte[] DvdrPhysicalInformation { get; } + /// Raw DVD+R(W) ADIP + public byte[] DvdPlusAdip { get; } + /// Raw DVD+R(W) Disc Control Blocks + public byte[] DvdPlusDcb { get; } + /// Raw HD DVD Copyright Management Information + public byte[] HddvdCopyrightInformation { get; } + /// Raw HD DVD-R Medium Status + public byte[] HddvdrMediumStatus { get; } + /// Raw HD DVD-R(W) Last Border-Out RMD + public byte[] HddvdrLastRmd { get; } + /// Raw DVD-R(W) Layer Capacity + public byte[] DvdrLayerCapacity { get; } + /// Raw DVD-R DL Middle Zone start + public byte[] DvdrDlMiddleZoneStart { get; } + /// Raw DVD-R DL Jump Interval size + public byte[] DvdrDlJumpIntervalSize { get; } + /// Raw DVD-R DL Manual Layer Jump Start LBA + public byte[] DvdrDlManualLayerJumpStartLba { get; } + /// Raw DVD-R DL Remap Anchor Point + public byte[] DvdrDlRemapAnchorPoint { get; } + /// Raw Blu-ray Disc Information + public byte[] BlurayDiscInformation { get; } + /// Raw Blu-ray PAC + public byte[] BlurayPac { get; } + /// Raw Blu-ray Burst Cutting Area + public byte[] BlurayBurstCuttingArea { get; } + /// Raw Blu-ray Disc Definition Structure + public byte[] BlurayDds { get; } + /// Raw Blu-ray Cartridge Status + public byte[] BlurayCartridgeStatus { get; } + /// Raw Blu-ray Spare Area Information + public byte[] BluraySpareAreaInformation { get; } + /// Raw Blu-ray DFL + public byte[] BlurayRawDfl { get; } + /// Raw Blu-ray Pseudo OverWrite Resources + public byte[] BlurayPowResources { get; } + /// Raw READ TOC response + public byte[] Toc { get; } + /// Raw READ ATIP response + public byte[] Atip { get; } + /// Raw READ DISC INFORMATION response + public byte[] DiscInformation { get; } + /// Raw READ SESSION response + public byte[] Session { get; } + /// Raw READ FULL TOC response + public byte[] RawToc { get; } + /// Raw READ PMA response + public byte[] Pma { get; } + /// Raw Lead-In's CD-TEXT response + public byte[] CdTextLeadIn { get; } + /// Decoded READ TOC response + public TOC.CDTOC? DecodedToc { get; } + /// Decoded READ ATIP response + public ATIP.CDATIP DecodedAtip { get; } + /// Decoded READ SESSION response + public Session.CDSessionInfo? DecodedSession { get; } + /// Decoded READ FULL TOC response + public FullTOC.CDFullTOC? FullToc { get; } + /// Decoded Lead-In CD-TEXT response + public CDTextOnLeadIn.CDText? DecodedCdTextLeadIn { get; } + /// Raw Blu-ray track resources + public byte[] BlurayTrackResources { get; } + /// Decoded Blu-ray Disc Information + public DiscInformation.StandardDiscInformation? DecodedDiscInformation { get; } + /// Decoded Media Catalogue Number + public string Mcn { get; } + /// List of decoded track ISRCs + public Dictionary Isrcs { get; } + /// Set if media is inserted in drive + public bool MediaInserted { get; } + /// Detected media type + public MediaType MediaType { get; } + /// Device information + public DeviceInfo DeviceInfo { get; } + /// Raw READ CAPACITY(10) response + public byte[] ReadCapacity { get; } + /// Number of blocks in media + public ulong Blocks { get; } + /// Logical block size + public uint BlockSize { get; } + /// Raw READ CAPACITY(16) response + public byte[] ReadCapacity16 { get; } + /// Raw SSC Density support + public byte[] DensitySupport { get; } + /// Decoded SSC Density support + public DensitySupport.DensitySupportHeader? DensitySupportHeader { get; } + /// Raw SSC media support + public byte[] MediaTypeSupport { get; } + /// Decoded SSC media support + public DensitySupport.MediaTypeSupportHeader? MediaTypeSupportHeader { get; } + /// Raw data from DVD sector Copyright Management Information + public byte[] DvdSectorCmi { get; } + /// Raw DVD Disc Key + public byte[] DvdDiscKey { get; } } \ No newline at end of file diff --git a/Aaru.Core/Media/Info/XgdInfo.cs b/Aaru.Core/Media/Info/XgdInfo.cs index b64d41462..097b5e1fb 100644 --- a/Aaru.Core/Media/Info/XgdInfo.cs +++ b/Aaru.Core/Media/Info/XgdInfo.cs @@ -30,22 +30,21 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Core.Media.Info +namespace Aaru.Core.Media.Info; + +/// Information about an XGD, XGD2 or XGD3 media. +public sealed class XgdInfo { - /// Information about an XGD, XGD2 or XGD3 media. - public sealed class XgdInfo - { - /// Size of the game partition - public ulong GameSize; - /// Size of layer 0 of the video partition - public ulong L0Video; - /// Size of layer 1 of the video partition - public ulong L1Video; - /// Real layer break - public ulong LayerBreak; - /// Size of the middle zone - public ulong MiddleZone; - /// Total size of media - public ulong TotalSize; - } + /// Size of the game partition + public ulong GameSize; + /// Size of layer 0 of the video partition + public ulong L0Video; + /// Size of layer 1 of the video partition + public ulong L1Video; + /// Real layer break + public ulong LayerBreak; + /// Size of the middle zone + public ulong MiddleZone; + /// Total size of media + public ulong TotalSize; } \ No newline at end of file diff --git a/Aaru.Core/Options.cs b/Aaru.Core/Options.cs index 64d925208..6ea7bbf33 100644 --- a/Aaru.Core/Options.cs +++ b/Aaru.Core/Options.cs @@ -34,171 +34,170 @@ using System.Collections.Generic; using System.Globalization; using System.Text; -namespace Aaru.Core +namespace Aaru.Core; + +/// Option parsing +public static class Options { - /// Option parsing - public static class Options + /// Parses a string with options + /// Options string + /// Options name-value dictionary + public static Dictionary Parse(string options) { - /// Parses a string with options - /// Options string - /// Options name-value dictionary - public static Dictionary Parse(string options) - { - Dictionary parsed = new Dictionary(); - bool escaped = false; - bool quoted = false; - bool inValue = false; - string name = null; - string value; - var sb = new StringBuilder(); - - if(options == null) - return parsed; - - for(int index = 0; index < options.Length; index++) - { - char c = options[index]; - - switch(c) - { - case '\\' when !escaped: - escaped = true; - - break; - case '"' when !escaped: - quoted = !quoted; - - break; - case '=' when quoted: - sb.Append(c); - - break; - case '=': - name = sb.ToString().ToLower(CultureInfo.CurrentCulture); - sb = new StringBuilder(); - inValue = true; - - break; - case ',' when quoted: - sb.Append(c); - - break; - case ',' when inValue: - value = sb.ToString(); - sb = new StringBuilder(); - inValue = false; - - if(string.IsNullOrEmpty(name) || - string.IsNullOrEmpty(value)) - continue; - - if(parsed.ContainsKey(name)) - parsed.Remove(name); - - parsed.Add(name, value); - - break; - default: - if(escaped) - switch(c) - { - case 'a': - sb.Append('\a'); - escaped = false; - - break; - case 'b': - sb.Append('\b'); - escaped = false; - - break; - case 'f': - sb.Append('\f'); - escaped = false; - - break; - case 'n': - sb.Append('\n'); - escaped = false; - - break; - case 'r': - sb.Append('\r'); - escaped = false; - - break; - case 't': - sb.Append('\t'); - escaped = false; - - break; - case 'v': - sb.Append('\v'); - escaped = false; - - break; - case '\\': - sb.Append('\\'); - escaped = false; - - break; - case '\'': - sb.Append('\''); - escaped = false; - - break; - case '"': - sb.Append('"'); - escaped = false; - - break; - case '0': - sb.Append('\0'); - escaped = false; - - break; - case 'u': - string unicode = options.Substring(index + 1, 4); - sb.Append((char)int.Parse(unicode, NumberStyles.HexNumber)); - escaped = false; - index += 4; - - break; - case 'U': - string longUnicode = options.Substring(index + 1, 8); - sb.Append((char)int.Parse(longUnicode, NumberStyles.HexNumber)); - escaped = false; - index += 8; - - break; - default: - sb.Append(c); - escaped = false; - - break; - } - else - sb.Append(c); - - break; - } - } - - if(!inValue) - return parsed; - - value = sb.ToString(); - - if(string.IsNullOrEmpty(name) || - string.IsNullOrEmpty(value)) - return parsed; - - if(parsed.ContainsKey(name)) - parsed.Remove(name); - - parsed.Add(name, value); + Dictionary parsed = new Dictionary(); + bool escaped = false; + bool quoted = false; + bool inValue = false; + string name = null; + string value; + var sb = new StringBuilder(); + if(options == null) return parsed; + + for(int index = 0; index < options.Length; index++) + { + char c = options[index]; + + switch(c) + { + case '\\' when !escaped: + escaped = true; + + break; + case '"' when !escaped: + quoted = !quoted; + + break; + case '=' when quoted: + sb.Append(c); + + break; + case '=': + name = sb.ToString().ToLower(CultureInfo.CurrentCulture); + sb = new StringBuilder(); + inValue = true; + + break; + case ',' when quoted: + sb.Append(c); + + break; + case ',' when inValue: + value = sb.ToString(); + sb = new StringBuilder(); + inValue = false; + + if(string.IsNullOrEmpty(name) || + string.IsNullOrEmpty(value)) + continue; + + if(parsed.ContainsKey(name)) + parsed.Remove(name); + + parsed.Add(name, value); + + break; + default: + if(escaped) + switch(c) + { + case 'a': + sb.Append('\a'); + escaped = false; + + break; + case 'b': + sb.Append('\b'); + escaped = false; + + break; + case 'f': + sb.Append('\f'); + escaped = false; + + break; + case 'n': + sb.Append('\n'); + escaped = false; + + break; + case 'r': + sb.Append('\r'); + escaped = false; + + break; + case 't': + sb.Append('\t'); + escaped = false; + + break; + case 'v': + sb.Append('\v'); + escaped = false; + + break; + case '\\': + sb.Append('\\'); + escaped = false; + + break; + case '\'': + sb.Append('\''); + escaped = false; + + break; + case '"': + sb.Append('"'); + escaped = false; + + break; + case '0': + sb.Append('\0'); + escaped = false; + + break; + case 'u': + string unicode = options.Substring(index + 1, 4); + sb.Append((char)int.Parse(unicode, NumberStyles.HexNumber)); + escaped = false; + index += 4; + + break; + case 'U': + string longUnicode = options.Substring(index + 1, 8); + sb.Append((char)int.Parse(longUnicode, NumberStyles.HexNumber)); + escaped = false; + index += 8; + + break; + default: + sb.Append(c); + escaped = false; + + break; + } + else + sb.Append(c); + + break; + } } + + if(!inValue) + return parsed; + + value = sb.ToString(); + + if(string.IsNullOrEmpty(name) || + string.IsNullOrEmpty(value)) + return parsed; + + if(parsed.ContainsKey(name)) + parsed.Remove(name); + + parsed.Add(name, value); + + return parsed; } } \ No newline at end of file diff --git a/Aaru.Core/Partitions.cs b/Aaru.Core/Partitions.cs index 001836012..64c47174f 100644 --- a/Aaru.Core/Partitions.cs +++ b/Aaru.Core/Partitions.cs @@ -37,169 +37,168 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Aaru.Console; -namespace Aaru.Core +namespace Aaru.Core; + +/// Implements methods for handling partitions +public static class Partitions { - /// Implements methods for handling partitions - public static class Partitions + /// Gets a list of all partitions present in the specified image + /// Image + /// List of found partitions + public static List GetAll(IMediaImage image) { - /// Gets a list of all partitions present in the specified image - /// Image - /// List of found partitions - public static List GetAll(IMediaImage image) - { - PluginBase plugins = GetPluginBase.Instance; - List foundPartitions = new List(); - List childPartitions = new List(); - List checkedLocations = new List(); + PluginBase plugins = GetPluginBase.Instance; + List foundPartitions = new List(); + List childPartitions = new List(); + List checkedLocations = new List(); - var tapeImage = image as ITapeImage; - var partitionableImage = image as IPartitionableMediaImage; + var tapeImage = image as ITapeImage; + var partitionableImage = image as IPartitionableMediaImage; - // Create partitions from image files - if(tapeImage?.Files != null) - foreach(TapeFile tapeFile in tapeImage.Files) - { - foreach(IPartition partitionPlugin in plugins.PartPluginsList.Values) - if(partitionPlugin.GetInformation(image, out List partitions, tapeFile.FirstBlock)) - { - foundPartitions.AddRange(partitions); - - AaruConsole.DebugWriteLine("Partitions", "Found {0} @ {1}", partitionPlugin.Name, - tapeFile.FirstBlock); - } - - checkedLocations.Add(tapeFile.FirstBlock); - } - - // Getting all partitions from device (e.g. tracks) - if(partitionableImage?.Partitions != null) - foreach(Partition imagePartition in partitionableImage.Partitions) - { - foreach(IPartition partitionPlugin in plugins.PartPluginsList.Values) - if(partitionPlugin.GetInformation(image, out List partitions, imagePartition.Start)) - { - foundPartitions.AddRange(partitions); - - AaruConsole.DebugWriteLine("Partitions", "Found {0} @ {1}", partitionPlugin.Name, - imagePartition.Start); - } - - checkedLocations.Add(imagePartition.Start); - } - - // Getting all partitions at start of device - if(!checkedLocations.Contains(0)) + // Create partitions from image files + if(tapeImage?.Files != null) + foreach(TapeFile tapeFile in tapeImage.Files) { foreach(IPartition partitionPlugin in plugins.PartPluginsList.Values) - if(partitionPlugin.GetInformation(image, out List partitions, 0)) + if(partitionPlugin.GetInformation(image, out List partitions, tapeFile.FirstBlock)) { foundPartitions.AddRange(partitions); - AaruConsole.DebugWriteLine("Partitions", "Found {0} @ 0", partitionPlugin.Name); + + AaruConsole.DebugWriteLine("Partitions", "Found {0} @ {1}", partitionPlugin.Name, + tapeFile.FirstBlock); } - checkedLocations.Add(0); + checkedLocations.Add(tapeFile.FirstBlock); } - while(foundPartitions.Count > 0) + // Getting all partitions from device (e.g. tracks) + if(partitionableImage?.Partitions != null) + foreach(Partition imagePartition in partitionableImage.Partitions) { - if(checkedLocations.Contains(foundPartitions[0].Start)) - { - childPartitions.Add(foundPartitions[0]); - foundPartitions.RemoveAt(0); - - continue; - } - - List children = new List(); - foreach(IPartition partitionPlugin in plugins.PartPluginsList.Values) - { - AaruConsole.DebugWriteLine("Partitions", "Trying {0} @ {1}", partitionPlugin.Name, - foundPartitions[0].Start); + if(partitionPlugin.GetInformation(image, out List partitions, imagePartition.Start)) + { + foundPartitions.AddRange(partitions); - if(!partitionPlugin.GetInformation(image, out List partitions, foundPartitions[0].Start)) - continue; + AaruConsole.DebugWriteLine("Partitions", "Found {0} @ {1}", partitionPlugin.Name, + imagePartition.Start); + } - AaruConsole.DebugWriteLine("Partitions", "Found {0} @ {1}", partitionPlugin.Name, - foundPartitions[0].Start); - - children.AddRange(partitions); - } - - checkedLocations.Add(foundPartitions[0].Start); - - AaruConsole.DebugWriteLine("Partitions", "Got {0} children", children.Count); - - if(children.Count > 0) - { - foundPartitions.RemoveAt(0); - - foreach(Partition child in children) - if(checkedLocations.Contains(child.Start)) - childPartitions.Add(child); - else - foundPartitions.Add(child); - } - else - { - childPartitions.Add(foundPartitions[0]); - foundPartitions.RemoveAt(0); - } - - AaruConsole.DebugWriteLine("Partitions", "Got {0} parents", foundPartitions.Count); - AaruConsole.DebugWriteLine("Partitions", "Got {0} partitions", childPartitions.Count); + checkedLocations.Add(imagePartition.Start); } - // Be sure that device partitions are not excluded if not mapped by any scheme... - if(!(tapeImage is null)) - { - List startLocations = - childPartitions.Select(detectedPartition => detectedPartition.Start).ToList(); - - if(tapeImage.Files != null) - childPartitions.AddRange(tapeImage.Files.Where(f => !startLocations.Contains(f.FirstBlock)). - Select(tapeFile => new Partition - { - Start = tapeFile.FirstBlock, - Length = tapeFile.LastBlock - tapeFile.FirstBlock + 1, - Sequence = tapeFile.File - })); - } - - if(!(partitionableImage is null)) - { - List startLocations = - childPartitions.Select(detectedPartition => detectedPartition.Start).ToList(); - - if(partitionableImage.Partitions != null) - childPartitions.AddRange(partitionableImage.Partitions.Where(imagePartition => - !startLocations.Contains(imagePartition.Start))); - } - - Partition[] childArray = childPartitions.OrderBy(part => part.Start).ThenBy(part => part.Length). - ThenBy(part => part.Scheme).ToArray(); - - for(long i = 0; i < childArray.LongLength; i++) - childArray[i].Sequence = (ulong)i; - - return childArray.ToList(); - } - - /// Adds all partition schemes from the specified list of partitions to statistics - /// List of partitions - public static void AddSchemesToStats(List partitions) + // Getting all partitions at start of device + if(!checkedLocations.Contains(0)) { - if(partitions == null || - partitions.Count == 0) - return; + foreach(IPartition partitionPlugin in plugins.PartPluginsList.Values) + if(partitionPlugin.GetInformation(image, out List partitions, 0)) + { + foundPartitions.AddRange(partitions); + AaruConsole.DebugWriteLine("Partitions", "Found {0} @ 0", partitionPlugin.Name); + } - List schemes = new List(); - - foreach(Partition part in partitions.Where(part => !schemes.Contains(part.Scheme))) - schemes.Add(part.Scheme); - - foreach(string scheme in schemes) - Statistics.AddPartition(scheme); + checkedLocations.Add(0); } + + while(foundPartitions.Count > 0) + { + if(checkedLocations.Contains(foundPartitions[0].Start)) + { + childPartitions.Add(foundPartitions[0]); + foundPartitions.RemoveAt(0); + + continue; + } + + List children = new List(); + + foreach(IPartition partitionPlugin in plugins.PartPluginsList.Values) + { + AaruConsole.DebugWriteLine("Partitions", "Trying {0} @ {1}", partitionPlugin.Name, + foundPartitions[0].Start); + + if(!partitionPlugin.GetInformation(image, out List partitions, foundPartitions[0].Start)) + continue; + + AaruConsole.DebugWriteLine("Partitions", "Found {0} @ {1}", partitionPlugin.Name, + foundPartitions[0].Start); + + children.AddRange(partitions); + } + + checkedLocations.Add(foundPartitions[0].Start); + + AaruConsole.DebugWriteLine("Partitions", "Got {0} children", children.Count); + + if(children.Count > 0) + { + foundPartitions.RemoveAt(0); + + foreach(Partition child in children) + if(checkedLocations.Contains(child.Start)) + childPartitions.Add(child); + else + foundPartitions.Add(child); + } + else + { + childPartitions.Add(foundPartitions[0]); + foundPartitions.RemoveAt(0); + } + + AaruConsole.DebugWriteLine("Partitions", "Got {0} parents", foundPartitions.Count); + AaruConsole.DebugWriteLine("Partitions", "Got {0} partitions", childPartitions.Count); + } + + // Be sure that device partitions are not excluded if not mapped by any scheme... + if(!(tapeImage is null)) + { + List startLocations = + childPartitions.Select(detectedPartition => detectedPartition.Start).ToList(); + + if(tapeImage.Files != null) + childPartitions.AddRange(tapeImage.Files.Where(f => !startLocations.Contains(f.FirstBlock)). + Select(tapeFile => new Partition + { + Start = tapeFile.FirstBlock, + Length = tapeFile.LastBlock - tapeFile.FirstBlock + 1, + Sequence = tapeFile.File + })); + } + + if(!(partitionableImage is null)) + { + List startLocations = + childPartitions.Select(detectedPartition => detectedPartition.Start).ToList(); + + if(partitionableImage.Partitions != null) + childPartitions.AddRange(partitionableImage.Partitions.Where(imagePartition => + !startLocations.Contains(imagePartition.Start))); + } + + Partition[] childArray = childPartitions.OrderBy(part => part.Start).ThenBy(part => part.Length). + ThenBy(part => part.Scheme).ToArray(); + + for(long i = 0; i < childArray.LongLength; i++) + childArray[i].Sequence = (ulong)i; + + return childArray.ToList(); + } + + /// Adds all partition schemes from the specified list of partitions to statistics + /// List of partitions + public static void AddSchemesToStats(List partitions) + { + if(partitions == null || + partitions.Count == 0) + return; + + List schemes = new List(); + + foreach(Partition part in partitions.Where(part => !schemes.Contains(part.Scheme))) + schemes.Add(part.Scheme); + + foreach(string scheme in schemes) + Statistics.AddPartition(scheme); } } \ No newline at end of file diff --git a/Aaru.Core/PrintScsiModePages.cs b/Aaru.Core/PrintScsiModePages.cs index 4794efa9a..06df018f7 100644 --- a/Aaru.Core/PrintScsiModePages.cs +++ b/Aaru.Core/PrintScsiModePages.cs @@ -35,344 +35,343 @@ using Aaru.Console; using Aaru.Decoders.SCSI; using Aaru.Helpers; -namespace Aaru.Core +namespace Aaru.Core; + +/// Prints all SCSI MODE pages +public static class PrintScsiModePages { /// Prints all SCSI MODE pages - public static class PrintScsiModePages + /// Decoded SCSI MODE SENSE + /// SCSI Peripheral Type + /// SCSI vendor identification + public static void Print(Modes.DecodedMode decMode, PeripheralDeviceTypes devType, byte[] vendorId) { - /// Prints all SCSI MODE pages - /// Decoded SCSI MODE SENSE - /// SCSI Peripheral Type - /// SCSI vendor identification - public static void Print(Modes.DecodedMode decMode, PeripheralDeviceTypes devType, byte[] vendorId) - { - AaruConsole.WriteLine(Modes.PrettifyModeHeader(decMode.Header, devType)); + AaruConsole.WriteLine(Modes.PrettifyModeHeader(decMode.Header, devType)); - if(decMode.Pages == null) - return; + if(decMode.Pages == null) + return; - foreach(Modes.ModePage page in decMode.Pages) + foreach(Modes.ModePage page in decMode.Pages) - //AaruConsole.WriteLine("Page {0:X2}h subpage {1:X2}h is {2} bytes long", page.Page, page.Subpage, page.PageResponse.Length); - switch(page.Page) + //AaruConsole.WriteLine("Page {0:X2}h subpage {1:X2}h is {2} bytes long", page.Page, page.Subpage, page.PageResponse.Length); + switch(page.Page) + { + case 0x00: { - case 0x00: - { - if(devType == PeripheralDeviceTypes.MultiMediaDevice && - page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_00_SFF(page.PageResponse)); - else - { - if(page.Subpage != 0) - AaruConsole.WriteLine("Found unknown vendor mode page {0:X2}h subpage {1:X2}h", - page.Page, page.Subpage); - else - AaruConsole.WriteLine("Found unknown vendor mode page {0:X2}h", page.Page); - } - - break; - } - case 0x01: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(devType == PeripheralDeviceTypes.MultiMediaDevice - ? Modes.PrettifyModePage_01_MMC(page.PageResponse) - : Modes.PrettifyModePage_01(page.PageResponse)); - else - goto default; - - break; - } - case 0x02: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_02(page.PageResponse)); - else - goto default; - - break; - } - case 0x03: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_03(page.PageResponse)); - else - goto default; - - break; - } - case 0x04: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_04(page.PageResponse)); - else - goto default; - - break; - } - case 0x05: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_05(page.PageResponse)); - else - goto default; - - break; - } - case 0x06: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_06(page.PageResponse)); - else - goto default; - - break; - } - case 0x07: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(devType == PeripheralDeviceTypes.MultiMediaDevice - ? Modes.PrettifyModePage_07_MMC(page.PageResponse) - : Modes.PrettifyModePage_07(page.PageResponse)); - else - goto default; - - break; - } - case 0x08: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_08(page.PageResponse)); - else - goto default; - - break; - } - case 0x0A: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_0A(page.PageResponse)); - else if(page.Subpage == 1) - AaruConsole.WriteLine(Modes.PrettifyModePage_0A_S01(page.PageResponse)); - else - goto default; - - break; - } - case 0x0B: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_0B(page.PageResponse)); - else - goto default; - - break; - } - case 0x0D: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_0D(page.PageResponse)); - else - goto default; - - break; - } - case 0x0E: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_0E(page.PageResponse)); - else - goto default; - - break; - } - case 0x0F: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_0F(page.PageResponse)); - else - goto default; - - break; - } - case 0x10: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(devType == PeripheralDeviceTypes.SequentialAccess - ? Modes.PrettifyModePage_10_SSC(page.PageResponse) - : Modes.PrettifyModePage_10(page.PageResponse)); - else - goto default; - - break; - } - case 0x11: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_11(page.PageResponse)); - else - goto default; - - break; - } - case 0x12: - case 0x13: - case 0x14: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_12_13_14(page.PageResponse)); - else - goto default; - - break; - } - case 0x1A: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_1A(page.PageResponse)); - else if(page.Subpage == 1) - AaruConsole.WriteLine(Modes.PrettifyModePage_1A_S01(page.PageResponse)); - else - goto default; - - break; - } - case 0x1B: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_1B(page.PageResponse)); - else - goto default; - - break; - } - case 0x1C: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(devType == PeripheralDeviceTypes.MultiMediaDevice - ? Modes.PrettifyModePage_1C_SFF(page.PageResponse) - : Modes.PrettifyModePage_1C(page.PageResponse)); - else if(page.Subpage == 1) - AaruConsole.WriteLine(Modes.PrettifyModePage_1C_S01(page.PageResponse)); - else - goto default; - - break; - } - case 0x1D: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_1D(page.PageResponse)); - else - goto default; - - break; - } - case 0x21: - { - if(StringHandlers.CToString(vendorId).Trim() == "CERTANCE") - AaruConsole.WriteLine(Modes.PrettifyCertanceModePage_21(page.PageResponse)); - else - goto default; - - break; - } - case 0x22: - { - if(StringHandlers.CToString(vendorId).Trim() == "CERTANCE") - AaruConsole.WriteLine(Modes.PrettifyCertanceModePage_22(page.PageResponse)); - else - goto default; - - break; - } - case 0x24: - { - if(StringHandlers.CToString(vendorId).Trim() == "IBM") - AaruConsole.WriteLine(Modes.PrettifyIBMModePage_24(page.PageResponse)); - else - goto default; - - break; - } - case 0x2A: - { - if(page.Subpage == 0) - AaruConsole.WriteLine(Modes.PrettifyModePage_2A(page.PageResponse)); - else - goto default; - - break; - } - case 0x2F: - { - if(StringHandlers.CToString(vendorId).Trim() == "IBM") - AaruConsole.WriteLine(Modes.PrettifyIBMModePage_2F(page.PageResponse)); - else - goto default; - - break; - } - case 0x30: - { - if(Modes.IsAppleModePage_30(page.PageResponse)) - AaruConsole.WriteLine("Drive identifies as Apple OEM drive"); - else - goto default; - - break; - } - case 0x3B: - { - if(StringHandlers.CToString(vendorId).Trim() == "HP") - AaruConsole.WriteLine(Modes.PrettifyHPModePage_3B(page.PageResponse)); - else - goto default; - - break; - } - case 0x3C: - { - if(StringHandlers.CToString(vendorId).Trim() == "HP") - AaruConsole.WriteLine(Modes.PrettifyHPModePage_3C(page.PageResponse)); - else - goto default; - - break; - } - case 0x3D: - { - if(StringHandlers.CToString(vendorId).Trim() == "IBM") - AaruConsole.WriteLine(Modes.PrettifyIBMModePage_3D(page.PageResponse)); - else if(StringHandlers.CToString(vendorId).Trim() == "HP") - AaruConsole.WriteLine(Modes.PrettifyHPModePage_3D(page.PageResponse)); - else - goto default; - - break; - } - case 0x3E: - { - if(StringHandlers.CToString(vendorId).Trim() == "FUJITSU") - AaruConsole.WriteLine(Modes.PrettifyFujitsuModePage_3E(page.PageResponse)); - else if(StringHandlers.CToString(vendorId).Trim() == "HP") - AaruConsole.WriteLine(Modes.PrettifyHPModePage_3E(page.PageResponse)); - else - goto default; - - break; - } - default: + if(devType == PeripheralDeviceTypes.MultiMediaDevice && + page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_00_SFF(page.PageResponse)); + else { if(page.Subpage != 0) - AaruConsole.WriteLine("Found unknown mode page {0:X2}h subpage {1:X2}h", page.Page, - page.Subpage); + AaruConsole.WriteLine("Found unknown vendor mode page {0:X2}h subpage {1:X2}h", + page.Page, page.Subpage); else - AaruConsole.WriteLine("Found unknown mode page {0:X2}h", page.Page); - - break; + AaruConsole.WriteLine("Found unknown vendor mode page {0:X2}h", page.Page); } + + break; } - } + case 0x01: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(devType == PeripheralDeviceTypes.MultiMediaDevice + ? Modes.PrettifyModePage_01_MMC(page.PageResponse) + : Modes.PrettifyModePage_01(page.PageResponse)); + else + goto default; + + break; + } + case 0x02: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_02(page.PageResponse)); + else + goto default; + + break; + } + case 0x03: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_03(page.PageResponse)); + else + goto default; + + break; + } + case 0x04: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_04(page.PageResponse)); + else + goto default; + + break; + } + case 0x05: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_05(page.PageResponse)); + else + goto default; + + break; + } + case 0x06: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_06(page.PageResponse)); + else + goto default; + + break; + } + case 0x07: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(devType == PeripheralDeviceTypes.MultiMediaDevice + ? Modes.PrettifyModePage_07_MMC(page.PageResponse) + : Modes.PrettifyModePage_07(page.PageResponse)); + else + goto default; + + break; + } + case 0x08: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_08(page.PageResponse)); + else + goto default; + + break; + } + case 0x0A: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_0A(page.PageResponse)); + else if(page.Subpage == 1) + AaruConsole.WriteLine(Modes.PrettifyModePage_0A_S01(page.PageResponse)); + else + goto default; + + break; + } + case 0x0B: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_0B(page.PageResponse)); + else + goto default; + + break; + } + case 0x0D: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_0D(page.PageResponse)); + else + goto default; + + break; + } + case 0x0E: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_0E(page.PageResponse)); + else + goto default; + + break; + } + case 0x0F: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_0F(page.PageResponse)); + else + goto default; + + break; + } + case 0x10: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(devType == PeripheralDeviceTypes.SequentialAccess + ? Modes.PrettifyModePage_10_SSC(page.PageResponse) + : Modes.PrettifyModePage_10(page.PageResponse)); + else + goto default; + + break; + } + case 0x11: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_11(page.PageResponse)); + else + goto default; + + break; + } + case 0x12: + case 0x13: + case 0x14: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_12_13_14(page.PageResponse)); + else + goto default; + + break; + } + case 0x1A: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_1A(page.PageResponse)); + else if(page.Subpage == 1) + AaruConsole.WriteLine(Modes.PrettifyModePage_1A_S01(page.PageResponse)); + else + goto default; + + break; + } + case 0x1B: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_1B(page.PageResponse)); + else + goto default; + + break; + } + case 0x1C: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(devType == PeripheralDeviceTypes.MultiMediaDevice + ? Modes.PrettifyModePage_1C_SFF(page.PageResponse) + : Modes.PrettifyModePage_1C(page.PageResponse)); + else if(page.Subpage == 1) + AaruConsole.WriteLine(Modes.PrettifyModePage_1C_S01(page.PageResponse)); + else + goto default; + + break; + } + case 0x1D: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_1D(page.PageResponse)); + else + goto default; + + break; + } + case 0x21: + { + if(StringHandlers.CToString(vendorId).Trim() == "CERTANCE") + AaruConsole.WriteLine(Modes.PrettifyCertanceModePage_21(page.PageResponse)); + else + goto default; + + break; + } + case 0x22: + { + if(StringHandlers.CToString(vendorId).Trim() == "CERTANCE") + AaruConsole.WriteLine(Modes.PrettifyCertanceModePage_22(page.PageResponse)); + else + goto default; + + break; + } + case 0x24: + { + if(StringHandlers.CToString(vendorId).Trim() == "IBM") + AaruConsole.WriteLine(Modes.PrettifyIBMModePage_24(page.PageResponse)); + else + goto default; + + break; + } + case 0x2A: + { + if(page.Subpage == 0) + AaruConsole.WriteLine(Modes.PrettifyModePage_2A(page.PageResponse)); + else + goto default; + + break; + } + case 0x2F: + { + if(StringHandlers.CToString(vendorId).Trim() == "IBM") + AaruConsole.WriteLine(Modes.PrettifyIBMModePage_2F(page.PageResponse)); + else + goto default; + + break; + } + case 0x30: + { + if(Modes.IsAppleModePage_30(page.PageResponse)) + AaruConsole.WriteLine("Drive identifies as Apple OEM drive"); + else + goto default; + + break; + } + case 0x3B: + { + if(StringHandlers.CToString(vendorId).Trim() == "HP") + AaruConsole.WriteLine(Modes.PrettifyHPModePage_3B(page.PageResponse)); + else + goto default; + + break; + } + case 0x3C: + { + if(StringHandlers.CToString(vendorId).Trim() == "HP") + AaruConsole.WriteLine(Modes.PrettifyHPModePage_3C(page.PageResponse)); + else + goto default; + + break; + } + case 0x3D: + { + if(StringHandlers.CToString(vendorId).Trim() == "IBM") + AaruConsole.WriteLine(Modes.PrettifyIBMModePage_3D(page.PageResponse)); + else if(StringHandlers.CToString(vendorId).Trim() == "HP") + AaruConsole.WriteLine(Modes.PrettifyHPModePage_3D(page.PageResponse)); + else + goto default; + + break; + } + case 0x3E: + { + if(StringHandlers.CToString(vendorId).Trim() == "FUJITSU") + AaruConsole.WriteLine(Modes.PrettifyFujitsuModePage_3E(page.PageResponse)); + else if(StringHandlers.CToString(vendorId).Trim() == "HP") + AaruConsole.WriteLine(Modes.PrettifyHPModePage_3E(page.PageResponse)); + else + goto default; + + break; + } + default: + { + if(page.Subpage != 0) + AaruConsole.WriteLine("Found unknown mode page {0:X2}h subpage {1:X2}h", page.Page, + page.Subpage); + else + AaruConsole.WriteLine("Found unknown mode page {0:X2}h", page.Page); + + break; + } + } } } \ No newline at end of file diff --git a/Aaru.Core/Remote.cs b/Aaru.Core/Remote.cs index 0ca2fa390..8b8bd2a37 100644 --- a/Aaru.Core/Remote.cs +++ b/Aaru.Core/Remote.cs @@ -49,507 +49,506 @@ using Spectre.Console; using CdOffset = Aaru.Database.Models.CdOffset; using Version = Aaru.CommonTypes.Metadata.Version; -namespace Aaru.Core +namespace Aaru.Core; + +/// Handles connections to Aaru.Server +public static class Remote { - /// Handles connections to Aaru.Server - public static class Remote + /// Submits a device report + /// Device report + public static void SubmitReport(DeviceReportV2 report) { - /// Submits a device report - /// Device report - public static void SubmitReport(DeviceReportV2 report) + var submitThread = new Thread(() => { - var submitThread = new Thread(() => + Spectre.ProgressSingleSpinner(ctx => { - Spectre.ProgressSingleSpinner(ctx => + ctx.AddTask("Uploading device report").IsIndeterminate(); + + try { - ctx.AddTask("Uploading device report").IsIndeterminate(); + string json = JsonConvert.SerializeObject(report, Formatting.Indented, + new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }); - try - { - string json = JsonConvert.SerializeObject(report, Formatting.Indented, - new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore - }); + byte[] jsonBytes = Encoding.UTF8.GetBytes(json); + var request = WebRequest.Create("https://www.aaru.app/api/uploadreportv2"); + ((HttpWebRequest)request).UserAgent = $"Aaru {typeof(Version).Assembly.GetName().Version}"; + request.Method = "POST"; + request.ContentLength = jsonBytes.Length; + request.ContentType = "application/json"; + Stream reqStream = request.GetRequestStream(); + reqStream.Write(jsonBytes, 0, jsonBytes.Length); + reqStream.Close(); + WebResponse response = request.GetResponse(); - byte[] jsonBytes = Encoding.UTF8.GetBytes(json); - var request = WebRequest.Create("https://www.aaru.app/api/uploadreportv2"); - ((HttpWebRequest)request).UserAgent = $"Aaru {typeof(Version).Assembly.GetName().Version}"; - request.Method = "POST"; - request.ContentLength = jsonBytes.Length; - request.ContentType = "application/json"; - Stream reqStream = request.GetRequestStream(); - reqStream.Write(jsonBytes, 0, jsonBytes.Length); - reqStream.Close(); - WebResponse response = request.GetResponse(); + if(((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) + return; - if(((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) - return; + Stream data = response.GetResponseStream(); + var reader = new StreamReader(data ?? throw new InvalidOperationException()); - Stream data = response.GetResponseStream(); - var reader = new StreamReader(data ?? throw new InvalidOperationException()); + reader.ReadToEnd(); + data.Close(); + response.Close(); + } + catch(WebException) + { + // Can't connect to the server, do nothing + } - reader.ReadToEnd(); - data.Close(); - response.Close(); - } - catch(WebException) - { - // Can't connect to the server, do nothing - } - - // ReSharper disable once RedundantCatchClause - catch - { - #if DEBUG - if(Debugger.IsAttached) - throw; - #endif - } - }); + // ReSharper disable once RedundantCatchClause + catch + { + #if DEBUG + if(Debugger.IsAttached) + throw; + #endif + } }); + }); - submitThread.Start(); - } + submitThread.Start(); + } - /// Updates the main database - /// If true creates the database from scratch, otherwise updates an existing database - public static void UpdateMainDatabase(bool create) + /// Updates the main database + /// If true creates the database from scratch, otherwise updates an existing database + public static void UpdateMainDatabase(bool create) + { + var mctx = AaruContext.Create(Settings.Settings.MainDbPath); + + if(create) { - var mctx = AaruContext.Create(Settings.Settings.MainDbPath); + mctx.Database.EnsureCreated(); + + mctx.Database. + ExecuteSqlRaw("CREATE TABLE IF NOT EXISTS \"__EFMigrationsHistory\" (\"MigrationId\" TEXT PRIMARY KEY, \"ProductVersion\" TEXT)"); + + foreach(string migration in mctx.Database.GetPendingMigrations()) + { + mctx.Database. + ExecuteSqlRaw($"INSERT INTO \"__EFMigrationsHistory\" (MigrationId, ProductVersion) VALUES ('{migration}', '0.0.0')"); + } + } + else + mctx.Database.Migrate(); + + mctx.SaveChanges(); + + try + { + long lastUpdate = 0; + DateTime latest = DateTime.MinValue; + + if(!create) + { + List latestAll = new(); + + if(mctx.UsbVendors.Any()) + latestAll.Add(mctx.UsbVendors.Max(v => v.ModifiedWhen)); + + if(mctx.UsbProducts.Any()) + latestAll.Add(mctx.UsbProducts.Max(p => p.ModifiedWhen)); + + if(mctx.CdOffsets.Any()) + latestAll.Add(mctx.CdOffsets.Max(o => o.ModifiedWhen)); + + if(mctx.Devices.Any()) + latestAll.Add(mctx.Devices.Max(d => d.LastSynchronized)); + + if(latestAll.Any()) + { + latest = latestAll.Max(t => t); + lastUpdate = (latest.ToFileTimeUtc() - new DateTime(1970, 1, 1).ToFileTimeUtc()) / 10000000; + } + } + + if(lastUpdate == 0) + { + create = true; + AaruConsole.WriteLine("Creating main database"); + } + else + { + AaruConsole.WriteLine("Updating main database"); + AaruConsole.WriteLine("Last update: {0}", latest); + } + + DateTime updateStart = DateTime.UtcNow; + + var request = WebRequest.Create($"https://www.aaru.app/api/update?timestamp={lastUpdate}"); + ((HttpWebRequest)request).UserAgent = $"Aaru {typeof(Version).Assembly.GetName().Version}"; + request.Method = "GET"; + request.ContentType = "application/json"; + WebResponse response = request.GetResponse(); + + if(((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) + { + AaruConsole.ErrorWriteLine("Error {0} when trying to get updated entities.", + ((HttpWebResponse)response).StatusCode); + + return; + } + + Stream data = response.GetResponseStream(); + var reader = new StreamReader(data ?? throw new InvalidOperationException()); + SyncDto sync = JsonConvert.DeserializeObject(reader.ReadToEnd()) ?? new SyncDto(); if(create) { - mctx.Database.EnsureCreated(); + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Adding USB vendors"); + task.MaxValue = sync.UsbVendors.Count; - mctx.Database. - ExecuteSqlRaw("CREATE TABLE IF NOT EXISTS \"__EFMigrationsHistory\" (\"MigrationId\" TEXT PRIMARY KEY, \"ProductVersion\" TEXT)"); + foreach(UsbVendorDto vendor in sync.UsbVendors) + { + task.Increment(1); + mctx.UsbVendors.Add(new UsbVendor(vendor.VendorId, vendor.Vendor)); + } + }); - foreach(string migration in mctx.Database.GetPendingMigrations()) - { - mctx.Database. - ExecuteSqlRaw($"INSERT INTO \"__EFMigrationsHistory\" (MigrationId, ProductVersion) VALUES ('{migration}', '0.0.0')"); - } + AaruConsole.WriteLine("Added {0} usb vendors", sync.UsbVendors.Count); + + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Adding USB products"); + task.MaxValue = sync.UsbProducts.Count; + + foreach(UsbProductDto product in sync.UsbProducts) + { + task.Increment(1); + + mctx.UsbProducts.Add(new UsbProduct(product.VendorId, product.ProductId, + product.Product)); + } + }); + + AaruConsole.WriteLine("Added {0} usb products", sync.UsbProducts.Count); + + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Adding CompactDisc read offsets"); + task.MaxValue = sync.Offsets.Count; + + foreach(CdOffsetDto offset in sync.Offsets) + { + task.Increment(1); + + mctx.CdOffsets.Add(new CdOffset(offset) + { + Id = offset.Id + }); + } + }); + + AaruConsole.WriteLine("Added {0} CompactDisc read offsets", sync.Offsets.Count); + + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Adding known devices"); + task.MaxValue = sync.Devices.Count; + + foreach(DeviceDto device in sync.Devices) + + { + task.Increment(1); + + mctx.Devices.Add(new Device(device) + { + Id = device.Id + }); + } + }); + + AaruConsole.WriteLine("Added {0} known devices", sync.Devices.Count); + + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Adding known iNES/NES 2.0 headers"); + task.MaxValue = sync.NesHeaders?.Count ?? 0; + + foreach(NesHeaderDto header in sync.NesHeaders ?? new List()) + { + task.Increment(1); + + mctx.NesHeaders.Add(new NesHeaderInfo + { + Id = header.Id, + AddedWhen = DateTime.UtcNow, + BatteryPresent = header.BatteryPresent, + ConsoleType = header.ConsoleType, + DefaultExpansionDevice = header.DefaultExpansionDevice, + ExtendedConsoleType = header.ExtendedConsoleType, + FourScreenMode = header.FourScreenMode, + Mapper = header.Mapper, + ModifiedWhen = DateTime.UtcNow, + NametableMirroring = header.NametableMirroring, + Sha256 = header.Sha256, + Submapper = header.Submapper, + TimingMode = header.TimingMode, + VsHardwareType = header.VsHardwareType, + VsPpuType = header.VsPpuType + }); + } + }); + + AaruConsole.WriteLine("Added {0} known iNES/NES 2.0 headers", sync.NesHeaders?.Count ?? 0); } else - mctx.Database.Migrate(); - - mctx.SaveChanges(); - - try { - long lastUpdate = 0; - DateTime latest = DateTime.MinValue; + long addedVendors = 0; + long addedProducts = 0; + long addedOffsets = 0; + long addedDevices = 0; + long addedNesHeaders = 0; + long modifiedVendors = 0; + long modifiedProducts = 0; + long modifiedOffsets = 0; + long modifiedDevices = 0; + long modifiedNesHeaders = 0; - if(!create) - { - List latestAll = new(); + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Updating USB vendors"); + task.MaxValue = sync.UsbVendors.Count; - if(mctx.UsbVendors.Any()) - latestAll.Add(mctx.UsbVendors.Max(v => v.ModifiedWhen)); - - if(mctx.UsbProducts.Any()) - latestAll.Add(mctx.UsbProducts.Max(p => p.ModifiedWhen)); - - if(mctx.CdOffsets.Any()) - latestAll.Add(mctx.CdOffsets.Max(o => o.ModifiedWhen)); - - if(mctx.Devices.Any()) - latestAll.Add(mctx.Devices.Max(d => d.LastSynchronized)); - - if(latestAll.Any()) - { - latest = latestAll.Max(t => t); - lastUpdate = (latest.ToFileTimeUtc() - new DateTime(1970, 1, 1).ToFileTimeUtc()) / 10000000; - } - } - - if(lastUpdate == 0) - { - create = true; - AaruConsole.WriteLine("Creating main database"); - } - else - { - AaruConsole.WriteLine("Updating main database"); - AaruConsole.WriteLine("Last update: {0}", latest); - } - - DateTime updateStart = DateTime.UtcNow; - - var request = WebRequest.Create($"https://www.aaru.app/api/update?timestamp={lastUpdate}"); - ((HttpWebRequest)request).UserAgent = $"Aaru {typeof(Version).Assembly.GetName().Version}"; - request.Method = "GET"; - request.ContentType = "application/json"; - WebResponse response = request.GetResponse(); - - if(((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) - { - AaruConsole.ErrorWriteLine("Error {0} when trying to get updated entities.", - ((HttpWebResponse)response).StatusCode); - - return; - } - - Stream data = response.GetResponseStream(); - var reader = new StreamReader(data ?? throw new InvalidOperationException()); - SyncDto sync = JsonConvert.DeserializeObject(reader.ReadToEnd()) ?? new SyncDto(); - - if(create) - { - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => + foreach(UsbVendorDto vendor in sync.UsbVendors) { - ProgressTask task = ctx.AddTask("Adding USB vendors"); - task.MaxValue = sync.UsbVendors.Count; + task.Increment(1); - foreach(UsbVendorDto vendor in sync.UsbVendors) + UsbVendor existing = + mctx.UsbVendors.FirstOrDefault(v => v.Id == vendor.VendorId); + + if(existing != null) { - task.Increment(1); + modifiedVendors++; + existing.Vendor = vendor.Vendor; + existing.ModifiedWhen = updateStart; + mctx.UsbVendors.Update(existing); + } + else + { + addedVendors++; mctx.UsbVendors.Add(new UsbVendor(vendor.VendorId, vendor.Vendor)); } - }); + } + }); - AaruConsole.WriteLine("Added {0} usb vendors", sync.UsbVendors.Count); + AaruConsole.WriteLine("Added {0} USB vendors", addedVendors); + AaruConsole.WriteLine("Modified {0} USB vendors", modifiedVendors); - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Updating USB products"); + task.MaxValue = sync.UsbVendors.Count; + + foreach(UsbProductDto product in sync.UsbProducts) { - ProgressTask task = ctx.AddTask("Adding USB products"); - task.MaxValue = sync.UsbProducts.Count; + task.Increment(1); - foreach(UsbProductDto product in sync.UsbProducts) + UsbProduct existing = + mctx.UsbProducts.FirstOrDefault(p => p.VendorId == product.VendorId && + p.ProductId == product.ProductId); + + if(existing != null) { - task.Increment(1); + modifiedProducts++; + existing.Product = product.Product; + existing.ModifiedWhen = updateStart; + mctx.UsbProducts.Update(existing); + } + else + { + addedProducts++; mctx.UsbProducts.Add(new UsbProduct(product.VendorId, product.ProductId, product.Product)); } - }); + } + }); - AaruConsole.WriteLine("Added {0} usb products", sync.UsbProducts.Count); + AaruConsole.WriteLine("Added {0} USB products", addedProducts); + AaruConsole.WriteLine("Modified {0} USB products", modifiedProducts); - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Updating CompactDisc read offsets"); + task.MaxValue = sync.Offsets.Count; + + foreach(CdOffsetDto offset in sync.Offsets) { - ProgressTask task = ctx.AddTask("Adding CompactDisc read offsets"); - task.MaxValue = sync.Offsets.Count; + CdOffset existing = mctx.CdOffsets.FirstOrDefault(o => o.Id == offset.Id); + task.Increment(1); - foreach(CdOffsetDto offset in sync.Offsets) + if(existing != null) { - task.Increment(1); + modifiedOffsets++; + existing.Agreement = offset.Agreement; + existing.Manufacturer = offset.Manufacturer; + existing.Model = offset.Model; + existing.Submissions = offset.Submissions; + existing.Offset = offset.Offset; + existing.ModifiedWhen = updateStart; + mctx.CdOffsets.Update(existing); + } + else + { + addedOffsets++; mctx.CdOffsets.Add(new CdOffset(offset) { Id = offset.Id }); } - }); + } + }); - AaruConsole.WriteLine("Added {0} CompactDisc read offsets", sync.Offsets.Count); + AaruConsole.WriteLine("Added {0} CompactDisc read offsets", addedOffsets); + AaruConsole.WriteLine("Modified {0} CompactDisc read offsets", modifiedOffsets); - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Updating known devices"); + task.MaxValue = sync.Offsets.Count; + + foreach(DeviceDto device in sync.Devices) { - ProgressTask task = ctx.AddTask("Adding known devices"); - task.MaxValue = sync.Devices.Count; - - foreach(DeviceDto device in sync.Devices) + task.Increment(1); + Device existing = mctx.Devices.FirstOrDefault(d => d.Id == device.Id); + if(existing != null) { - task.Increment(1); + modifiedDevices++; + + mctx.Remove(existing); + + existing = new Device(device) + { + Id = device.Id, + OptimalMultipleSectorsRead = device.OptimalMultipleSectorsRead, + CanReadGdRomUsingSwapDisc = device.CanReadGdRomUsingSwapDisc + }; + + mctx.Devices.Add(existing); + } + else + { + addedDevices++; mctx.Devices.Add(new Device(device) { - Id = device.Id + Id = device.Id, + OptimalMultipleSectorsRead = device.OptimalMultipleSectorsRead, + CanReadGdRomUsingSwapDisc = device.CanReadGdRomUsingSwapDisc }); } - }); + } + }); - AaruConsole.WriteLine("Added {0} known devices", sync.Devices.Count); + AaruConsole.WriteLine("Added {0} known devices", addedDevices); + AaruConsole.WriteLine("Modified {0} known devices", modifiedDevices); - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => + AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). + Start(ctx => + { + ProgressTask task = ctx.AddTask("Updating known iNES/NES 2.0 headers"); + task.MaxValue = sync.Offsets.Count; + + foreach(NesHeaderDto header in sync.NesHeaders) { - ProgressTask task = ctx.AddTask("Adding known iNES/NES 2.0 headers"); - task.MaxValue = sync.NesHeaders?.Count ?? 0; + task.Increment(1); + NesHeaderInfo existing = mctx.NesHeaders.FirstOrDefault(d => d.Id == header.Id); - foreach(NesHeaderDto header in sync.NesHeaders ?? new List()) + if(existing != null) { - task.Increment(1); + modifiedNesHeaders++; + DateTime addedDate = existing.AddedWhen; + + mctx.Remove(existing); + + existing = new NesHeaderInfo + { + Id = header.Id, + AddedWhen = addedDate, + BatteryPresent = header.BatteryPresent, + ConsoleType = header.ConsoleType, + DefaultExpansionDevice = header.DefaultExpansionDevice, + ExtendedConsoleType = header.ExtendedConsoleType, + FourScreenMode = header.FourScreenMode, + Mapper = header.Mapper, + ModifiedWhen = DateTime.UtcNow, + NametableMirroring = header.NametableMirroring, + Sha256 = header.Sha256, + Submapper = header.Submapper, + TimingMode = header.TimingMode, + VsHardwareType = header.VsHardwareType, + VsPpuType = header.VsPpuType + }; + + mctx.NesHeaders.Add(existing); + } + else + { + addedNesHeaders++; mctx.NesHeaders.Add(new NesHeaderInfo { - Id = header.Id, - AddedWhen = DateTime.UtcNow, - BatteryPresent = header.BatteryPresent, - ConsoleType = header.ConsoleType, + Id = header.Id, + AddedWhen = DateTime.UtcNow, + BatteryPresent = header.BatteryPresent, + ConsoleType = header.ConsoleType, DefaultExpansionDevice = header.DefaultExpansionDevice, - ExtendedConsoleType = header.ExtendedConsoleType, - FourScreenMode = header.FourScreenMode, - Mapper = header.Mapper, - ModifiedWhen = DateTime.UtcNow, - NametableMirroring = header.NametableMirroring, - Sha256 = header.Sha256, - Submapper = header.Submapper, - TimingMode = header.TimingMode, - VsHardwareType = header.VsHardwareType, - VsPpuType = header.VsPpuType + ExtendedConsoleType = header.ExtendedConsoleType, + FourScreenMode = header.FourScreenMode, + Mapper = header.Mapper, + ModifiedWhen = DateTime.UtcNow, + NametableMirroring = header.NametableMirroring, + Sha256 = header.Sha256, + Submapper = header.Submapper, + TimingMode = header.TimingMode, + VsHardwareType = header.VsHardwareType, + VsPpuType = header.VsPpuType }); } - }); + } + }); - AaruConsole.WriteLine("Added {0} known iNES/NES 2.0 headers", sync.NesHeaders?.Count ?? 0); - } - else - { - long addedVendors = 0; - long addedProducts = 0; - long addedOffsets = 0; - long addedDevices = 0; - long addedNesHeaders = 0; - long modifiedVendors = 0; - long modifiedProducts = 0; - long modifiedOffsets = 0; - long modifiedDevices = 0; - long modifiedNesHeaders = 0; - - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => - { - ProgressTask task = ctx.AddTask("Updating USB vendors"); - task.MaxValue = sync.UsbVendors.Count; - - foreach(UsbVendorDto vendor in sync.UsbVendors) - { - task.Increment(1); - - UsbVendor existing = - mctx.UsbVendors.FirstOrDefault(v => v.Id == vendor.VendorId); - - if(existing != null) - { - modifiedVendors++; - existing.Vendor = vendor.Vendor; - existing.ModifiedWhen = updateStart; - mctx.UsbVendors.Update(existing); - } - else - { - addedVendors++; - mctx.UsbVendors.Add(new UsbVendor(vendor.VendorId, vendor.Vendor)); - } - } - }); - - AaruConsole.WriteLine("Added {0} USB vendors", addedVendors); - AaruConsole.WriteLine("Modified {0} USB vendors", modifiedVendors); - - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => - { - ProgressTask task = ctx.AddTask("Updating USB products"); - task.MaxValue = sync.UsbVendors.Count; - - foreach(UsbProductDto product in sync.UsbProducts) - { - task.Increment(1); - - UsbProduct existing = - mctx.UsbProducts.FirstOrDefault(p => p.VendorId == product.VendorId && - p.ProductId == product.ProductId); - - if(existing != null) - { - modifiedProducts++; - existing.Product = product.Product; - existing.ModifiedWhen = updateStart; - mctx.UsbProducts.Update(existing); - } - else - { - addedProducts++; - - mctx.UsbProducts.Add(new UsbProduct(product.VendorId, product.ProductId, - product.Product)); - } - } - }); - - AaruConsole.WriteLine("Added {0} USB products", addedProducts); - AaruConsole.WriteLine("Modified {0} USB products", modifiedProducts); - - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => - { - ProgressTask task = ctx.AddTask("Updating CompactDisc read offsets"); - task.MaxValue = sync.Offsets.Count; - - foreach(CdOffsetDto offset in sync.Offsets) - { - CdOffset existing = mctx.CdOffsets.FirstOrDefault(o => o.Id == offset.Id); - task.Increment(1); - - if(existing != null) - { - modifiedOffsets++; - existing.Agreement = offset.Agreement; - existing.Manufacturer = offset.Manufacturer; - existing.Model = offset.Model; - existing.Submissions = offset.Submissions; - existing.Offset = offset.Offset; - existing.ModifiedWhen = updateStart; - mctx.CdOffsets.Update(existing); - } - else - { - addedOffsets++; - - mctx.CdOffsets.Add(new CdOffset(offset) - { - Id = offset.Id - }); - } - } - }); - - AaruConsole.WriteLine("Added {0} CompactDisc read offsets", addedOffsets); - AaruConsole.WriteLine("Modified {0} CompactDisc read offsets", modifiedOffsets); - - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => - { - ProgressTask task = ctx.AddTask("Updating known devices"); - task.MaxValue = sync.Offsets.Count; - - foreach(DeviceDto device in sync.Devices) - { - task.Increment(1); - Device existing = mctx.Devices.FirstOrDefault(d => d.Id == device.Id); - - if(existing != null) - { - modifiedDevices++; - - mctx.Remove(existing); - - existing = new Device(device) - { - Id = device.Id, - OptimalMultipleSectorsRead = device.OptimalMultipleSectorsRead, - CanReadGdRomUsingSwapDisc = device.CanReadGdRomUsingSwapDisc - }; - - mctx.Devices.Add(existing); - } - else - { - addedDevices++; - - mctx.Devices.Add(new Device(device) - { - Id = device.Id, - OptimalMultipleSectorsRead = device.OptimalMultipleSectorsRead, - CanReadGdRomUsingSwapDisc = device.CanReadGdRomUsingSwapDisc - }); - } - } - }); - - AaruConsole.WriteLine("Added {0} known devices", addedDevices); - AaruConsole.WriteLine("Modified {0} known devices", modifiedDevices); - - AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new ProgressBarColumn(), new PercentageColumn()). - Start(ctx => - { - ProgressTask task = ctx.AddTask("Updating known iNES/NES 2.0 headers"); - task.MaxValue = sync.Offsets.Count; - - foreach(NesHeaderDto header in sync.NesHeaders) - { - task.Increment(1); - NesHeaderInfo existing = mctx.NesHeaders.FirstOrDefault(d => d.Id == header.Id); - - if(existing != null) - { - modifiedNesHeaders++; - DateTime addedDate = existing.AddedWhen; - - mctx.Remove(existing); - - existing = new NesHeaderInfo - { - Id = header.Id, - AddedWhen = addedDate, - BatteryPresent = header.BatteryPresent, - ConsoleType = header.ConsoleType, - DefaultExpansionDevice = header.DefaultExpansionDevice, - ExtendedConsoleType = header.ExtendedConsoleType, - FourScreenMode = header.FourScreenMode, - Mapper = header.Mapper, - ModifiedWhen = DateTime.UtcNow, - NametableMirroring = header.NametableMirroring, - Sha256 = header.Sha256, - Submapper = header.Submapper, - TimingMode = header.TimingMode, - VsHardwareType = header.VsHardwareType, - VsPpuType = header.VsPpuType - }; - - mctx.NesHeaders.Add(existing); - } - else - { - addedNesHeaders++; - - mctx.NesHeaders.Add(new NesHeaderInfo - { - Id = header.Id, - AddedWhen = DateTime.UtcNow, - BatteryPresent = header.BatteryPresent, - ConsoleType = header.ConsoleType, - DefaultExpansionDevice = header.DefaultExpansionDevice, - ExtendedConsoleType = header.ExtendedConsoleType, - FourScreenMode = header.FourScreenMode, - Mapper = header.Mapper, - ModifiedWhen = DateTime.UtcNow, - NametableMirroring = header.NametableMirroring, - Sha256 = header.Sha256, - Submapper = header.Submapper, - TimingMode = header.TimingMode, - VsHardwareType = header.VsHardwareType, - VsPpuType = header.VsPpuType - }); - } - } - }); - - AaruConsole.WriteLine("Added {0} known iNES/NES 2.0 headers", addedDevices); - AaruConsole.WriteLine("Modified {0} known iNES/NES 2.0 headers", modifiedDevices); - } + AaruConsole.WriteLine("Added {0} known iNES/NES 2.0 headers", addedDevices); + AaruConsole.WriteLine("Modified {0} known iNES/NES 2.0 headers", modifiedDevices); } - catch(Exception ex) + } + catch(Exception ex) + { + AaruConsole.ErrorWriteLine("Exception {0} when updating database.", ex); + } + finally + { + Spectre.ProgressSingleSpinner(ctx => { - AaruConsole.ErrorWriteLine("Exception {0} when updating database.", ex); - } - finally - { - Spectre.ProgressSingleSpinner(ctx => - { - ctx.AddTask("Saving changes...").IsIndeterminate(); - mctx.SaveChanges(); - }); - } + ctx.AddTask("Saving changes...").IsIndeterminate(); + mctx.SaveChanges(); + }); } } } \ No newline at end of file diff --git a/Aaru.Core/Sidecar/AudioMedia.cs b/Aaru.Core/Sidecar/AudioMedia.cs index b5dfd914e..e80e15acf 100644 --- a/Aaru.Core/Sidecar/AudioMedia.cs +++ b/Aaru.Core/Sidecar/AudioMedia.cs @@ -38,54 +38,53 @@ using Aaru.CommonTypes; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Core -{ - public sealed partial class Sidecar - { - // TODO: Complete it - /// Creates a metadata sidecar for an audio media (e.g. cassette) - /// Image - /// Filter uuid - /// Image path - /// Image file information - /// Image plugins - /// List of image checksums - /// Metadata sidecar - /// Encoding to be used for filesystem plugins - static void AudioMedia(IBaseImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins, - List imgChecksums, ref CICMMetadataType sidecar, Encoding encoding) - { - sidecar.AudioMedia = new[] - { - new AudioMediaType - { - Checksums = imgChecksums.ToArray(), - Image = new ImageType - { - format = image.Format, - offset = 0, - offsetSpecified = true, - Value = Path.GetFileName(imagePath) - }, - Size = (ulong)fi.Length, - Sequence = new SequenceType - { - MediaTitle = image.Info.MediaTitle - } - } - }; +namespace Aaru.Core; - if(image.Info.MediaSequence != 0 && - image.Info.LastMediaSequence != 0) +public sealed partial class Sidecar +{ + // TODO: Complete it + /// Creates a metadata sidecar for an audio media (e.g. cassette) + /// Image + /// Filter uuid + /// Image path + /// Image file information + /// Image plugins + /// List of image checksums + /// Metadata sidecar + /// Encoding to be used for filesystem plugins + static void AudioMedia(IBaseImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins, + List imgChecksums, ref CICMMetadataType sidecar, Encoding encoding) + { + sidecar.AudioMedia = new[] + { + new AudioMediaType { - sidecar.AudioMedia[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence; - sidecar.AudioMedia[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence; - } - else - { - sidecar.AudioMedia[0].Sequence.MediaSequence = 1; - sidecar.AudioMedia[0].Sequence.TotalMedia = 1; + Checksums = imgChecksums.ToArray(), + Image = new ImageType + { + format = image.Format, + offset = 0, + offsetSpecified = true, + Value = Path.GetFileName(imagePath) + }, + Size = (ulong)fi.Length, + Sequence = new SequenceType + { + MediaTitle = image.Info.MediaTitle + } } + }; + + if(image.Info.MediaSequence != 0 && + image.Info.LastMediaSequence != 0) + { + sidecar.AudioMedia[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence; + sidecar.AudioMedia[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence; + } + else + { + sidecar.AudioMedia[0].Sequence.MediaSequence = 1; + sidecar.AudioMedia[0].Sequence.TotalMedia = 1; } } } \ No newline at end of file diff --git a/Aaru.Core/Sidecar/BlockMedia.cs b/Aaru.Core/Sidecar/BlockMedia.cs index 1a75988ad..4ae9e443b 100644 --- a/Aaru.Core/Sidecar/BlockMedia.cs +++ b/Aaru.Core/Sidecar/BlockMedia.cs @@ -49,455 +49,555 @@ using Schemas; using MediaType = Aaru.CommonTypes.Metadata.MediaType; using Tuple = Aaru.Decoders.PCMCIA.Tuple; -namespace Aaru.Core +namespace Aaru.Core; + +public sealed partial class Sidecar { - public sealed partial class Sidecar + /// Creates a metadata sidecar for a block media (e.g. floppy, hard disk, flash card, usb stick) + /// Image + /// Filter uuid + /// Image path + /// Image file information + /// Image plugins + /// List of image checksums + /// Metadata sidecar + /// Encoding to be used for filesystem plugins + void BlockMedia(IMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins, + List imgChecksums, ref CICMMetadataType sidecar, Encoding encoding) { - /// Creates a metadata sidecar for a block media (e.g. floppy, hard disk, flash card, usb stick) - /// Image - /// Filter uuid - /// Image path - /// Image file information - /// Image plugins - /// List of image checksums - /// Metadata sidecar - /// Encoding to be used for filesystem plugins - void BlockMedia(IMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins, - List imgChecksums, ref CICMMetadataType sidecar, Encoding encoding) + if(_aborted) + return; + + sidecar.BlockMedia = new[] + { + new BlockMediaType + { + Checksums = imgChecksums.ToArray(), + Image = new ImageType + { + format = image.Format, + offset = 0, + offsetSpecified = true, + Value = Path.GetFileName(imagePath) + }, + Size = (ulong)fi.Length, + Sequence = new SequenceType + { + MediaTitle = image.Info.MediaTitle + } + } + }; + + if(image.Info.MediaSequence != 0 && + image.Info.LastMediaSequence != 0) + { + sidecar.BlockMedia[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence; + sidecar.BlockMedia[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence; + } + else + { + sidecar.BlockMedia[0].Sequence.MediaSequence = 1; + sidecar.BlockMedia[0].Sequence.TotalMedia = 1; + } + + UpdateStatus("Hashing media tags..."); + ErrorNumber errno; + byte[] buffer; + + foreach(MediaTagType tagType in image.Info.ReadableMediaTags) { if(_aborted) return; - sidecar.BlockMedia = new[] + switch(tagType) { - new BlockMediaType - { - Checksums = imgChecksums.ToArray(), - Image = new ImageType + case MediaTagType.ATAPI_IDENTIFY: + errno = image.ReadMediaTag(MediaTagType.ATAPI_IDENTIFY, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].ATA = new ATAType { - format = image.Format, - offset = 0, - offsetSpecified = true, - Value = Path.GetFileName(imagePath) - }, - Size = (ulong)fi.Length, - Sequence = new SequenceType + Identify = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + } + }; + + break; + case MediaTagType.ATA_IDENTIFY: + errno = image.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].ATA = new ATAType { - MediaTitle = image.Info.MediaTitle - } - } - }; + Identify = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + } + }; - if(image.Info.MediaSequence != 0 && - image.Info.LastMediaSequence != 0) - { - sidecar.BlockMedia[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence; - sidecar.BlockMedia[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence; - } - else - { - sidecar.BlockMedia[0].Sequence.MediaSequence = 1; - sidecar.BlockMedia[0].Sequence.TotalMedia = 1; + break; + case MediaTagType.PCMCIA_CIS: + errno = image.ReadMediaTag(MediaTagType.PCMCIA_CIS, out byte[] cis); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].PCMCIA = new PCMCIAType + { + CIS = new DumpType + { + Checksums = Checksum.GetChecksums(cis).ToArray(), + Size = (ulong)cis.Length + } + }; + + Tuple[] tuples = CIS.GetTuples(cis); + + if(tuples != null) + foreach(Tuple tuple in tuples) + switch(tuple.Code) + { + case TupleCodes.CISTPL_MANFID: + ManufacturerIdentificationTuple manfid = + CIS.DecodeManufacturerIdentificationTuple(tuple); + + if(manfid != null) + { + sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = manfid.ManufacturerID; + + sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID; + sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true; + sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified = true; + } + + break; + case TupleCodes.CISTPL_VERS_1: + Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple); + + if(vers != null) + { + sidecar.BlockMedia[0].PCMCIA.Manufacturer = vers.Manufacturer; + sidecar.BlockMedia[0].PCMCIA.ProductName = vers.Product; + + sidecar.BlockMedia[0].PCMCIA.Compliance = + $"{vers.MajorVersion}.{vers.MinorVersion}"; + + sidecar.BlockMedia[0].PCMCIA.AdditionalInformation = + vers.AdditionalInformation; + } + + break; + } + + break; + case MediaTagType.SCSI_INQUIRY: + errno = image.ReadMediaTag(MediaTagType.SCSI_INQUIRY, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].SCSI = new SCSIType + { + Inquiry = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + } + }; + + break; + case MediaTagType.SD_CID: + errno = image.ReadMediaTag(MediaTagType.SD_CID, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].SecureDigital ??= new SecureDigitalType(); + + sidecar.BlockMedia[0].SecureDigital.CID = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.SD_CSD: + errno = image.ReadMediaTag(MediaTagType.SD_CSD, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].SecureDigital ??= new SecureDigitalType(); + + sidecar.BlockMedia[0].SecureDigital.CSD = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.SD_SCR: + errno = image.ReadMediaTag(MediaTagType.SD_SCR, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].SecureDigital ??= new SecureDigitalType(); + + sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.SD_OCR: + errno = image.ReadMediaTag(MediaTagType.SD_OCR, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].SecureDigital ??= new SecureDigitalType(); + + sidecar.BlockMedia[0].SecureDigital.OCR = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.MMC_CID: + errno = image.ReadMediaTag(MediaTagType.MMC_CID, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].MultiMediaCard ??= new MultiMediaCardType(); + + sidecar.BlockMedia[0].MultiMediaCard.CID = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.MMC_CSD: + errno = image.ReadMediaTag(MediaTagType.MMC_CSD, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].MultiMediaCard ??= new MultiMediaCardType(); + + sidecar.BlockMedia[0].MultiMediaCard.CSD = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.MMC_OCR: + errno = image.ReadMediaTag(MediaTagType.MMC_OCR, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].MultiMediaCard ??= new MultiMediaCardType(); + + sidecar.BlockMedia[0].MultiMediaCard.OCR = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.MMC_ExtendedCSD: + errno = image.ReadMediaTag(MediaTagType.MMC_ExtendedCSD, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].MultiMediaCard ??= new MultiMediaCardType(); + + sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.USB_Descriptors: + errno = image.ReadMediaTag(MediaTagType.USB_Descriptors, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].USB ??= new USBType(); + + sidecar.BlockMedia[0].USB.Descriptors = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.SCSI_MODESENSE_6: + errno = image.ReadMediaTag(MediaTagType.SCSI_MODESENSE_6, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].SCSI ??= new SCSIType(); + + sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; + case MediaTagType.SCSI_MODESENSE_10: + errno = image.ReadMediaTag(MediaTagType.SCSI_MODESENSE_10, out buffer); + + if(errno != ErrorNumber.NoError) + break; + + sidecar.BlockMedia[0].SCSI ??= new SCSIType(); + + sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType + { + Checksums = Checksum.GetChecksums(buffer).ToArray(), + Size = (ulong)buffer.Length + }; + + break; } + } - UpdateStatus("Hashing media tags..."); - ErrorNumber errno; - byte[] buffer; + // If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum. + if(image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") && + filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) + sidecar.BlockMedia[0].ContentChecksums = sidecar.BlockMedia[0].Checksums; + else + { + UpdateStatus("Hashing sectors..."); - foreach(MediaTagType tagType in image.Info.ReadableMediaTags) + var contentChkWorker = new Checksum(); + + // For fast debugging, skip checksum + //goto skipImageChecksum; + + uint sectorsToRead = 64; + ulong sectors = image.Info.Sectors; + ulong doneSectors = 0; + + InitProgress2(); + + while(doneSectors < sectors) { if(_aborted) + { + EndProgress2(); + return; - - switch(tagType) - { - case MediaTagType.ATAPI_IDENTIFY: - errno = image.ReadMediaTag(MediaTagType.ATAPI_IDENTIFY, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].ATA = new ATAType - { - Identify = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - } - }; - - break; - case MediaTagType.ATA_IDENTIFY: - errno = image.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].ATA = new ATAType - { - Identify = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - } - }; - - break; - case MediaTagType.PCMCIA_CIS: - errno = image.ReadMediaTag(MediaTagType.PCMCIA_CIS, out byte[] cis); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].PCMCIA = new PCMCIAType - { - CIS = new DumpType - { - Checksums = Checksum.GetChecksums(cis).ToArray(), - Size = (ulong)cis.Length - } - }; - - Tuple[] tuples = CIS.GetTuples(cis); - - if(tuples != null) - foreach(Tuple tuple in tuples) - switch(tuple.Code) - { - case TupleCodes.CISTPL_MANFID: - ManufacturerIdentificationTuple manfid = - CIS.DecodeManufacturerIdentificationTuple(tuple); - - if(manfid != null) - { - sidecar.BlockMedia[0].PCMCIA.ManufacturerCode = manfid.ManufacturerID; - - sidecar.BlockMedia[0].PCMCIA.CardCode = manfid.CardID; - sidecar.BlockMedia[0].PCMCIA.ManufacturerCodeSpecified = true; - sidecar.BlockMedia[0].PCMCIA.CardCodeSpecified = true; - } - - break; - case TupleCodes.CISTPL_VERS_1: - Level1VersionTuple vers = CIS.DecodeLevel1VersionTuple(tuple); - - if(vers != null) - { - sidecar.BlockMedia[0].PCMCIA.Manufacturer = vers.Manufacturer; - sidecar.BlockMedia[0].PCMCIA.ProductName = vers.Product; - - sidecar.BlockMedia[0].PCMCIA.Compliance = - $"{vers.MajorVersion}.{vers.MinorVersion}"; - - sidecar.BlockMedia[0].PCMCIA.AdditionalInformation = - vers.AdditionalInformation; - } - - break; - } - - break; - case MediaTagType.SCSI_INQUIRY: - errno = image.ReadMediaTag(MediaTagType.SCSI_INQUIRY, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].SCSI = new SCSIType - { - Inquiry = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - } - }; - - break; - case MediaTagType.SD_CID: - errno = image.ReadMediaTag(MediaTagType.SD_CID, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].SecureDigital ??= new SecureDigitalType(); - - sidecar.BlockMedia[0].SecureDigital.CID = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.SD_CSD: - errno = image.ReadMediaTag(MediaTagType.SD_CSD, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].SecureDigital ??= new SecureDigitalType(); - - sidecar.BlockMedia[0].SecureDigital.CSD = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.SD_SCR: - errno = image.ReadMediaTag(MediaTagType.SD_SCR, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].SecureDigital ??= new SecureDigitalType(); - - sidecar.BlockMedia[0].SecureDigital.SCR = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.SD_OCR: - errno = image.ReadMediaTag(MediaTagType.SD_OCR, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].SecureDigital ??= new SecureDigitalType(); - - sidecar.BlockMedia[0].SecureDigital.OCR = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.MMC_CID: - errno = image.ReadMediaTag(MediaTagType.MMC_CID, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].MultiMediaCard ??= new MultiMediaCardType(); - - sidecar.BlockMedia[0].MultiMediaCard.CID = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.MMC_CSD: - errno = image.ReadMediaTag(MediaTagType.MMC_CSD, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].MultiMediaCard ??= new MultiMediaCardType(); - - sidecar.BlockMedia[0].MultiMediaCard.CSD = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.MMC_OCR: - errno = image.ReadMediaTag(MediaTagType.MMC_OCR, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].MultiMediaCard ??= new MultiMediaCardType(); - - sidecar.BlockMedia[0].MultiMediaCard.OCR = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.MMC_ExtendedCSD: - errno = image.ReadMediaTag(MediaTagType.MMC_ExtendedCSD, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].MultiMediaCard ??= new MultiMediaCardType(); - - sidecar.BlockMedia[0].MultiMediaCard.ExtendedCSD = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.USB_Descriptors: - errno = image.ReadMediaTag(MediaTagType.USB_Descriptors, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].USB ??= new USBType(); - - sidecar.BlockMedia[0].USB.Descriptors = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.SCSI_MODESENSE_6: - errno = image.ReadMediaTag(MediaTagType.SCSI_MODESENSE_6, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].SCSI ??= new SCSIType(); - - sidecar.BlockMedia[0].SCSI.ModeSense = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; - case MediaTagType.SCSI_MODESENSE_10: - errno = image.ReadMediaTag(MediaTagType.SCSI_MODESENSE_10, out buffer); - - if(errno != ErrorNumber.NoError) - break; - - sidecar.BlockMedia[0].SCSI ??= new SCSIType(); - - sidecar.BlockMedia[0].SCSI.ModeSense10 = new DumpType - { - Checksums = Checksum.GetChecksums(buffer).ToArray(), - Size = (ulong)buffer.Length - }; - - break; } - } - // If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum. - if(image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") && - filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000")) - sidecar.BlockMedia[0].ContentChecksums = sidecar.BlockMedia[0].Checksums; - else - { - UpdateStatus("Hashing sectors..."); + byte[] sector; - var contentChkWorker = new Checksum(); - - // For fast debugging, skip checksum - //goto skipImageChecksum; - - uint sectorsToRead = 64; - ulong sectors = image.Info.Sectors; - ulong doneSectors = 0; - - InitProgress2(); - - while(doneSectors < sectors) + if(sectors - doneSectors >= sectorsToRead) { - if(_aborted) + errno = image.ReadSectors(doneSectors, sectorsToRead, out sector); + + if(errno != ErrorNumber.NoError) { + UpdateStatus($"Error {errno} reading sector {doneSectors}"); EndProgress2(); return; } - byte[] sector; + UpdateProgress2("Hashing sector {0} of {1}", (long)doneSectors, (long)sectors); + doneSectors += sectorsToRead; + } + else + { + errno = image.ReadSectors(doneSectors, (uint)(sectors - doneSectors), out sector); - if(sectors - doneSectors >= sectorsToRead) + if(errno != ErrorNumber.NoError) { - errno = image.ReadSectors(doneSectors, sectorsToRead, out sector); + UpdateStatus($"Error {errno} reading sector {doneSectors}"); + EndProgress2(); - if(errno != ErrorNumber.NoError) - { - UpdateStatus($"Error {errno} reading sector {doneSectors}"); - EndProgress2(); - - return; - } - - UpdateProgress2("Hashing sector {0} of {1}", (long)doneSectors, (long)sectors); - doneSectors += sectorsToRead; - } - else - { - errno = image.ReadSectors(doneSectors, (uint)(sectors - doneSectors), out sector); - - if(errno != ErrorNumber.NoError) - { - UpdateStatus($"Error {errno} reading sector {doneSectors}"); - EndProgress2(); - - return; - } - - UpdateProgress2("Hashing sector {0} of {1}", (long)doneSectors, (long)sectors); - doneSectors += sectors - doneSectors; + return; } - contentChkWorker.Update(sector); + UpdateProgress2("Hashing sector {0} of {1}", (long)doneSectors, (long)sectors); + doneSectors += sectors - doneSectors; } - // For fast debugging, skip checksum - //skipImageChecksum: - - List cntChecksums = contentChkWorker.End(); - - sidecar.BlockMedia[0].ContentChecksums = cntChecksums.ToArray(); - - EndProgress2(); + contentChkWorker.Update(sector); } - (string type, string subType) diskType = MediaType.MediaTypeToString(image.Info.MediaType); - sidecar.BlockMedia[0].DiskType = diskType.type; - sidecar.BlockMedia[0].DiskSubType = diskType.subType; - Statistics.AddMedia(image.Info.MediaType, false); + // For fast debugging, skip checksum + //skipImageChecksum: - sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType); + List cntChecksums = contentChkWorker.End(); - sidecar.BlockMedia[0].LogicalBlocks = image.Info.Sectors; - sidecar.BlockMedia[0].LogicalBlockSize = image.Info.SectorSize; + sidecar.BlockMedia[0].ContentChecksums = cntChecksums.ToArray(); - // TODO: Detect it - sidecar.BlockMedia[0].PhysicalBlockSize = image.Info.SectorSize; + EndProgress2(); + } - if(image is ITapeImage { IsTape: true } tapeImage) + (string type, string subType) diskType = MediaType.MediaTypeToString(image.Info.MediaType); + sidecar.BlockMedia[0].DiskType = diskType.type; + sidecar.BlockMedia[0].DiskSubType = diskType.subType; + Statistics.AddMedia(image.Info.MediaType, false); + + sidecar.BlockMedia[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType); + + sidecar.BlockMedia[0].LogicalBlocks = image.Info.Sectors; + sidecar.BlockMedia[0].LogicalBlockSize = image.Info.SectorSize; + + // TODO: Detect it + sidecar.BlockMedia[0].PhysicalBlockSize = image.Info.SectorSize; + + if(image is ITapeImage { IsTape: true } tapeImage) + { + List tapePartitions = new(); + + foreach(TapePartition tapePartition in tapeImage.TapePartitions) { - List tapePartitions = new(); - - foreach(TapePartition tapePartition in tapeImage.TapePartitions) + var thisPartition = new TapePartitionType { - var thisPartition = new TapePartitionType + Image = sidecar.BlockMedia[0].Image, + Sequence = tapePartition.Number, + StartBlock = tapePartition.FirstBlock, + EndBlock = tapePartition.LastBlock + }; + + if(tapeImage.TapePartitions.Count == 1) + thisPartition.Checksums = sidecar.BlockMedia[0].ContentChecksums; + else + { + UpdateStatus($"Hashing partition {tapePartition.Number}..."); + + if(_aborted) + return; + + var tapePartitionChk = new Checksum(); + + // For fast debugging, skip checksum + //goto skipImageChecksum; + + uint sectorsToRead = 64; + ulong sectors = tapePartition.LastBlock - tapePartition.FirstBlock + 1; + ulong doneSectors = 0; + + InitProgress2(); + + while(doneSectors < sectors) { + if(_aborted) + { + EndProgress2(); + + return; + } + + byte[] sector; + + if(sectors - doneSectors >= sectorsToRead) + { + errno = image.ReadSectors(tapePartition.FirstBlock + doneSectors, sectorsToRead, + out sector); + + if(errno != ErrorNumber.NoError) + { + AaruConsole. + ErrorWriteLine($"Error {errno} reading sector {tapePartition.FirstBlock + doneSectors}"); + + EndProgress2(); + + return; + } + + UpdateProgress2("Hashing blocks {0} of {1}", (long)doneSectors, (long)sectors); + doneSectors += sectorsToRead; + } + else + { + errno = image.ReadSectors(tapePartition.FirstBlock + doneSectors, + (uint)(sectors - doneSectors), out sector); + + if(errno != ErrorNumber.NoError) + { + AaruConsole. + ErrorWriteLine($"Error {errno} reading sector {tapePartition.FirstBlock + doneSectors}"); + + EndProgress2(); + + return; + } + + UpdateProgress2("Hashing blocks {0} of {1}", (long)doneSectors, (long)sectors); + doneSectors += sectors - doneSectors; + } + + thisPartition.Size += (ulong)sector.LongLength; + + tapePartitionChk.Update(sector); + } + + // For fast debugging, skip checksum + //skipImageChecksum: + + List partitionChecksums = tapePartitionChk.End(); + + thisPartition.Checksums = partitionChecksums.ToArray(); + + EndProgress2(); + } + + List filesInPartition = new(); + + foreach(TapeFile tapeFile in tapeImage.Files.Where(f => f.Partition == tapePartition.Number)) + { + var thisFile = new TapeFileType + { + Sequence = tapeFile.File, + StartBlock = tapeFile.FirstBlock, + EndBlock = tapeFile.LastBlock, Image = sidecar.BlockMedia[0].Image, - Sequence = tapePartition.Number, - StartBlock = tapePartition.FirstBlock, - EndBlock = tapePartition.LastBlock + Size = 0, + BlockSize = 0 }; - if(tapeImage.TapePartitions.Count == 1) - thisPartition.Checksums = sidecar.BlockMedia[0].ContentChecksums; + if(tapeImage.Files.Count(f => f.Partition == tapePartition.Number) == 1) + { + thisFile.Checksums = thisPartition.Checksums; + thisFile.Size = thisPartition.Size; + } else { - UpdateStatus($"Hashing partition {tapePartition.Number}..."); + UpdateStatus($"Hashing file {tapeFile.File}..."); if(_aborted) return; - var tapePartitionChk = new Checksum(); + var tapeFileChk = new Checksum(); // For fast debugging, skip checksum //goto skipImageChecksum; uint sectorsToRead = 64; - ulong sectors = tapePartition.LastBlock - tapePartition.FirstBlock + 1; + ulong sectors = tapeFile.LastBlock - tapeFile.FirstBlock + 1; ulong doneSectors = 0; InitProgress2(); @@ -515,13 +615,13 @@ namespace Aaru.Core if(sectors - doneSectors >= sectorsToRead) { - errno = image.ReadSectors(tapePartition.FirstBlock + doneSectors, sectorsToRead, + errno = image.ReadSectors(tapeFile.FirstBlock + doneSectors, sectorsToRead, out sector); if(errno != ErrorNumber.NoError) { AaruConsole. - ErrorWriteLine($"Error {errno} reading sector {tapePartition.FirstBlock + doneSectors}"); + ErrorWriteLine($"Error {errno} reading sector {tapeFile.FirstBlock + doneSectors}"); EndProgress2(); @@ -533,13 +633,13 @@ namespace Aaru.Core } else { - errno = image.ReadSectors(tapePartition.FirstBlock + doneSectors, + errno = image.ReadSectors(tapeFile.FirstBlock + doneSectors, (uint)(sectors - doneSectors), out sector); if(errno != ErrorNumber.NoError) { AaruConsole. - ErrorWriteLine($"Error {errno} reading sector {tapePartition.FirstBlock + doneSectors}"); + ErrorWriteLine($"Error {errno} reading sector {tapeFile.FirstBlock + doneSectors}"); EndProgress2(); @@ -550,216 +650,61 @@ namespace Aaru.Core doneSectors += sectors - doneSectors; } - thisPartition.Size += (ulong)sector.LongLength; + if((ulong)sector.LongLength > thisFile.BlockSize) + thisFile.BlockSize = (ulong)sector.LongLength; - tapePartitionChk.Update(sector); + thisFile.Size += (ulong)sector.LongLength; + + tapeFileChk.Update(sector); } // For fast debugging, skip checksum //skipImageChecksum: - List partitionChecksums = tapePartitionChk.End(); + List fileChecksums = tapeFileChk.End(); - thisPartition.Checksums = partitionChecksums.ToArray(); + thisFile.Checksums = fileChecksums.ToArray(); EndProgress2(); } - List filesInPartition = new(); - - foreach(TapeFile tapeFile in tapeImage.Files.Where(f => f.Partition == tapePartition.Number)) - { - var thisFile = new TapeFileType - { - Sequence = tapeFile.File, - StartBlock = tapeFile.FirstBlock, - EndBlock = tapeFile.LastBlock, - Image = sidecar.BlockMedia[0].Image, - Size = 0, - BlockSize = 0 - }; - - if(tapeImage.Files.Count(f => f.Partition == tapePartition.Number) == 1) - { - thisFile.Checksums = thisPartition.Checksums; - thisFile.Size = thisPartition.Size; - } - else - { - UpdateStatus($"Hashing file {tapeFile.File}..."); - - if(_aborted) - return; - - var tapeFileChk = new Checksum(); - - // For fast debugging, skip checksum - //goto skipImageChecksum; - - uint sectorsToRead = 64; - ulong sectors = tapeFile.LastBlock - tapeFile.FirstBlock + 1; - ulong doneSectors = 0; - - InitProgress2(); - - while(doneSectors < sectors) - { - if(_aborted) - { - EndProgress2(); - - return; - } - - byte[] sector; - - if(sectors - doneSectors >= sectorsToRead) - { - errno = image.ReadSectors(tapeFile.FirstBlock + doneSectors, sectorsToRead, - out sector); - - if(errno != ErrorNumber.NoError) - { - AaruConsole. - ErrorWriteLine($"Error {errno} reading sector {tapeFile.FirstBlock + doneSectors}"); - - EndProgress2(); - - return; - } - - UpdateProgress2("Hashing blocks {0} of {1}", (long)doneSectors, (long)sectors); - doneSectors += sectorsToRead; - } - else - { - errno = image.ReadSectors(tapeFile.FirstBlock + doneSectors, - (uint)(sectors - doneSectors), out sector); - - if(errno != ErrorNumber.NoError) - { - AaruConsole. - ErrorWriteLine($"Error {errno} reading sector {tapeFile.FirstBlock + doneSectors}"); - - EndProgress2(); - - return; - } - - UpdateProgress2("Hashing blocks {0} of {1}", (long)doneSectors, (long)sectors); - doneSectors += sectors - doneSectors; - } - - if((ulong)sector.LongLength > thisFile.BlockSize) - thisFile.BlockSize = (ulong)sector.LongLength; - - thisFile.Size += (ulong)sector.LongLength; - - tapeFileChk.Update(sector); - } - - // For fast debugging, skip checksum - //skipImageChecksum: - - List fileChecksums = tapeFileChk.End(); - - thisFile.Checksums = fileChecksums.ToArray(); - - EndProgress2(); - } - - filesInPartition.Add(thisFile); - } - - thisPartition.File = filesInPartition.ToArray(); - tapePartitions.Add(thisPartition); + filesInPartition.Add(thisFile); } - sidecar.BlockMedia[0].TapeInformation = tapePartitions.ToArray(); + thisPartition.File = filesInPartition.ToArray(); + tapePartitions.Add(thisPartition); } - UpdateStatus("Checking filesystems..."); + sidecar.BlockMedia[0].TapeInformation = tapePartitions.ToArray(); + } - if(_aborted) - return; + UpdateStatus("Checking filesystems..."); - List partitions = Partitions.GetAll(image); - Partitions.AddSchemesToStats(partitions); + if(_aborted) + return; - sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[1]; + List partitions = Partitions.GetAll(image); + Partitions.AddSchemesToStats(partitions); - if(partitions.Count > 0) - { - sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[partitions.Count]; + sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[1]; - for(int i = 0; i < partitions.Count; i++) - { - if(_aborted) - return; + if(partitions.Count > 0) + { + sidecar.BlockMedia[0].FileSystemInformation = new PartitionType[partitions.Count]; - sidecar.BlockMedia[0].FileSystemInformation[i] = new PartitionType - { - Description = partitions[i].Description, - EndSector = partitions[i].End, - Name = partitions[i].Name, - Sequence = (uint)partitions[i].Sequence, - StartSector = partitions[i].Start, - Type = partitions[i].Type - }; - - List lstFs = new(); - - foreach(IFilesystem plugin in plugins.PluginsList.Values) - try - { - if(_aborted) - return; - - if(!plugin.Identify(image, partitions[i])) - continue; - - if(plugin is IReadOnlyFilesystem fsPlugin && - fsPlugin.Mount(image, partitions[i], encoding, null, null) == ErrorNumber.NoError) - { - UpdateStatus($"Mounting {fsPlugin.XmlFsType.Type}"); - - fsPlugin.XmlFsType.Contents = Files(fsPlugin); - - fsPlugin.Unmount(); - } - else - plugin.GetInformation(image, partitions[i], out _, encoding); - - lstFs.Add(plugin.XmlFsType); - Statistics.AddFilesystem(plugin.XmlFsType.Type); - } - #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body - catch - #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body - { - //AaruConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); - } - - if(lstFs.Count > 0) - sidecar.BlockMedia[0].FileSystemInformation[i].FileSystems = lstFs.ToArray(); - } - } - else + for(int i = 0; i < partitions.Count; i++) { if(_aborted) return; - sidecar.BlockMedia[0].FileSystemInformation[0] = new PartitionType + sidecar.BlockMedia[0].FileSystemInformation[i] = new PartitionType { - StartSector = 0, - EndSector = image.Info.Sectors - 1 - }; - - var wholePart = new Partition - { - Name = "Whole device", - Length = image.Info.Sectors, - Size = image.Info.Sectors * image.Info.SectorSize + Description = partitions[i].Description, + EndSector = partitions[i].End, + Name = partitions[i].Name, + Sequence = (uint)partitions[i].Sequence, + StartSector = partitions[i].Start, + Type = partitions[i].Type }; List lstFs = new(); @@ -770,11 +715,11 @@ namespace Aaru.Core if(_aborted) return; - if(!plugin.Identify(image, wholePart)) + if(!plugin.Identify(image, partitions[i])) continue; if(plugin is IReadOnlyFilesystem fsPlugin && - fsPlugin.Mount(image, wholePart, encoding, null, null) == ErrorNumber.NoError) + fsPlugin.Mount(image, partitions[i], encoding, null, null) == ErrorNumber.NoError) { UpdateStatus($"Mounting {fsPlugin.XmlFsType.Type}"); @@ -783,7 +728,7 @@ namespace Aaru.Core fsPlugin.Unmount(); } else - plugin.GetInformation(image, wholePart, out _, encoding); + plugin.GetInformation(image, partitions[i], out _, encoding); lstFs.Add(plugin.XmlFsType); Statistics.AddFilesystem(plugin.XmlFsType.Type); @@ -796,467 +741,521 @@ namespace Aaru.Core } if(lstFs.Count > 0) - sidecar.BlockMedia[0].FileSystemInformation[0].FileSystems = lstFs.ToArray(); + sidecar.BlockMedia[0].FileSystemInformation[i].FileSystems = lstFs.ToArray(); } - - UpdateStatus("Saving metadata..."); - - if(image.Info.Cylinders > 0 && - image.Info.Heads > 0 && - image.Info.SectorsPerTrack > 0) - { - sidecar.BlockMedia[0].CylindersSpecified = true; - sidecar.BlockMedia[0].HeadsSpecified = true; - sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; - sidecar.BlockMedia[0].Cylinders = image.Info.Cylinders; - sidecar.BlockMedia[0].Heads = (ushort)image.Info.Heads; - sidecar.BlockMedia[0].SectorsPerTrack = image.Info.SectorsPerTrack; - } - - if(image.Info.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) - { - Identify.IdentifyDevice? ataId = null; - errno = image.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out buffer); - - if(errno == ErrorNumber.NoError) - ataId = Identify.Decode(buffer); - - if(ataId.HasValue) - if(ataId.Value.CurrentCylinders > 0 && - ataId.Value.CurrentHeads > 0 && - ataId.Value.CurrentSectorsPerTrack > 0) - { - sidecar.BlockMedia[0].CylindersSpecified = true; - sidecar.BlockMedia[0].HeadsSpecified = true; - sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; - sidecar.BlockMedia[0].Cylinders = ataId.Value.CurrentCylinders; - sidecar.BlockMedia[0].Heads = ataId.Value.CurrentHeads; - sidecar.BlockMedia[0].SectorsPerTrack = ataId.Value.CurrentSectorsPerTrack; - } - else if(ataId.Value.Cylinders > 0 && - ataId.Value.Heads > 0 && - ataId.Value.SectorsPerTrack > 0) - { - sidecar.BlockMedia[0].CylindersSpecified = true; - sidecar.BlockMedia[0].HeadsSpecified = true; - sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; - sidecar.BlockMedia[0].Cylinders = ataId.Value.Cylinders; - sidecar.BlockMedia[0].Heads = ataId.Value.Heads; - sidecar.BlockMedia[0].SectorsPerTrack = ataId.Value.SectorsPerTrack; - } - } - - if(image.DumpHardware != null) - sidecar.BlockMedia[0].DumpHardwareArray = image.DumpHardware.ToArray(); - - // TODO: This is more of a hack, redo it planned for >4.0 - string trkFormat = null; - - switch(image.Info.MediaType) - { - case CommonTypes.MediaType.Apple32SS: - case CommonTypes.MediaType.Apple32DS: - trkFormat = "Apple GCR (DOS 3.2)"; - - break; - case CommonTypes.MediaType.Apple33SS: - case CommonTypes.MediaType.Apple33DS: - trkFormat = "Apple GCR (DOS 3.3)"; - - break; - case CommonTypes.MediaType.AppleSonySS: - case CommonTypes.MediaType.AppleSonyDS: - trkFormat = "Apple GCR (Sony)"; - - break; - case CommonTypes.MediaType.AppleFileWare: - trkFormat = "Apple GCR (Twiggy)"; - - break; - case CommonTypes.MediaType.DOS_525_SS_DD_9: - case CommonTypes.MediaType.DOS_525_DS_DD_8: - case CommonTypes.MediaType.DOS_525_DS_DD_9: - case CommonTypes.MediaType.DOS_525_HD: - case CommonTypes.MediaType.DOS_35_SS_DD_8: - case CommonTypes.MediaType.DOS_35_SS_DD_9: - case CommonTypes.MediaType.DOS_35_DS_DD_8: - case CommonTypes.MediaType.DOS_35_DS_DD_9: - case CommonTypes.MediaType.DOS_35_HD: - case CommonTypes.MediaType.DOS_35_ED: - case CommonTypes.MediaType.DMF: - case CommonTypes.MediaType.DMF_82: - case CommonTypes.MediaType.XDF_525: - case CommonTypes.MediaType.XDF_35: - case CommonTypes.MediaType.IBM53FD_256: - case CommonTypes.MediaType.IBM53FD_512: - case CommonTypes.MediaType.IBM53FD_1024: - case CommonTypes.MediaType.RX02: - case CommonTypes.MediaType.RX03: - case CommonTypes.MediaType.RX50: - case CommonTypes.MediaType.ACORN_525_SS_DD_40: - case CommonTypes.MediaType.ACORN_525_SS_DD_80: - case CommonTypes.MediaType.ACORN_525_DS_DD: - case CommonTypes.MediaType.ACORN_35_DS_DD: - case CommonTypes.MediaType.ACORN_35_DS_HD: - case CommonTypes.MediaType.ATARI_525_ED: - case CommonTypes.MediaType.ATARI_525_DD: - case CommonTypes.MediaType.ATARI_35_SS_DD: - case CommonTypes.MediaType.ATARI_35_DS_DD: - case CommonTypes.MediaType.ATARI_35_SS_DD_11: - case CommonTypes.MediaType.ATARI_35_DS_DD_11: - case CommonTypes.MediaType.DOS_525_SS_DD_8: - case CommonTypes.MediaType.NEC_8_DD: - case CommonTypes.MediaType.NEC_525_SS: - case CommonTypes.MediaType.NEC_525_DS: - case CommonTypes.MediaType.NEC_525_HD: - case CommonTypes.MediaType.NEC_35_HD_8: - case CommonTypes.MediaType.NEC_35_HD_15: - case CommonTypes.MediaType.NEC_35_TD: - case CommonTypes.MediaType.FDFORMAT_525_DD: - case CommonTypes.MediaType.FDFORMAT_525_HD: - case CommonTypes.MediaType.FDFORMAT_35_DD: - case CommonTypes.MediaType.FDFORMAT_35_HD: - case CommonTypes.MediaType.Apricot_35: - case CommonTypes.MediaType.CompactFloppy: - case CommonTypes.MediaType.MetaFloppy_Mod_I: - case CommonTypes.MediaType.MetaFloppy_Mod_II: - trkFormat = "IBM MFM"; - - break; - case CommonTypes.MediaType.ATARI_525_SD: - case CommonTypes.MediaType.NEC_8_SD: - case CommonTypes.MediaType.ACORN_525_SS_SD_40: - case CommonTypes.MediaType.ACORN_525_SS_SD_80: - case CommonTypes.MediaType.RX01: - case CommonTypes.MediaType.IBM23FD: - case CommonTypes.MediaType.IBM33FD_128: - case CommonTypes.MediaType.IBM33FD_256: - case CommonTypes.MediaType.IBM33FD_512: - case CommonTypes.MediaType.IBM43FD_128: - case CommonTypes.MediaType.IBM43FD_256: - trkFormat = "IBM FM"; - - break; - case CommonTypes.MediaType.CBM_35_DD: - trkFormat = "Commodore MFM"; - - break; - case CommonTypes.MediaType.CBM_AMIGA_35_HD: - case CommonTypes.MediaType.CBM_AMIGA_35_DD: - trkFormat = "Amiga MFM"; - - break; - case CommonTypes.MediaType.CBM_1540: - case CommonTypes.MediaType.CBM_1540_Ext: - case CommonTypes.MediaType.CBM_1571: - trkFormat = "Commodore GCR"; - - break; - case CommonTypes.MediaType.SHARP_525_9: - case CommonTypes.MediaType.SHARP_35_9: break; - case CommonTypes.MediaType.ECMA_99_15: - case CommonTypes.MediaType.ECMA_99_26: - case CommonTypes.MediaType.ECMA_99_8: - trkFormat = "ISO MFM"; - - break; - case CommonTypes.MediaType.ECMA_54: - case CommonTypes.MediaType.ECMA_59: - case CommonTypes.MediaType.ECMA_66: - case CommonTypes.MediaType.ECMA_69_8: - case CommonTypes.MediaType.ECMA_69_15: - case CommonTypes.MediaType.ECMA_69_26: - case CommonTypes.MediaType.ECMA_70: - case CommonTypes.MediaType.ECMA_78: - case CommonTypes.MediaType.ECMA_78_2: - trkFormat = "ISO FM"; - - break; - default: - trkFormat = "Unknown"; - - break; - } - - #region SuperCardPro - string scpFilePath = Path.Combine(Path.GetDirectoryName(imagePath), - Path.GetFileNameWithoutExtension(imagePath) + ".scp"); - + } + else + { if(_aborted) return; - if(File.Exists(scpFilePath)) + sidecar.BlockMedia[0].FileSystemInformation[0] = new PartitionType { - UpdateStatus("Hashing SuperCardPro image..."); - var scpImage = new SuperCardPro(); - var scpFilter = new ZZZNoFilter(); - scpFilter.Open(scpFilePath); + StartSector = 0, + EndSector = image.Info.Sectors - 1 + }; - if(image.Info.Heads <= 2 && - scpImage.Identify(scpFilter)) + var wholePart = new Partition + { + Name = "Whole device", + Length = image.Info.Sectors, + Size = image.Info.Sectors * image.Info.SectorSize + }; + + List lstFs = new(); + + foreach(IFilesystem plugin in plugins.PluginsList.Values) + try { - try + if(_aborted) + return; + + if(!plugin.Identify(image, wholePart)) + continue; + + if(plugin is IReadOnlyFilesystem fsPlugin && + fsPlugin.Mount(image, wholePart, encoding, null, null) == ErrorNumber.NoError) { - scpImage.Open(scpFilter); + UpdateStatus($"Mounting {fsPlugin.XmlFsType.Type}"); + + fsPlugin.XmlFsType.Contents = Files(fsPlugin); + + fsPlugin.Unmount(); } - catch(NotImplementedException) {} + else + plugin.GetInformation(image, wholePart, out _, encoding); - if((image.Info.Heads == 2 && scpImage.Header.heads == 0) || - (image.Info.Heads == 1 && (scpImage.Header.heads == 1 || scpImage.Header.heads == 2))) - if(scpImage.Header.end + 1 >= image.Info.Cylinders) + lstFs.Add(plugin.XmlFsType); + Statistics.AddFilesystem(plugin.XmlFsType.Type); + } + #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body + catch + #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body + { + //AaruConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); + } + + if(lstFs.Count > 0) + sidecar.BlockMedia[0].FileSystemInformation[0].FileSystems = lstFs.ToArray(); + } + + UpdateStatus("Saving metadata..."); + + if(image.Info.Cylinders > 0 && + image.Info.Heads > 0 && + image.Info.SectorsPerTrack > 0) + { + sidecar.BlockMedia[0].CylindersSpecified = true; + sidecar.BlockMedia[0].HeadsSpecified = true; + sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; + sidecar.BlockMedia[0].Cylinders = image.Info.Cylinders; + sidecar.BlockMedia[0].Heads = (ushort)image.Info.Heads; + sidecar.BlockMedia[0].SectorsPerTrack = image.Info.SectorsPerTrack; + } + + if(image.Info.ReadableMediaTags.Contains(MediaTagType.ATA_IDENTIFY)) + { + Identify.IdentifyDevice? ataId = null; + errno = image.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out buffer); + + if(errno == ErrorNumber.NoError) + ataId = Identify.Decode(buffer); + + if(ataId.HasValue) + if(ataId.Value.CurrentCylinders > 0 && + ataId.Value.CurrentHeads > 0 && + ataId.Value.CurrentSectorsPerTrack > 0) + { + sidecar.BlockMedia[0].CylindersSpecified = true; + sidecar.BlockMedia[0].HeadsSpecified = true; + sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; + sidecar.BlockMedia[0].Cylinders = ataId.Value.CurrentCylinders; + sidecar.BlockMedia[0].Heads = ataId.Value.CurrentHeads; + sidecar.BlockMedia[0].SectorsPerTrack = ataId.Value.CurrentSectorsPerTrack; + } + else if(ataId.Value.Cylinders > 0 && + ataId.Value.Heads > 0 && + ataId.Value.SectorsPerTrack > 0) + { + sidecar.BlockMedia[0].CylindersSpecified = true; + sidecar.BlockMedia[0].HeadsSpecified = true; + sidecar.BlockMedia[0].SectorsPerTrackSpecified = true; + sidecar.BlockMedia[0].Cylinders = ataId.Value.Cylinders; + sidecar.BlockMedia[0].Heads = ataId.Value.Heads; + sidecar.BlockMedia[0].SectorsPerTrack = ataId.Value.SectorsPerTrack; + } + } + + if(image.DumpHardware != null) + sidecar.BlockMedia[0].DumpHardwareArray = image.DumpHardware.ToArray(); + + // TODO: This is more of a hack, redo it planned for >4.0 + string trkFormat = null; + + switch(image.Info.MediaType) + { + case CommonTypes.MediaType.Apple32SS: + case CommonTypes.MediaType.Apple32DS: + trkFormat = "Apple GCR (DOS 3.2)"; + + break; + case CommonTypes.MediaType.Apple33SS: + case CommonTypes.MediaType.Apple33DS: + trkFormat = "Apple GCR (DOS 3.3)"; + + break; + case CommonTypes.MediaType.AppleSonySS: + case CommonTypes.MediaType.AppleSonyDS: + trkFormat = "Apple GCR (Sony)"; + + break; + case CommonTypes.MediaType.AppleFileWare: + trkFormat = "Apple GCR (Twiggy)"; + + break; + case CommonTypes.MediaType.DOS_525_SS_DD_9: + case CommonTypes.MediaType.DOS_525_DS_DD_8: + case CommonTypes.MediaType.DOS_525_DS_DD_9: + case CommonTypes.MediaType.DOS_525_HD: + case CommonTypes.MediaType.DOS_35_SS_DD_8: + case CommonTypes.MediaType.DOS_35_SS_DD_9: + case CommonTypes.MediaType.DOS_35_DS_DD_8: + case CommonTypes.MediaType.DOS_35_DS_DD_9: + case CommonTypes.MediaType.DOS_35_HD: + case CommonTypes.MediaType.DOS_35_ED: + case CommonTypes.MediaType.DMF: + case CommonTypes.MediaType.DMF_82: + case CommonTypes.MediaType.XDF_525: + case CommonTypes.MediaType.XDF_35: + case CommonTypes.MediaType.IBM53FD_256: + case CommonTypes.MediaType.IBM53FD_512: + case CommonTypes.MediaType.IBM53FD_1024: + case CommonTypes.MediaType.RX02: + case CommonTypes.MediaType.RX03: + case CommonTypes.MediaType.RX50: + case CommonTypes.MediaType.ACORN_525_SS_DD_40: + case CommonTypes.MediaType.ACORN_525_SS_DD_80: + case CommonTypes.MediaType.ACORN_525_DS_DD: + case CommonTypes.MediaType.ACORN_35_DS_DD: + case CommonTypes.MediaType.ACORN_35_DS_HD: + case CommonTypes.MediaType.ATARI_525_ED: + case CommonTypes.MediaType.ATARI_525_DD: + case CommonTypes.MediaType.ATARI_35_SS_DD: + case CommonTypes.MediaType.ATARI_35_DS_DD: + case CommonTypes.MediaType.ATARI_35_SS_DD_11: + case CommonTypes.MediaType.ATARI_35_DS_DD_11: + case CommonTypes.MediaType.DOS_525_SS_DD_8: + case CommonTypes.MediaType.NEC_8_DD: + case CommonTypes.MediaType.NEC_525_SS: + case CommonTypes.MediaType.NEC_525_DS: + case CommonTypes.MediaType.NEC_525_HD: + case CommonTypes.MediaType.NEC_35_HD_8: + case CommonTypes.MediaType.NEC_35_HD_15: + case CommonTypes.MediaType.NEC_35_TD: + case CommonTypes.MediaType.FDFORMAT_525_DD: + case CommonTypes.MediaType.FDFORMAT_525_HD: + case CommonTypes.MediaType.FDFORMAT_35_DD: + case CommonTypes.MediaType.FDFORMAT_35_HD: + case CommonTypes.MediaType.Apricot_35: + case CommonTypes.MediaType.CompactFloppy: + case CommonTypes.MediaType.MetaFloppy_Mod_I: + case CommonTypes.MediaType.MetaFloppy_Mod_II: + trkFormat = "IBM MFM"; + + break; + case CommonTypes.MediaType.ATARI_525_SD: + case CommonTypes.MediaType.NEC_8_SD: + case CommonTypes.MediaType.ACORN_525_SS_SD_40: + case CommonTypes.MediaType.ACORN_525_SS_SD_80: + case CommonTypes.MediaType.RX01: + case CommonTypes.MediaType.IBM23FD: + case CommonTypes.MediaType.IBM33FD_128: + case CommonTypes.MediaType.IBM33FD_256: + case CommonTypes.MediaType.IBM33FD_512: + case CommonTypes.MediaType.IBM43FD_128: + case CommonTypes.MediaType.IBM43FD_256: + trkFormat = "IBM FM"; + + break; + case CommonTypes.MediaType.CBM_35_DD: + trkFormat = "Commodore MFM"; + + break; + case CommonTypes.MediaType.CBM_AMIGA_35_HD: + case CommonTypes.MediaType.CBM_AMIGA_35_DD: + trkFormat = "Amiga MFM"; + + break; + case CommonTypes.MediaType.CBM_1540: + case CommonTypes.MediaType.CBM_1540_Ext: + case CommonTypes.MediaType.CBM_1571: + trkFormat = "Commodore GCR"; + + break; + case CommonTypes.MediaType.SHARP_525_9: + case CommonTypes.MediaType.SHARP_35_9: break; + case CommonTypes.MediaType.ECMA_99_15: + case CommonTypes.MediaType.ECMA_99_26: + case CommonTypes.MediaType.ECMA_99_8: + trkFormat = "ISO MFM"; + + break; + case CommonTypes.MediaType.ECMA_54: + case CommonTypes.MediaType.ECMA_59: + case CommonTypes.MediaType.ECMA_66: + case CommonTypes.MediaType.ECMA_69_8: + case CommonTypes.MediaType.ECMA_69_15: + case CommonTypes.MediaType.ECMA_69_26: + case CommonTypes.MediaType.ECMA_70: + case CommonTypes.MediaType.ECMA_78: + case CommonTypes.MediaType.ECMA_78_2: + trkFormat = "ISO FM"; + + break; + default: + trkFormat = "Unknown"; + + break; + } + + #region SuperCardPro + string scpFilePath = Path.Combine(Path.GetDirectoryName(imagePath), + Path.GetFileNameWithoutExtension(imagePath) + ".scp"); + + if(_aborted) + return; + + if(File.Exists(scpFilePath)) + { + UpdateStatus("Hashing SuperCardPro image..."); + var scpImage = new SuperCardPro(); + var scpFilter = new ZZZNoFilter(); + scpFilter.Open(scpFilePath); + + if(image.Info.Heads <= 2 && + scpImage.Identify(scpFilter)) + { + try + { + scpImage.Open(scpFilter); + } + catch(NotImplementedException) {} + + if((image.Info.Heads == 2 && scpImage.Header.heads == 0) || + (image.Info.Heads == 1 && (scpImage.Header.heads == 1 || scpImage.Header.heads == 2))) + if(scpImage.Header.end + 1 >= image.Info.Cylinders) + { + List scpBlockTrackTypes = new(); + ulong currentSector = 0; + Stream scpStream = scpFilter.GetDataForkStream(); + + for(byte t = scpImage.Header.start; t <= scpImage.Header.end; t++) { - List scpBlockTrackTypes = new(); - ulong currentSector = 0; - Stream scpStream = scpFilter.GetDataForkStream(); + if(_aborted) + return; - for(byte t = scpImage.Header.start; t <= scpImage.Header.end; t++) + var scpBlockTrackType = new BlockTrackType { - if(_aborted) - return; - - var scpBlockTrackType = new BlockTrackType + Cylinder = t / image.Info.Heads, + Head = (ushort)(t % image.Info.Heads), + Image = new ImageType { - Cylinder = t / image.Info.Heads, - Head = (ushort)(t % image.Info.Heads), - Image = new ImageType - { - format = scpImage.Format, - Value = Path.GetFileName(scpFilePath), - offset = scpImage.Header.offsets[t] - } - }; - - if(scpBlockTrackType.Cylinder < image.Info.Cylinders) - { - scpBlockTrackType.StartSector = currentSector; - currentSector += image.Info.SectorsPerTrack; - scpBlockTrackType.EndSector = currentSector - 1; - scpBlockTrackType.Sectors = image.Info.SectorsPerTrack; - scpBlockTrackType.BytesPerSector = image.Info.SectorSize; - scpBlockTrackType.Format = trkFormat; + format = scpImage.Format, + Value = Path.GetFileName(scpFilePath), + offset = scpImage.Header.offsets[t] } + }; - if(scpImage.ScpTracks.TryGetValue(t, out SuperCardPro.TrackHeader scpTrack)) - { - byte[] trackContents = - new byte[scpTrack.Entries.Last().dataOffset + - scpTrack.Entries.Last().trackLength - scpImage.Header.offsets[t] + 1]; - - scpStream.Position = scpImage.Header.offsets[t]; - scpStream.Read(trackContents, 0, trackContents.Length); - scpBlockTrackType.Size = (ulong)trackContents.Length; - scpBlockTrackType.Checksums = Checksum.GetChecksums(trackContents).ToArray(); - } - - scpBlockTrackTypes.Add(scpBlockTrackType); + if(scpBlockTrackType.Cylinder < image.Info.Cylinders) + { + scpBlockTrackType.StartSector = currentSector; + currentSector += image.Info.SectorsPerTrack; + scpBlockTrackType.EndSector = currentSector - 1; + scpBlockTrackType.Sectors = image.Info.SectorsPerTrack; + scpBlockTrackType.BytesPerSector = image.Info.SectorSize; + scpBlockTrackType.Format = trkFormat; } - sidecar.BlockMedia[0].Track = scpBlockTrackTypes.OrderBy(t => t.Cylinder). - ThenBy(t => t.Head).ToArray(); + if(scpImage.ScpTracks.TryGetValue(t, out SuperCardPro.TrackHeader scpTrack)) + { + byte[] trackContents = + new byte[scpTrack.Entries.Last().dataOffset + + scpTrack.Entries.Last().trackLength - scpImage.Header.offsets[t] + 1]; + + scpStream.Position = scpImage.Header.offsets[t]; + scpStream.Read(trackContents, 0, trackContents.Length); + scpBlockTrackType.Size = (ulong)trackContents.Length; + scpBlockTrackType.Checksums = Checksum.GetChecksums(trackContents).ToArray(); + } + + scpBlockTrackTypes.Add(scpBlockTrackType); } - else - AaruConsole. - ErrorWriteLine("SuperCardPro image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", - scpImage.Header.end + 1, image.Info.Cylinders); + + sidecar.BlockMedia[0].Track = scpBlockTrackTypes.OrderBy(t => t.Cylinder). + ThenBy(t => t.Head).ToArray(); + } else AaruConsole. - ErrorWriteLine("SuperCardPro image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", - 2, image.Info.Heads); - } - } - #endregion - - #region KryoFlux - string kfFile = null; - - string basename = Path.Combine(Path.GetDirectoryName(imagePath), - Path.GetFileNameWithoutExtension(imagePath)); - - bool kfDir = false; - - if(_aborted) - return; - - if(Directory.Exists(basename)) - { - string[] possibleKfStarts = Directory.GetFiles(basename, "*.raw", SearchOption.TopDirectoryOnly); - - if(possibleKfStarts.Length > 0) - { - kfFile = possibleKfStarts[0]; - kfDir = true; - } - } - else if(File.Exists(basename + "00.0.raw")) - kfFile = basename + "00.0.raw"; - else if(File.Exists(basename + "00.1.raw")) - kfFile = basename + "00.1.raw"; - - if(kfFile != null) - { - UpdateStatus("Hashing KryoFlux images..."); - - var kfImage = new KryoFlux(); - var kfFilter = new ZZZNoFilter(); - kfFilter.Open(kfFile); - - if(image.Info.Heads <= 2 && - kfImage.Identify(kfFilter)) - { - try - { - kfImage.Open(kfFilter); - } - catch(NotImplementedException) {} - - if(kfImage.Info.Heads == image.Info.Heads) - if(kfImage.Info.Cylinders >= image.Info.Cylinders) - { - List kfBlockTrackTypes = new(); - - ulong currentSector = 0; - - foreach(KeyValuePair kvp in kfImage.tracks) - { - if(_aborted) - return; - - var kfBlockTrackType = new BlockTrackType - { - Cylinder = kvp.Key / image.Info.Heads, - Head = (ushort)(kvp.Key % image.Info.Heads), - Image = new ImageType - { - format = kfImage.Format, - Value = kfDir - ? Path. - Combine(Path.GetFileName(Path.GetDirectoryName(kvp.Value.BasePath)), - kvp.Value.Filename) : kvp.Value.Filename, - offset = 0 - } - }; - - if(kfBlockTrackType.Cylinder < image.Info.Cylinders) - { - kfBlockTrackType.StartSector = currentSector; - currentSector += image.Info.SectorsPerTrack; - kfBlockTrackType.EndSector = currentSector - 1; - kfBlockTrackType.Sectors = image.Info.SectorsPerTrack; - kfBlockTrackType.BytesPerSector = image.Info.SectorSize; - kfBlockTrackType.Format = trkFormat; - } - - Stream kfStream = kvp.Value.GetDataForkStream(); - byte[] trackContents = new byte[kfStream.Length]; - kfStream.Position = 0; - kfStream.Read(trackContents, 0, trackContents.Length); - kfBlockTrackType.Size = (ulong)trackContents.Length; - kfBlockTrackType.Checksums = Checksum.GetChecksums(trackContents).ToArray(); - - kfBlockTrackTypes.Add(kfBlockTrackType); - } - - sidecar.BlockMedia[0].Track = kfBlockTrackTypes.OrderBy(t => t.Cylinder). - ThenBy(t => t.Head).ToArray(); - } - else - AaruConsole. - ErrorWriteLine("KryoFlux image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", - kfImage.Info.Cylinders, image.Info.Cylinders); - else - AaruConsole. - ErrorWriteLine("KryoFluximage do not contain same number of heads ({0}) than disk image ({1}), ignoring...", - kfImage.Info.Heads, image.Info.Heads); - } - } - #endregion - - #region DiscFerret - string dfiFilePath = Path.Combine(Path.GetDirectoryName(imagePath), - Path.GetFileNameWithoutExtension(imagePath) + ".dfi"); - - if(_aborted) - return; - - if(!File.Exists(dfiFilePath)) - return; - - var dfiImage = new DiscFerret(); - var dfiFilter = new ZZZNoFilter(); - dfiFilter.Open(dfiFilePath); - - if(!dfiImage.Identify(dfiFilter)) - return; - - try - { - dfiImage.Open(dfiFilter); - } - catch(NotImplementedException) {} - - UpdateStatus("Hashing DiscFerret image..."); - - if(image.Info.Heads == dfiImage.Info.Heads) - if(dfiImage.Info.Cylinders >= image.Info.Cylinders) - { - List dfiBlockTrackTypes = new(); - ulong currentSector = 0; - Stream dfiStream = dfiFilter.GetDataForkStream(); - - foreach(int t in dfiImage.TrackOffsets.Keys) - { - if(_aborted) - return; - - var dfiBlockTrackType = new BlockTrackType - { - Cylinder = (uint)(t / image.Info.Heads), - Head = (ushort)(t % image.Info.Heads), - Image = new ImageType - { - format = dfiImage.Format, - Value = Path.GetFileName(dfiFilePath) - } - }; - - if(dfiBlockTrackType.Cylinder < image.Info.Cylinders) - { - dfiBlockTrackType.StartSector = currentSector; - currentSector += image.Info.SectorsPerTrack; - dfiBlockTrackType.EndSector = currentSector - 1; - dfiBlockTrackType.Sectors = image.Info.SectorsPerTrack; - dfiBlockTrackType.BytesPerSector = image.Info.SectorSize; - dfiBlockTrackType.Format = trkFormat; - } - - if(dfiImage.TrackOffsets.TryGetValue(t, out long offset) && - dfiImage.TrackLengths.TryGetValue(t, out long length)) - { - dfiBlockTrackType.Image.offset = (ulong)offset; - byte[] trackContents = new byte[length]; - dfiStream.Position = offset; - dfiStream.Read(trackContents, 0, trackContents.Length); - dfiBlockTrackType.Size = (ulong)trackContents.Length; - dfiBlockTrackType.Checksums = Checksum.GetChecksums(trackContents).ToArray(); - } - - dfiBlockTrackTypes.Add(dfiBlockTrackType); - } - - sidecar.BlockMedia[0].Track = - dfiBlockTrackTypes.OrderBy(t => t.Cylinder).ThenBy(t => t.Head).ToArray(); - } + ErrorWriteLine("SuperCardPro image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", + scpImage.Header.end + 1, image.Info.Cylinders); else AaruConsole. - ErrorWriteLine("DiscFerret image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", - dfiImage.Info.Cylinders, image.Info.Cylinders); + ErrorWriteLine("SuperCardPro image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", + 2, image.Info.Heads); + } + } + #endregion + + #region KryoFlux + string kfFile = null; + + string basename = Path.Combine(Path.GetDirectoryName(imagePath), + Path.GetFileNameWithoutExtension(imagePath)); + + bool kfDir = false; + + if(_aborted) + return; + + if(Directory.Exists(basename)) + { + string[] possibleKfStarts = Directory.GetFiles(basename, "*.raw", SearchOption.TopDirectoryOnly); + + if(possibleKfStarts.Length > 0) + { + kfFile = possibleKfStarts[0]; + kfDir = true; + } + } + else if(File.Exists(basename + "00.0.raw")) + kfFile = basename + "00.0.raw"; + else if(File.Exists(basename + "00.1.raw")) + kfFile = basename + "00.1.raw"; + + if(kfFile != null) + { + UpdateStatus("Hashing KryoFlux images..."); + + var kfImage = new KryoFlux(); + var kfFilter = new ZZZNoFilter(); + kfFilter.Open(kfFile); + + if(image.Info.Heads <= 2 && + kfImage.Identify(kfFilter)) + { + try + { + kfImage.Open(kfFilter); + } + catch(NotImplementedException) {} + + if(kfImage.Info.Heads == image.Info.Heads) + if(kfImage.Info.Cylinders >= image.Info.Cylinders) + { + List kfBlockTrackTypes = new(); + + ulong currentSector = 0; + + foreach(KeyValuePair kvp in kfImage.tracks) + { + if(_aborted) + return; + + var kfBlockTrackType = new BlockTrackType + { + Cylinder = kvp.Key / image.Info.Heads, + Head = (ushort)(kvp.Key % image.Info.Heads), + Image = new ImageType + { + format = kfImage.Format, + Value = kfDir + ? Path. + Combine(Path.GetFileName(Path.GetDirectoryName(kvp.Value.BasePath)), + kvp.Value.Filename) : kvp.Value.Filename, + offset = 0 + } + }; + + if(kfBlockTrackType.Cylinder < image.Info.Cylinders) + { + kfBlockTrackType.StartSector = currentSector; + currentSector += image.Info.SectorsPerTrack; + kfBlockTrackType.EndSector = currentSector - 1; + kfBlockTrackType.Sectors = image.Info.SectorsPerTrack; + kfBlockTrackType.BytesPerSector = image.Info.SectorSize; + kfBlockTrackType.Format = trkFormat; + } + + Stream kfStream = kvp.Value.GetDataForkStream(); + byte[] trackContents = new byte[kfStream.Length]; + kfStream.Position = 0; + kfStream.Read(trackContents, 0, trackContents.Length); + kfBlockTrackType.Size = (ulong)trackContents.Length; + kfBlockTrackType.Checksums = Checksum.GetChecksums(trackContents).ToArray(); + + kfBlockTrackTypes.Add(kfBlockTrackType); + } + + sidecar.BlockMedia[0].Track = kfBlockTrackTypes.OrderBy(t => t.Cylinder). + ThenBy(t => t.Head).ToArray(); + } + else + AaruConsole. + ErrorWriteLine("KryoFlux image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", + kfImage.Info.Cylinders, image.Info.Cylinders); + else + AaruConsole. + ErrorWriteLine("KryoFluximage do not contain same number of heads ({0}) than disk image ({1}), ignoring...", + kfImage.Info.Heads, image.Info.Heads); + } + } + #endregion + + #region DiscFerret + string dfiFilePath = Path.Combine(Path.GetDirectoryName(imagePath), + Path.GetFileNameWithoutExtension(imagePath) + ".dfi"); + + if(_aborted) + return; + + if(!File.Exists(dfiFilePath)) + return; + + var dfiImage = new DiscFerret(); + var dfiFilter = new ZZZNoFilter(); + dfiFilter.Open(dfiFilePath); + + if(!dfiImage.Identify(dfiFilter)) + return; + + try + { + dfiImage.Open(dfiFilter); + } + catch(NotImplementedException) {} + + UpdateStatus("Hashing DiscFerret image..."); + + if(image.Info.Heads == dfiImage.Info.Heads) + if(dfiImage.Info.Cylinders >= image.Info.Cylinders) + { + List dfiBlockTrackTypes = new(); + ulong currentSector = 0; + Stream dfiStream = dfiFilter.GetDataForkStream(); + + foreach(int t in dfiImage.TrackOffsets.Keys) + { + if(_aborted) + return; + + var dfiBlockTrackType = new BlockTrackType + { + Cylinder = (uint)(t / image.Info.Heads), + Head = (ushort)(t % image.Info.Heads), + Image = new ImageType + { + format = dfiImage.Format, + Value = Path.GetFileName(dfiFilePath) + } + }; + + if(dfiBlockTrackType.Cylinder < image.Info.Cylinders) + { + dfiBlockTrackType.StartSector = currentSector; + currentSector += image.Info.SectorsPerTrack; + dfiBlockTrackType.EndSector = currentSector - 1; + dfiBlockTrackType.Sectors = image.Info.SectorsPerTrack; + dfiBlockTrackType.BytesPerSector = image.Info.SectorSize; + dfiBlockTrackType.Format = trkFormat; + } + + if(dfiImage.TrackOffsets.TryGetValue(t, out long offset) && + dfiImage.TrackLengths.TryGetValue(t, out long length)) + { + dfiBlockTrackType.Image.offset = (ulong)offset; + byte[] trackContents = new byte[length]; + dfiStream.Position = offset; + dfiStream.Read(trackContents, 0, trackContents.Length); + dfiBlockTrackType.Size = (ulong)trackContents.Length; + dfiBlockTrackType.Checksums = Checksum.GetChecksums(trackContents).ToArray(); + } + + dfiBlockTrackTypes.Add(dfiBlockTrackType); + } + + sidecar.BlockMedia[0].Track = + dfiBlockTrackTypes.OrderBy(t => t.Cylinder).ThenBy(t => t.Head).ToArray(); + } else AaruConsole. - ErrorWriteLine("DiscFerret image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", - dfiImage.Info.Heads, image.Info.Heads); - #endregion + ErrorWriteLine("DiscFerret image do not contain same number of tracks ({0}) than disk image ({1}), ignoring...", + dfiImage.Info.Cylinders, image.Info.Cylinders); + else + AaruConsole. + ErrorWriteLine("DiscFerret image do not contain same number of heads ({0}) than disk image ({1}), ignoring...", + dfiImage.Info.Heads, image.Info.Heads); + #endregion - // TODO: Implement support for getting CHS from SCSI mode pages - } + // TODO: Implement support for getting CHS from SCSI mode pages } } \ No newline at end of file diff --git a/Aaru.Core/Sidecar/BlockTape.cs b/Aaru.Core/Sidecar/BlockTape.cs index 8a74a57b5..d6872f463 100644 --- a/Aaru.Core/Sidecar/BlockTape.cs +++ b/Aaru.Core/Sidecar/BlockTape.cs @@ -34,190 +34,189 @@ using System.Collections.Generic; using System.IO; using Schemas; -namespace Aaru.Core +namespace Aaru.Core; + +/// Sidecar operations +public sealed partial class Sidecar { - /// Sidecar operations - public sealed partial class Sidecar + /// Creates a metadata sidecar for a block tape (e.g. scsi streaming) + /// List of files + /// Dump path + /// Expected block size in bytes + public CICMMetadataType BlockTape(string folderName, List files, uint blockSize) { - /// Creates a metadata sidecar for a block tape (e.g. scsi streaming) - /// List of files - /// Dump path - /// Expected block size in bytes - public CICMMetadataType BlockTape(string folderName, List files, uint blockSize) + _sidecar = new CICMMetadataType { - _sidecar = new CICMMetadataType + BlockMedia = new[] { - BlockMedia = new[] + new BlockMediaType { - new BlockMediaType + Image = new ImageType { - Image = new ImageType + format = "Directory", + offsetSpecified = false, + Value = folderName + }, + Sequence = new SequenceType + { + MediaTitle = folderName, + MediaSequence = 1, + TotalMedia = 1 + }, + PhysicalBlockSize = blockSize, + LogicalBlockSize = blockSize, + TapeInformation = new[] + { + new TapePartitionType { - format = "Directory", - offsetSpecified = false, - Value = folderName - }, - Sequence = new SequenceType - { - MediaTitle = folderName, - MediaSequence = 1, - TotalMedia = 1 - }, - PhysicalBlockSize = blockSize, - LogicalBlockSize = blockSize, - TapeInformation = new[] - { - new TapePartitionType + Image = new ImageType { - Image = new ImageType - { - format = "Directory", - offsetSpecified = false, - Value = folderName - } + format = "Directory", + offsetSpecified = false, + Value = folderName } } } } - }; + } + }; + if(_aborted) + return _sidecar; + + ulong currentBlock = 0; + ulong totalSize = 0; + var tapeWorker = new Checksum(); + List tapeFiles = new List(); + + UpdateStatus("Hashing files..."); + + for(int i = 0; i < files.Count; i++) + { if(_aborted) return _sidecar; - ulong currentBlock = 0; - ulong totalSize = 0; - var tapeWorker = new Checksum(); - List tapeFiles = new List(); + _fs = new FileStream(files[i], FileMode.Open, FileAccess.Read); + var fileWorker = new Checksum(); - UpdateStatus("Hashing files..."); + var tapeFile = new TapeFileType + { + Image = new ImageType + { + format = "Raw disk image (sector by sector copy)", + offset = 0, + Value = Path.GetFileName(files[i]) + }, + Size = (ulong)_fs.Length, + BlockSize = blockSize, + StartBlock = currentBlock, + Sequence = (ulong)i + }; - for(int i = 0; i < files.Count; i++) + const uint sectorsToRead = 512; + ulong sectors = (ulong)_fs.Length / blockSize; + ulong doneSectors = 0; + + InitProgress2(); + + while(doneSectors < sectors) { if(_aborted) + { + EndProgress2(); + return _sidecar; - - _fs = new FileStream(files[i], FileMode.Open, FileAccess.Read); - var fileWorker = new Checksum(); - - var tapeFile = new TapeFileType - { - Image = new ImageType - { - format = "Raw disk image (sector by sector copy)", - offset = 0, - Value = Path.GetFileName(files[i]) - }, - Size = (ulong)_fs.Length, - BlockSize = blockSize, - StartBlock = currentBlock, - Sequence = (ulong)i - }; - - const uint sectorsToRead = 512; - ulong sectors = (ulong)_fs.Length / blockSize; - ulong doneSectors = 0; - - InitProgress2(); - - while(doneSectors < sectors) - { - if(_aborted) - { - EndProgress2(); - - return _sidecar; - } - - byte[] sector; - - if(sectors - doneSectors >= sectorsToRead) - { - sector = new byte[sectorsToRead * blockSize]; - _fs.Read(sector, 0, sector.Length); - - UpdateProgress2($"Hashing block {doneSectors} of {sectors} on file {i + 1} of {files.Count}", - (long)doneSectors, (long)sectors); - - doneSectors += sectorsToRead; - } - else - { - sector = new byte[(uint)(sectors - doneSectors) * blockSize]; - _fs.Read(sector, 0, sector.Length); - - UpdateProgress2($"Hashing block {doneSectors} of {sectors} on file {i + 1} of {files.Count}", - (long)doneSectors, (long)sectors); - - doneSectors += sectors - doneSectors; - } - - fileWorker.Update(sector); - tapeWorker.Update(sector); } - tapeFile.EndBlock = tapeFile.StartBlock + sectors - 1; - currentBlock += sectors; - totalSize += (ulong)_fs.Length; - tapeFile.Checksums = fileWorker.End().ToArray(); - tapeFiles.Add(tapeFile); + byte[] sector; - EndProgress2(); - } + if(sectors - doneSectors >= sectorsToRead) + { + sector = new byte[sectorsToRead * blockSize]; + _fs.Read(sector, 0, sector.Length); - UpdateStatus("Setting metadata..."); - _sidecar.BlockMedia[0].Checksums = tapeWorker.End().ToArray(); - _sidecar.BlockMedia[0].ContentChecksums = _sidecar.BlockMedia[0].Checksums; - _sidecar.BlockMedia[0].Size = totalSize; - _sidecar.BlockMedia[0].LogicalBlocks = currentBlock; - _sidecar.BlockMedia[0].TapeInformation[0].EndBlock = currentBlock - 1; - _sidecar.BlockMedia[0].TapeInformation[0].Size = totalSize; - _sidecar.BlockMedia[0].TapeInformation[0].Checksums = _sidecar.BlockMedia[0].Checksums; - _sidecar.BlockMedia[0].TapeInformation[0].File = tapeFiles.ToArray(); + UpdateProgress2($"Hashing block {doneSectors} of {sectors} on file {i + 1} of {files.Count}", + (long)doneSectors, (long)sectors); - // This is purely for convenience, as typically these kind of data represents QIC tapes - if(blockSize == 512) - { - _sidecar.BlockMedia[0].DiskType = "Quarter-inch cartridge"; - - if(totalSize <= 20 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-11"; - else if(totalSize <= 40 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-40"; - else if(totalSize <= 60 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-24"; - else if(totalSize <= 80 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-80"; - else if(totalSize <= 120 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-120"; - else if(totalSize <= 150 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-150"; - else if(totalSize <= 320 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-320"; - else if(totalSize <= 340 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-3010"; - else if(totalSize <= 525 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-525"; - else if(totalSize <= 670 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-3020"; - else if(totalSize <= 1200 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-3080"; - else if(totalSize <= 1350 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-1350"; - else if(totalSize <= (long)4000 * 1048576) - _sidecar.BlockMedia[0].DiskSubType = "QIC-3095"; + doneSectors += sectorsToRead; + } else { - _sidecar.BlockMedia[0].DiskType = "Unknown tape"; - _sidecar.BlockMedia[0].DiskSubType = "Unknown tape"; + sector = new byte[(uint)(sectors - doneSectors) * blockSize]; + _fs.Read(sector, 0, sector.Length); + + UpdateProgress2($"Hashing block {doneSectors} of {sectors} on file {i + 1} of {files.Count}", + (long)doneSectors, (long)sectors); + + doneSectors += sectors - doneSectors; } + + fileWorker.Update(sector); + tapeWorker.Update(sector); } + + tapeFile.EndBlock = tapeFile.StartBlock + sectors - 1; + currentBlock += sectors; + totalSize += (ulong)_fs.Length; + tapeFile.Checksums = fileWorker.End().ToArray(); + tapeFiles.Add(tapeFile); + + EndProgress2(); + } + + UpdateStatus("Setting metadata..."); + _sidecar.BlockMedia[0].Checksums = tapeWorker.End().ToArray(); + _sidecar.BlockMedia[0].ContentChecksums = _sidecar.BlockMedia[0].Checksums; + _sidecar.BlockMedia[0].Size = totalSize; + _sidecar.BlockMedia[0].LogicalBlocks = currentBlock; + _sidecar.BlockMedia[0].TapeInformation[0].EndBlock = currentBlock - 1; + _sidecar.BlockMedia[0].TapeInformation[0].Size = totalSize; + _sidecar.BlockMedia[0].TapeInformation[0].Checksums = _sidecar.BlockMedia[0].Checksums; + _sidecar.BlockMedia[0].TapeInformation[0].File = tapeFiles.ToArray(); + + // This is purely for convenience, as typically these kind of data represents QIC tapes + if(blockSize == 512) + { + _sidecar.BlockMedia[0].DiskType = "Quarter-inch cartridge"; + + if(totalSize <= 20 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-11"; + else if(totalSize <= 40 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-40"; + else if(totalSize <= 60 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-24"; + else if(totalSize <= 80 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-80"; + else if(totalSize <= 120 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-120"; + else if(totalSize <= 150 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-150"; + else if(totalSize <= 320 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-320"; + else if(totalSize <= 340 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-3010"; + else if(totalSize <= 525 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-525"; + else if(totalSize <= 670 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-3020"; + else if(totalSize <= 1200 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-3080"; + else if(totalSize <= 1350 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-1350"; + else if(totalSize <= (long)4000 * 1048576) + _sidecar.BlockMedia[0].DiskSubType = "QIC-3095"; else { _sidecar.BlockMedia[0].DiskType = "Unknown tape"; _sidecar.BlockMedia[0].DiskSubType = "Unknown tape"; } - - return _sidecar; } + else + { + _sidecar.BlockMedia[0].DiskType = "Unknown tape"; + _sidecar.BlockMedia[0].DiskSubType = "Unknown tape"; + } + + return _sidecar; } } \ No newline at end of file diff --git a/Aaru.Core/Sidecar/Events.cs b/Aaru.Core/Sidecar/Events.cs index 197874c36..f5d8868ca 100644 --- a/Aaru.Core/Sidecar/Events.cs +++ b/Aaru.Core/Sidecar/Events.cs @@ -32,47 +32,46 @@ using Aaru.CommonTypes; -namespace Aaru.Core +namespace Aaru.Core; + +public sealed partial class Sidecar { - public sealed partial class Sidecar - { - /// Initializes a progress indicator (e.g. makes a progress bar visible) - public event InitProgressHandler InitProgressEvent; - /// Updates a progress indicator with text - public event UpdateProgressHandler UpdateProgressEvent; - /// Uninitializes a progress indicator (e.g. adds a newline to the console) - public event EndProgressHandler EndProgressEvent; - /// Initializes a secondary progress indicator (e.g. makes a progress bar visible) - public event InitProgressHandler2 InitProgressEvent2; - /// Event raised to update the values of a determinate progress bar - public event UpdateProgressHandler2 UpdateProgressEvent2; - /// Event raised when the progress bar is not longer needed - public event EndProgressHandler2 EndProgressEvent2; - /// Updates a status indicator - public event UpdateStatusHandler UpdateStatusEvent; + /// Initializes a progress indicator (e.g. makes a progress bar visible) + public event InitProgressHandler InitProgressEvent; + /// Updates a progress indicator with text + public event UpdateProgressHandler UpdateProgressEvent; + /// Uninitializes a progress indicator (e.g. adds a newline to the console) + public event EndProgressHandler EndProgressEvent; + /// Initializes a secondary progress indicator (e.g. makes a progress bar visible) + public event InitProgressHandler2 InitProgressEvent2; + /// Event raised to update the values of a determinate progress bar + public event UpdateProgressHandler2 UpdateProgressEvent2; + /// Event raised when the progress bar is not longer needed + public event EndProgressHandler2 EndProgressEvent2; + /// Updates a status indicator + public event UpdateStatusHandler UpdateStatusEvent; - /// Initializes a progress indicator (e.g. makes a progress bar visible) - public void InitProgress() => InitProgressEvent?.Invoke(); + /// Initializes a progress indicator (e.g. makes a progress bar visible) + public void InitProgress() => InitProgressEvent?.Invoke(); - /// Updates a progress indicator with text - public void UpdateProgress(string text, long current, long maximum) => - UpdateProgressEvent?.Invoke(string.Format(text, current, maximum), current, maximum); + /// Updates a progress indicator with text + public void UpdateProgress(string text, long current, long maximum) => + UpdateProgressEvent?.Invoke(string.Format(text, current, maximum), current, maximum); - /// Uninitializes a progress indicator (e.g. adds a newline to the console) - public void EndProgress() => EndProgressEvent?.Invoke(); + /// Uninitializes a progress indicator (e.g. adds a newline to the console) + public void EndProgress() => EndProgressEvent?.Invoke(); - /// Initializes a secondary progress indicator (e.g. makes a progress bar visible) - public void InitProgress2() => InitProgressEvent2?.Invoke(); + /// Initializes a secondary progress indicator (e.g. makes a progress bar visible) + public void InitProgress2() => InitProgressEvent2?.Invoke(); - /// Event raised to update the values of a determinate progress bar - public void UpdateProgress2(string text, long current, long maximum) => - UpdateProgressEvent2?.Invoke(string.Format(text, current, maximum), current, maximum); + /// Event raised to update the values of a determinate progress bar + public void UpdateProgress2(string text, long current, long maximum) => + UpdateProgressEvent2?.Invoke(string.Format(text, current, maximum), current, maximum); - /// Event raised when the progress bar is not longer needed - public void EndProgress2() => EndProgressEvent2?.Invoke(); + /// Event raised when the progress bar is not longer needed + public void EndProgress2() => EndProgressEvent2?.Invoke(); - /// Updates a status indicator - public void UpdateStatus(string text, params object[] args) => - UpdateStatusEvent?.Invoke(string.Format(text, args)); - } + /// Updates a status indicator + public void UpdateStatus(string text, params object[] args) => + UpdateStatusEvent?.Invoke(string.Format(text, args)); } \ No newline at end of file diff --git a/Aaru.Core/Sidecar/Files.cs b/Aaru.Core/Sidecar/Files.cs index 22256c4c2..fa91675e1 100644 --- a/Aaru.Core/Sidecar/Files.cs +++ b/Aaru.Core/Sidecar/Files.cs @@ -40,288 +40,287 @@ using Aaru.CommonTypes.Structs; using Aaru.Console; using Schemas; -namespace Aaru.Core +namespace Aaru.Core; + +public sealed partial class Sidecar { - public sealed partial class Sidecar + FilesystemContentsType Files(IReadOnlyFilesystem filesystem) { - FilesystemContentsType Files(IReadOnlyFilesystem filesystem) - { - var contents = new FilesystemContentsType(); + var contents = new FilesystemContentsType(); - ErrorNumber ret = filesystem.ReadDir("/", out List dirents); + ErrorNumber ret = filesystem.ReadDir("/", out List dirents); + + if(ret != ErrorNumber.NoError) + return null; + + List directories = new(); + List files = new(); + + foreach(string dirent in dirents) + { + ret = filesystem.Stat(dirent, out FileEntryInfo stat); if(ret != ErrorNumber.NoError) - return null; - - List directories = new(); - List files = new(); - - foreach(string dirent in dirents) { - ret = filesystem.Stat(dirent, out FileEntryInfo stat); + AaruConsole.DebugWriteLine("Create-Sidecar command", "Cannot stat {0}", dirent); - if(ret != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("Create-Sidecar command", "Cannot stat {0}", dirent); - - continue; - } - - if(stat.Attributes.HasFlag(FileAttributes.Directory)) - { - directories.Add(SidecarDirectory(filesystem, "", dirent, stat)); - - continue; - } - - files.Add(SidecarFile(filesystem, "", dirent, stat)); + continue; } - if(files.Count > 0) - contents.File = files.OrderBy(f => f.name).ToArray(); + if(stat.Attributes.HasFlag(FileAttributes.Directory)) + { + directories.Add(SidecarDirectory(filesystem, "", dirent, stat)); - if(directories.Count > 0) - contents.Directory = directories.OrderBy(d => d.name).ToArray(); + continue; + } - return contents; + files.Add(SidecarFile(filesystem, "", dirent, stat)); } - DirectoryType SidecarDirectory(IReadOnlyFilesystem filesystem, string path, string filename, FileEntryInfo stat) + if(files.Count > 0) + contents.File = files.OrderBy(f => f.name).ToArray(); + + if(directories.Count > 0) + contents.Directory = directories.OrderBy(d => d.name).ToArray(); + + return contents; + } + + DirectoryType SidecarDirectory(IReadOnlyFilesystem filesystem, string path, string filename, FileEntryInfo stat) + { + var directory = new DirectoryType(); + + if(stat.AccessTimeUtc.HasValue) { - var directory = new DirectoryType(); + directory.accessTime = stat.AccessTimeUtc.Value; + directory.accessTimeSpecified = true; + } - if(stat.AccessTimeUtc.HasValue) - { - directory.accessTime = stat.AccessTimeUtc.Value; - directory.accessTimeSpecified = true; - } + directory.attributes = (ulong)stat.Attributes; - directory.attributes = (ulong)stat.Attributes; + if(stat.BackupTimeUtc.HasValue) + { + directory.backupTime = stat.BackupTimeUtc.Value; + directory.backupTimeSpecified = true; + } - if(stat.BackupTimeUtc.HasValue) - { - directory.backupTime = stat.BackupTimeUtc.Value; - directory.backupTimeSpecified = true; - } + if(stat.CreationTimeUtc.HasValue) + { + directory.creationTime = stat.CreationTimeUtc.Value; + directory.creationTimeSpecified = true; + } - if(stat.CreationTimeUtc.HasValue) - { - directory.creationTime = stat.CreationTimeUtc.Value; - directory.creationTimeSpecified = true; - } + if(stat.DeviceNo.HasValue) + { + directory.deviceNumber = stat.DeviceNo.Value; + directory.deviceNumberSpecified = true; + } - if(stat.DeviceNo.HasValue) - { - directory.deviceNumber = stat.DeviceNo.Value; - directory.deviceNumberSpecified = true; - } + directory.inode = stat.Inode; - directory.inode = stat.Inode; + if(stat.LastWriteTimeUtc.HasValue) + { + directory.lastWriteTime = stat.LastWriteTimeUtc.Value; + directory.lastWriteTimeSpecified = true; + } - if(stat.LastWriteTimeUtc.HasValue) - { - directory.lastWriteTime = stat.LastWriteTimeUtc.Value; - directory.lastWriteTimeSpecified = true; - } + directory.links = stat.Links; + directory.name = filename; - directory.links = stat.Links; - directory.name = filename; + if(stat.GID.HasValue) + { + directory.posixGroupId = stat.GID.Value; + directory.posixGroupIdSpecified = true; + } - if(stat.GID.HasValue) - { - directory.posixGroupId = stat.GID.Value; - directory.posixGroupIdSpecified = true; - } + if(stat.Mode.HasValue) + { + directory.posixMode = stat.Mode.Value; + directory.posixModeSpecified = true; + } - if(stat.Mode.HasValue) - { - directory.posixMode = stat.Mode.Value; - directory.posixModeSpecified = true; - } + if(stat.UID.HasValue) + { + directory.posixUserId = stat.UID.Value; + directory.posixUserIdSpecified = true; + } - if(stat.UID.HasValue) - { - directory.posixUserId = stat.UID.Value; - directory.posixUserIdSpecified = true; - } + if(stat.StatusChangeTimeUtc.HasValue) + { + directory.statusChangeTime = stat.StatusChangeTimeUtc.Value; + directory.statusChangeTimeSpecified = true; + } - if(stat.StatusChangeTimeUtc.HasValue) - { - directory.statusChangeTime = stat.StatusChangeTimeUtc.Value; - directory.statusChangeTimeSpecified = true; - } + ErrorNumber ret = filesystem.ReadDir(path + "/" + filename, out List dirents); - ErrorNumber ret = filesystem.ReadDir(path + "/" + filename, out List dirents); + if(ret != ErrorNumber.NoError) + return null; + + List directories = new(); + List files = new(); + + foreach(string dirent in dirents) + { + ret = filesystem.Stat(path + "/" + filename + "/" + dirent, out FileEntryInfo entryStat); if(ret != ErrorNumber.NoError) - return null; - - List directories = new(); - List files = new(); - - foreach(string dirent in dirents) { - ret = filesystem.Stat(path + "/" + filename + "/" + dirent, out FileEntryInfo entryStat); + AaruConsole.DebugWriteLine("Create-Sidecar command", "Cannot stat {0}", dirent); - if(ret != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("Create-Sidecar command", "Cannot stat {0}", dirent); - - continue; - } - - if(entryStat.Attributes.HasFlag(FileAttributes.Directory)) - { - directories.Add(SidecarDirectory(filesystem, path + "/" + filename, dirent, entryStat)); - - continue; - } - - files.Add(SidecarFile(filesystem, path + "/" + filename, dirent, entryStat)); + continue; } - if(files.Count > 0) - directory.File = files.OrderBy(f => f.name).ToArray(); + if(entryStat.Attributes.HasFlag(FileAttributes.Directory)) + { + directories.Add(SidecarDirectory(filesystem, path + "/" + filename, dirent, entryStat)); - if(directories.Count > 0) - directory.Directory = directories.OrderBy(d => d.name).ToArray(); + continue; + } - return directory; + files.Add(SidecarFile(filesystem, path + "/" + filename, dirent, entryStat)); } - ContentsFileType SidecarFile(IReadOnlyFilesystem filesystem, string path, string filename, FileEntryInfo stat) + if(files.Count > 0) + directory.File = files.OrderBy(f => f.name).ToArray(); + + if(directories.Count > 0) + directory.Directory = directories.OrderBy(d => d.name).ToArray(); + + return directory; + } + + ContentsFileType SidecarFile(IReadOnlyFilesystem filesystem, string path, string filename, FileEntryInfo stat) + { + var file = new ContentsFileType(); + var fileChkWorker = new Checksum(); + + if(stat.AccessTimeUtc.HasValue) { - var file = new ContentsFileType(); - var fileChkWorker = new Checksum(); + file.accessTime = stat.AccessTimeUtc.Value; + file.accessTimeSpecified = true; + } - if(stat.AccessTimeUtc.HasValue) + file.attributes = (ulong)stat.Attributes; + + if(stat.BackupTimeUtc.HasValue) + { + file.backupTime = stat.BackupTimeUtc.Value; + file.backupTimeSpecified = true; + } + + if(stat.CreationTimeUtc.HasValue) + { + file.creationTime = stat.CreationTimeUtc.Value; + file.creationTimeSpecified = true; + } + + if(stat.DeviceNo.HasValue) + { + file.deviceNumber = stat.DeviceNo.Value; + file.deviceNumberSpecified = true; + } + + file.inode = stat.Inode; + + if(stat.LastWriteTimeUtc.HasValue) + { + file.lastWriteTime = stat.LastWriteTimeUtc.Value; + file.lastWriteTimeSpecified = true; + } + + file.length = (ulong)stat.Length; + file.links = stat.Links; + file.name = filename; + + if(stat.GID.HasValue) + { + file.posixGroupId = stat.GID.Value; + file.posixGroupIdSpecified = true; + } + + if(stat.Mode.HasValue) + { + file.posixMode = stat.Mode.Value; + file.posixModeSpecified = true; + } + + if(stat.UID.HasValue) + { + file.posixUserId = stat.UID.Value; + file.posixUserIdSpecified = true; + } + + if(stat.StatusChangeTimeUtc.HasValue) + { + file.statusChangeTime = stat.StatusChangeTimeUtc.Value; + file.statusChangeTimeSpecified = true; + } + + byte[] data = Array.Empty(); + + if(stat.Length > 0) + { + long position = 0; + UpdateStatus($"Hashing file {path}/{filename}..."); + InitProgress2(); + + while(position < stat.Length - 1048576) { - file.accessTime = stat.AccessTimeUtc.Value; - file.accessTimeSpecified = true; - } + if(_aborted) + return file; - file.attributes = (ulong)stat.Attributes; - - if(stat.BackupTimeUtc.HasValue) - { - file.backupTime = stat.BackupTimeUtc.Value; - file.backupTimeSpecified = true; - } - - if(stat.CreationTimeUtc.HasValue) - { - file.creationTime = stat.CreationTimeUtc.Value; - file.creationTimeSpecified = true; - } - - if(stat.DeviceNo.HasValue) - { - file.deviceNumber = stat.DeviceNo.Value; - file.deviceNumberSpecified = true; - } - - file.inode = stat.Inode; - - if(stat.LastWriteTimeUtc.HasValue) - { - file.lastWriteTime = stat.LastWriteTimeUtc.Value; - file.lastWriteTimeSpecified = true; - } - - file.length = (ulong)stat.Length; - file.links = stat.Links; - file.name = filename; - - if(stat.GID.HasValue) - { - file.posixGroupId = stat.GID.Value; - file.posixGroupIdSpecified = true; - } - - if(stat.Mode.HasValue) - { - file.posixMode = stat.Mode.Value; - file.posixModeSpecified = true; - } - - if(stat.UID.HasValue) - { - file.posixUserId = stat.UID.Value; - file.posixUserIdSpecified = true; - } - - if(stat.StatusChangeTimeUtc.HasValue) - { - file.statusChangeTime = stat.StatusChangeTimeUtc.Value; - file.statusChangeTimeSpecified = true; - } - - byte[] data = Array.Empty(); - - if(stat.Length > 0) - { - long position = 0; - UpdateStatus($"Hashing file {path}/{filename}..."); - InitProgress2(); - - while(position < stat.Length - 1048576) - { - if(_aborted) - return file; - - data = new byte[1048576]; - filesystem.Read(path + "/" + filename, position, 1048576, ref data); - - UpdateProgress2("Hashing file byte {0} of {1}", position, stat.Length); - - fileChkWorker.Update(data); - - position += 1048576; - } - - data = new byte[stat.Length - position]; - filesystem.Read(path + "/" + filename, position, stat.Length - position, ref data); + data = new byte[1048576]; + filesystem.Read(path + "/" + filename, position, 1048576, ref data); UpdateProgress2("Hashing file byte {0} of {1}", position, stat.Length); fileChkWorker.Update(data); - EndProgress2(); - - file.Checksums = fileChkWorker.End().ToArray(); + position += 1048576; } - else - file.Checksums = _emptyChecksums; - ErrorNumber ret = filesystem.ListXAttr(path + "/" + filename, out List xattrs); + data = new byte[stat.Length - position]; + filesystem.Read(path + "/" + filename, position, stat.Length - position, ref data); + + UpdateProgress2("Hashing file byte {0} of {1}", position, stat.Length); + + fileChkWorker.Update(data); + + EndProgress2(); + + file.Checksums = fileChkWorker.End().ToArray(); + } + else + file.Checksums = _emptyChecksums; + + ErrorNumber ret = filesystem.ListXAttr(path + "/" + filename, out List xattrs); + + if(ret != ErrorNumber.NoError) + return file; + + List xattrTypes = new(); + + foreach(string xattr in xattrs) + { + ret = filesystem.GetXattr(path + "/" + filename, xattr, ref data); if(ret != ErrorNumber.NoError) - return file; + continue; - List xattrTypes = new(); + var xattrChkWorker = new Checksum(); + xattrChkWorker.Update(data); - foreach(string xattr in xattrs) + xattrTypes.Add(new ExtendedAttributeType { - ret = filesystem.GetXattr(path + "/" + filename, xattr, ref data); - - if(ret != ErrorNumber.NoError) - continue; - - var xattrChkWorker = new Checksum(); - xattrChkWorker.Update(data); - - xattrTypes.Add(new ExtendedAttributeType - { - Checksums = xattrChkWorker.End().ToArray(), - length = (ulong)data.Length, - name = xattr - }); - } - - if(xattrTypes.Count > 0) - file.ExtendedAttributes = xattrTypes.OrderBy(x => x.name).ToArray(); - - return file; + Checksums = xattrChkWorker.End().ToArray(), + length = (ulong)data.Length, + name = xattr + }); } + + if(xattrTypes.Count > 0) + file.ExtendedAttributes = xattrTypes.OrderBy(x => x.name).ToArray(); + + return file; } } \ No newline at end of file diff --git a/Aaru.Core/Sidecar/Helpers.cs b/Aaru.Core/Sidecar/Helpers.cs index 3d18e273c..8060390e8 100644 --- a/Aaru.Core/Sidecar/Helpers.cs +++ b/Aaru.Core/Sidecar/Helpers.cs @@ -30,66 +30,65 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Core +namespace Aaru.Core; + +public sealed partial class Sidecar { - public sealed partial class Sidecar + /// Converts a LBA to MM:SS:FF string for CDs + /// LBA + /// MM:SS:FF + static string LbaToMsf(long lba) { - /// Converts a LBA to MM:SS:FF string for CDs - /// LBA - /// MM:SS:FF - static string LbaToMsf(long lba) + long m, s, f; + + if(lba >= -150) { - long m, s, f; - - if(lba >= -150) - { - m = (lba + 150) / (75 * 60); - lba -= m * (75 * 60); - s = (lba + 150) / 75; - lba -= s * 75; - f = lba + 150; - } - else - { - m = (lba + 450150) / (75 * 60); - lba -= m * (75 * 60); - s = (lba + 450150) / 75; - lba -= s * 75; - f = lba + 450150; - } - - return $"{m}:{s:D2}:{f:D2}"; + m = (lba + 150) / (75 * 60); + lba -= m * (75 * 60); + s = (lba + 150) / 75; + lba -= s * 75; + f = lba + 150; + } + else + { + m = (lba + 450150) / (75 * 60); + lba -= m * (75 * 60); + s = (lba + 450150) / 75; + lba -= s * 75; + f = lba + 450150; } - /// Converts a LBA to MM:SS:FF string for DDCDs - /// LBA - /// MM:SS:FF - static string DdcdLbaToMsf(long lba) + return $"{m}:{s:D2}:{f:D2}"; + } + + /// Converts a LBA to MM:SS:FF string for DDCDs + /// LBA + /// MM:SS:FF + static string DdcdLbaToMsf(long lba) + { + long h, m, s, f; + + if(lba >= -150) { - long h, m, s, f; - - if(lba >= -150) - { - h = (lba + 150) / (75 * 60 * 60); - lba -= h * (75 * 60 * 60); - m = (lba + 150) / (75 * 60); - lba -= m * (75 * 60); - s = (lba + 150) / 75; - lba -= s * 75; - f = lba + 150; - } - else - { - h = (lba + (450150 * 2)) / (75 * 60 * 60); - lba -= h * (75 * 60 * 60); - m = (lba + (450150 * 2)) / (75 * 60); - lba -= m * (75 * 60); - s = (lba + (450150 * 2)) / 75; - lba -= s * 75; - f = lba + (450150 * 2); - } - - return string.Format("{3}:{0:D2}:{1:D2}:{2:D2}", m, s, f, h); + h = (lba + 150) / (75 * 60 * 60); + lba -= h * (75 * 60 * 60); + m = (lba + 150) / (75 * 60); + lba -= m * (75 * 60); + s = (lba + 150) / 75; + lba -= s * 75; + f = lba + 150; } + else + { + h = (lba + (450150 * 2)) / (75 * 60 * 60); + lba -= h * (75 * 60 * 60); + m = (lba + (450150 * 2)) / (75 * 60); + lba -= m * (75 * 60); + s = (lba + (450150 * 2)) / 75; + lba -= s * 75; + f = lba + (450150 * 2); + } + + return string.Format("{3}:{0:D2}:{1:D2}:{2:D2}", m, s, f, h); } } \ No newline at end of file diff --git a/Aaru.Core/Sidecar/OpticalDisc.cs b/Aaru.Core/Sidecar/OpticalDisc.cs index 91a030311..67658d13d 100644 --- a/Aaru.Core/Sidecar/OpticalDisc.cs +++ b/Aaru.Core/Sidecar/OpticalDisc.cs @@ -49,648 +49,581 @@ using MediaType = Aaru.CommonTypes.MediaType; using Session = Aaru.CommonTypes.Structs.Session; using TrackType = Schemas.TrackType; -namespace Aaru.Core +namespace Aaru.Core; + +public sealed partial class Sidecar { - public sealed partial class Sidecar + /// Creates a metadata sidecar for an optical disc (e.g. CD, DVD, GD, BD, XGD, GOD) + /// Image + /// Filter uuid + /// Image path + /// Image file information + /// Image plugins + /// List of image checksums + /// Metadata sidecar + /// Encoding to be used for filesystem plugins + void OpticalDisc(IOpticalMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins, + List imgChecksums, ref CICMMetadataType sidecar, Encoding encoding) { - /// Creates a metadata sidecar for an optical disc (e.g. CD, DVD, GD, BD, XGD, GOD) - /// Image - /// Filter uuid - /// Image path - /// Image file information - /// Image plugins - /// List of image checksums - /// Metadata sidecar - /// Encoding to be used for filesystem plugins - void OpticalDisc(IOpticalMediaImage image, Guid filterId, string imagePath, FileInfo fi, PluginBase plugins, - List imgChecksums, ref CICMMetadataType sidecar, Encoding encoding) + if(_aborted) + return; + + sidecar.OpticalDisc = new[] + { + new OpticalDiscType + { + Checksums = imgChecksums.ToArray(), + Image = new ImageType + { + format = image.Format, + offset = 0, + offsetSpecified = true, + Value = Path.GetFileName(imagePath) + }, + Size = (ulong)fi.Length, + Sequence = new SequenceType + { + MediaTitle = image.Info.MediaTitle + } + } + }; + + if(image.Info.MediaSequence != 0 && + image.Info.LastMediaSequence != 0) + { + sidecar.OpticalDisc[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence; + sidecar.OpticalDisc[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence; + } + else + { + sidecar.OpticalDisc[0].Sequence.MediaSequence = 1; + sidecar.OpticalDisc[0].Sequence.TotalMedia = 1; + } + + MediaType dskType = image.Info.MediaType; + ErrorNumber errno; + + UpdateStatus("Hashing media tags..."); + + foreach(MediaTagType tagType in image.Info.ReadableMediaTags) { if(_aborted) return; - sidecar.OpticalDisc = new[] + errno = image.ReadMediaTag(tagType, out byte[] tag); + + if(errno != ErrorNumber.NoError) + continue; + + Dump.AddMediaTagToSidecar(imagePath, tagType, tag, ref sidecar); + + switch(tagType) { - new OpticalDiscType - { - Checksums = imgChecksums.ToArray(), - Image = new ImageType + case MediaTagType.CD_ATIP: + ATIP.CDATIP atip = ATIP.Decode(tag); + + if(atip != null) + if(atip.DDCD) + dskType = atip.DiscType ? MediaType.DDCDRW : MediaType.DDCDR; + else + dskType = atip.DiscType ? MediaType.CDRW : MediaType.CDR; + + break; + case MediaTagType.DVD_DMI: + if(DMI.IsXbox(tag)) { - format = image.Format, - offset = 0, - offsetSpecified = true, - Value = Path.GetFileName(imagePath) - }, - Size = (ulong)fi.Length, - Sequence = new SequenceType - { - MediaTitle = image.Info.MediaTitle + dskType = MediaType.XGD; + + sidecar.OpticalDisc[0].Dimensions = new DimensionsType + { + Diameter = 120, + Thickness = 1.2 + }; } - } - }; + else if(DMI.IsXbox360(tag)) + { + dskType = MediaType.XGD2; - if(image.Info.MediaSequence != 0 && - image.Info.LastMediaSequence != 0) - { - sidecar.OpticalDisc[0].Sequence.MediaSequence = (uint)image.Info.MediaSequence; - sidecar.OpticalDisc[0].Sequence.TotalMedia = (uint)image.Info.LastMediaSequence; - } - else - { - sidecar.OpticalDisc[0].Sequence.MediaSequence = 1; - sidecar.OpticalDisc[0].Sequence.TotalMedia = 1; - } - - MediaType dskType = image.Info.MediaType; - ErrorNumber errno; - - UpdateStatus("Hashing media tags..."); - - foreach(MediaTagType tagType in image.Info.ReadableMediaTags) - { - if(_aborted) - return; - - errno = image.ReadMediaTag(tagType, out byte[] tag); - - if(errno != ErrorNumber.NoError) - continue; - - Dump.AddMediaTagToSidecar(imagePath, tagType, tag, ref sidecar); - - switch(tagType) - { - case MediaTagType.CD_ATIP: - ATIP.CDATIP atip = ATIP.Decode(tag); - - if(atip != null) - if(atip.DDCD) - dskType = atip.DiscType ? MediaType.DDCDRW : MediaType.DDCDR; - else - dskType = atip.DiscType ? MediaType.CDRW : MediaType.CDR; - - break; - case MediaTagType.DVD_DMI: - if(DMI.IsXbox(tag)) + sidecar.OpticalDisc[0].Dimensions = new DimensionsType { - dskType = MediaType.XGD; + Diameter = 120, + Thickness = 1.2 + }; + } - sidecar.OpticalDisc[0].Dimensions = new DimensionsType - { - Diameter = 120, - Thickness = 1.2 - }; - } - else if(DMI.IsXbox360(tag)) + break; + case MediaTagType.DVD_PFI: + PFI.PhysicalFormatInformation? pfi = PFI.Decode(tag, dskType); + + if(pfi.HasValue) + if(dskType != MediaType.XGD && + dskType != MediaType.XGD2 && + dskType != MediaType.XGD3 && + dskType != MediaType.PS2DVD && + dskType != MediaType.PS3DVD && + dskType != MediaType.Nuon) { - dskType = MediaType.XGD2; - - sidecar.OpticalDisc[0].Dimensions = new DimensionsType + switch(pfi.Value.DiskCategory) { - Diameter = 120, - Thickness = 1.2 - }; - } + case DiskCategory.DVDPR: + dskType = MediaType.DVDPR; - break; - case MediaTagType.DVD_PFI: - PFI.PhysicalFormatInformation? pfi = PFI.Decode(tag, dskType); + break; + case DiskCategory.DVDPRDL: + dskType = MediaType.DVDPRDL; - if(pfi.HasValue) - if(dskType != MediaType.XGD && - dskType != MediaType.XGD2 && - dskType != MediaType.XGD3 && - dskType != MediaType.PS2DVD && - dskType != MediaType.PS3DVD && - dskType != MediaType.Nuon) - { - switch(pfi.Value.DiskCategory) - { - case DiskCategory.DVDPR: - dskType = MediaType.DVDPR; + break; + case DiskCategory.DVDPRW: + dskType = MediaType.DVDPRW; - break; - case DiskCategory.DVDPRDL: - dskType = MediaType.DVDPRDL; + break; + case DiskCategory.DVDPRWDL: + dskType = MediaType.DVDPRWDL; - break; - case DiskCategory.DVDPRW: - dskType = MediaType.DVDPRW; + break; + case DiskCategory.DVDR: + dskType = MediaType.DVDR; - break; - case DiskCategory.DVDPRWDL: - dskType = MediaType.DVDPRWDL; + break; + case DiskCategory.DVDRAM: + dskType = MediaType.DVDRAM; - break; - case DiskCategory.DVDR: - dskType = MediaType.DVDR; + break; + case DiskCategory.DVDROM: + dskType = MediaType.DVDROM; - break; - case DiskCategory.DVDRAM: - dskType = MediaType.DVDRAM; + break; + case DiskCategory.DVDRW: + dskType = MediaType.DVDRW; - break; - case DiskCategory.DVDROM: - dskType = MediaType.DVDROM; + break; + case DiskCategory.HDDVDR: + dskType = MediaType.HDDVDR; - break; - case DiskCategory.DVDRW: - dskType = MediaType.DVDRW; + break; + case DiskCategory.HDDVDRAM: + dskType = MediaType.HDDVDRAM; - break; - case DiskCategory.HDDVDR: - dskType = MediaType.HDDVDR; + break; + case DiskCategory.HDDVDROM: + dskType = MediaType.HDDVDROM; - break; - case DiskCategory.HDDVDRAM: - dskType = MediaType.HDDVDRAM; + break; + case DiskCategory.HDDVDRW: + dskType = MediaType.HDDVDRW; - break; - case DiskCategory.HDDVDROM: - dskType = MediaType.HDDVDROM; + break; + case DiskCategory.Nintendo: + dskType = MediaType.GOD; - break; - case DiskCategory.HDDVDRW: - dskType = MediaType.HDDVDRW; + break; + case DiskCategory.UMD: + dskType = MediaType.UMD; - break; - case DiskCategory.Nintendo: - dskType = MediaType.GOD; - - break; - case DiskCategory.UMD: - dskType = MediaType.UMD; - - break; - } - - if(dskType == MediaType.DVDR && - pfi.Value.PartVersion >= 6) - dskType = MediaType.DVDRDL; - - if(dskType == MediaType.DVDRW && - pfi.Value.PartVersion >= 15) - dskType = MediaType.DVDRWDL; - - if(dskType == MediaType.GOD && - pfi.Value.DiscSize == DVDSize.OneTwenty) - dskType = MediaType.WOD; - - sidecar.OpticalDisc[0].Dimensions = new DimensionsType(); - - if(dskType == MediaType.UMD) - { - sidecar.OpticalDisc[0].Dimensions.Height = 64; - sidecar.OpticalDisc[0].Dimensions.HeightSpecified = true; - sidecar.OpticalDisc[0].Dimensions.Width = 63; - sidecar.OpticalDisc[0].Dimensions.WidthSpecified = true; - sidecar.OpticalDisc[0].Dimensions.Thickness = 4; - } - else - switch(pfi.Value.DiscSize) - { - case DVDSize.Eighty: - sidecar.OpticalDisc[0].Dimensions.Diameter = 80; - sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true; - sidecar.OpticalDisc[0].Dimensions.Thickness = 1.2; - - break; - case DVDSize.OneTwenty: - sidecar.OpticalDisc[0].Dimensions.Diameter = 120; - sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true; - sidecar.OpticalDisc[0].Dimensions.Thickness = 1.2; - - break; - } + break; } - break; - } - } + if(dskType == MediaType.DVDR && + pfi.Value.PartVersion >= 6) + dskType = MediaType.DVDRDL; - try - { - List sessions = image.Sessions; - sidecar.OpticalDisc[0].Sessions = (uint)(sessions?.Count ?? 1); - } - catch - { - sidecar.OpticalDisc[0].Sessions = 1; - } + if(dskType == MediaType.DVDRW && + pfi.Value.PartVersion >= 15) + dskType = MediaType.DVDRWDL; - List tracks = image.Tracks; - List trksLst = null; + if(dskType == MediaType.GOD && + pfi.Value.DiscSize == DVDSize.OneTwenty) + dskType = MediaType.WOD; - if(tracks != null) - { - sidecar.OpticalDisc[0].Tracks = new uint[1]; - sidecar.OpticalDisc[0].Tracks[0] = (uint)tracks.Count; - trksLst = new List(); - } + sidecar.OpticalDisc[0].Dimensions = new DimensionsType(); - if(sidecar.OpticalDisc[0].Dimensions == null && - image.Info.MediaType != MediaType.Unknown) - sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType); + if(dskType == MediaType.UMD) + { + sidecar.OpticalDisc[0].Dimensions.Height = 64; + sidecar.OpticalDisc[0].Dimensions.HeightSpecified = true; + sidecar.OpticalDisc[0].Dimensions.Width = 63; + sidecar.OpticalDisc[0].Dimensions.WidthSpecified = true; + sidecar.OpticalDisc[0].Dimensions.Thickness = 4; + } + else + switch(pfi.Value.DiscSize) + { + case DVDSize.Eighty: + sidecar.OpticalDisc[0].Dimensions.Diameter = 80; + sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true; + sidecar.OpticalDisc[0].Dimensions.Thickness = 1.2; - if(_aborted) - return; + break; + case DVDSize.OneTwenty: + sidecar.OpticalDisc[0].Dimensions.Diameter = 120; + sidecar.OpticalDisc[0].Dimensions.DiameterSpecified = true; + sidecar.OpticalDisc[0].Dimensions.Thickness = 1.2; - InitProgress(); - - UpdateStatus("Checking filesystems"); - List partitions = Partitions.GetAll(image); - Partitions.AddSchemesToStats(partitions); - - UpdateStatus("Hashing tracks..."); - - foreach(Track trk in tracks) - { - if(_aborted) - { - EndProgress(); - - return; - } - - var xmlTrk = new TrackType(); - - switch(trk.Type) - { - case CommonTypes.Enums.TrackType.Audio: - xmlTrk.TrackType1 = TrackTypeTrackType.audio; - - break; - case CommonTypes.Enums.TrackType.CdMode2Form2: - xmlTrk.TrackType1 = TrackTypeTrackType.m2f2; - - break; - case CommonTypes.Enums.TrackType.CdMode2Formless: - xmlTrk.TrackType1 = TrackTypeTrackType.mode2; - - break; - case CommonTypes.Enums.TrackType.CdMode2Form1: - xmlTrk.TrackType1 = TrackTypeTrackType.m2f1; - - break; - case CommonTypes.Enums.TrackType.CdMode1: - xmlTrk.TrackType1 = TrackTypeTrackType.mode1; - - break; - case CommonTypes.Enums.TrackType.Data: - switch(sidecar.OpticalDisc[0].DiscType) - { - case "BD": - xmlTrk.TrackType1 = TrackTypeTrackType.bluray; - - break; - case "DDCD": - xmlTrk.TrackType1 = TrackTypeTrackType.ddcd; - - break; - case "DVD": - xmlTrk.TrackType1 = TrackTypeTrackType.dvd; - - break; - case "HD DVD": - xmlTrk.TrackType1 = TrackTypeTrackType.hddvd; - - break; - default: - xmlTrk.TrackType1 = TrackTypeTrackType.mode1; - - break; + break; + } } - break; + break; + } + } + + try + { + List sessions = image.Sessions; + sidecar.OpticalDisc[0].Sessions = (uint)(sessions?.Count ?? 1); + } + catch + { + sidecar.OpticalDisc[0].Sessions = 1; + } + + List tracks = image.Tracks; + List trksLst = null; + + if(tracks != null) + { + sidecar.OpticalDisc[0].Tracks = new uint[1]; + sidecar.OpticalDisc[0].Tracks[0] = (uint)tracks.Count; + trksLst = new List(); + } + + if(sidecar.OpticalDisc[0].Dimensions == null && + image.Info.MediaType != MediaType.Unknown) + sidecar.OpticalDisc[0].Dimensions = Dimensions.DimensionsFromMediaType(image.Info.MediaType); + + if(_aborted) + return; + + InitProgress(); + + UpdateStatus("Checking filesystems"); + List partitions = Partitions.GetAll(image); + Partitions.AddSchemesToStats(partitions); + + UpdateStatus("Hashing tracks..."); + + foreach(Track trk in tracks) + { + if(_aborted) + { + EndProgress(); + + return; + } + + var xmlTrk = new TrackType(); + + switch(trk.Type) + { + case CommonTypes.Enums.TrackType.Audio: + xmlTrk.TrackType1 = TrackTypeTrackType.audio; + + break; + case CommonTypes.Enums.TrackType.CdMode2Form2: + xmlTrk.TrackType1 = TrackTypeTrackType.m2f2; + + break; + case CommonTypes.Enums.TrackType.CdMode2Formless: + xmlTrk.TrackType1 = TrackTypeTrackType.mode2; + + break; + case CommonTypes.Enums.TrackType.CdMode2Form1: + xmlTrk.TrackType1 = TrackTypeTrackType.m2f1; + + break; + case CommonTypes.Enums.TrackType.CdMode1: + xmlTrk.TrackType1 = TrackTypeTrackType.mode1; + + break; + case CommonTypes.Enums.TrackType.Data: + switch(sidecar.OpticalDisc[0].DiscType) + { + case "BD": + xmlTrk.TrackType1 = TrackTypeTrackType.bluray; + + break; + case "DDCD": + xmlTrk.TrackType1 = TrackTypeTrackType.ddcd; + + break; + case "DVD": + xmlTrk.TrackType1 = TrackTypeTrackType.dvd; + + break; + case "HD DVD": + xmlTrk.TrackType1 = TrackTypeTrackType.hddvd; + + break; + default: + xmlTrk.TrackType1 = TrackTypeTrackType.mode1; + + break; + } + + break; + } + + xmlTrk.Sequence = new TrackSequenceType + { + Session = trk.Session, + TrackNumber = trk.Sequence + }; + + xmlTrk.StartSector = trk.StartSector; + xmlTrk.EndSector = trk.EndSector; + + int idx0 = -1; + + if(trk.Indexes?.TryGetValue(0, out idx0) == true && + idx0 >= 0) + xmlTrk.StartSector = (ulong)idx0; + + switch(sidecar.OpticalDisc[0].DiscType) + { + case "CD": + case "GD": + xmlTrk.StartMSF = LbaToMsf((long)xmlTrk.StartSector); + xmlTrk.EndMSF = LbaToMsf((long)xmlTrk.EndSector); + + break; + case "DDCD": + xmlTrk.StartMSF = DdcdLbaToMsf((long)xmlTrk.StartSector); + xmlTrk.EndMSF = DdcdLbaToMsf((long)xmlTrk.EndSector); + + break; + } + + xmlTrk.Image = new ImageType + { + Value = Path.GetFileName(trk.File), + format = trk.FileType + }; + + if(trk.FileOffset > 0) + { + xmlTrk.Image.offset = trk.FileOffset; + xmlTrk.Image.offsetSpecified = true; + } + + xmlTrk.Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * (ulong)trk.RawBytesPerSector; + + xmlTrk.BytesPerSector = (uint)trk.BytesPerSector; + + uint sectorsToRead = 512; + ulong sectors = xmlTrk.EndSector - xmlTrk.StartSector + 1; + ulong doneSectors = 0; + + // If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum. + if(image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") && + + // Only if filter is none... + (filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") || + + // ...or AppleDouble + filterId == new Guid("1b2165ee-c9df-4b21-bbbb-9e5892b2df4d"))) + xmlTrk.Checksums = sidecar.OpticalDisc[0].Checksums; + else + { + UpdateProgress("Track {0} of {1}", trk.Sequence, tracks.Count); + + // For fast debugging, skip checksum + //goto skipChecksum; + + var trkChkWorker = new Checksum(); + + InitProgress2(); + + while(doneSectors < sectors) + { + if(_aborted) + { + EndProgress(); + EndProgress2(); + + return; + } + + byte[] sector; + + if(sectors - doneSectors >= sectorsToRead) + { + errno = image.ReadSectorsLong(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber, + out sector); + + UpdateProgress2("Hashing sector {0} of {1}", (long)doneSectors, + (long)(trk.EndSector - trk.StartSector + 1)); + + if(errno != ErrorNumber.NoError) + { + UpdateStatus($"Error {errno} reading sector {doneSectors}"); + EndProgress2(); + + return; + } + + doneSectors += sectorsToRead; + } + else + { + errno = image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors), + xmlTrk.Sequence.TrackNumber, out sector); + + UpdateProgress2("Hashing sector {0} of {1}", (long)doneSectors, + (long)(trk.EndSector - trk.StartSector + 1)); + + if(errno != ErrorNumber.NoError) + { + UpdateStatus($"Error {errno} reading sector {doneSectors}"); + EndProgress2(); + + return; + } + + doneSectors += sectors - doneSectors; + } + + trkChkWorker.Update(sector); } - xmlTrk.Sequence = new TrackSequenceType + List trkChecksums = trkChkWorker.End(); + + xmlTrk.Checksums = trkChecksums.ToArray(); + + EndProgress2(); + } + + if(trk.SubchannelType != TrackSubchannelType.None) + { + xmlTrk.SubChannel = new SubChannelType { - Session = trk.Session, - TrackNumber = trk.Sequence + Image = new ImageType + { + Value = trk.SubchannelFile + }, + + // TODO: Packed subchannel has different size? + Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * 96 }; - xmlTrk.StartSector = trk.StartSector; - xmlTrk.EndSector = trk.EndSector; - - int idx0 = -1; - - if(trk.Indexes?.TryGetValue(0, out idx0) == true && - idx0 >= 0) - xmlTrk.StartSector = (ulong)idx0; - - switch(sidecar.OpticalDisc[0].DiscType) + switch(trk.SubchannelType) { - case "CD": - case "GD": - xmlTrk.StartMSF = LbaToMsf((long)xmlTrk.StartSector); - xmlTrk.EndMSF = LbaToMsf((long)xmlTrk.EndSector); + case TrackSubchannelType.Packed: + case TrackSubchannelType.PackedInterleaved: + xmlTrk.SubChannel.Image.format = "rw"; break; - case "DDCD": - xmlTrk.StartMSF = DdcdLbaToMsf((long)xmlTrk.StartSector); - xmlTrk.EndMSF = DdcdLbaToMsf((long)xmlTrk.EndSector); + case TrackSubchannelType.Raw: + case TrackSubchannelType.RawInterleaved: + xmlTrk.SubChannel.Image.format = "rw_raw"; + + break; + case TrackSubchannelType.Q16: + case TrackSubchannelType.Q16Interleaved: + xmlTrk.SubChannel.Image.format = "q16"; break; } - xmlTrk.Image = new ImageType - { - Value = Path.GetFileName(trk.File), - format = trk.FileType - }; - if(trk.FileOffset > 0) { - xmlTrk.Image.offset = trk.FileOffset; - xmlTrk.Image.offsetSpecified = true; + xmlTrk.SubChannel.Image.offset = trk.SubchannelOffset; + xmlTrk.SubChannel.Image.offsetSpecified = true; } - xmlTrk.Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * (ulong)trk.RawBytesPerSector; + var subChkWorker = new Checksum(); - xmlTrk.BytesPerSector = (uint)trk.BytesPerSector; + sectors = xmlTrk.EndSector - xmlTrk.StartSector + 1; + doneSectors = 0; - uint sectorsToRead = 512; - ulong sectors = xmlTrk.EndSector - xmlTrk.StartSector + 1; - ulong doneSectors = 0; + InitProgress2(); - // If there is only one track, and it's the same as the image file (e.g. ".iso" files), don't re-checksum. - if(image.Id == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") && - - // Only if filter is none... - (filterId == new Guid("12345678-AAAA-BBBB-CCCC-123456789000") || - - // ...or AppleDouble - filterId == new Guid("1b2165ee-c9df-4b21-bbbb-9e5892b2df4d"))) - xmlTrk.Checksums = sidecar.OpticalDisc[0].Checksums; - else + while(doneSectors < sectors) { - UpdateProgress("Track {0} of {1}", trk.Sequence, tracks.Count); - - // For fast debugging, skip checksum - //goto skipChecksum; - - var trkChkWorker = new Checksum(); - - InitProgress2(); - - while(doneSectors < sectors) + if(_aborted) { - if(_aborted) + EndProgress(); + EndProgress2(); + + return; + } + + byte[] sector; + + if(sectors - doneSectors >= sectorsToRead) + { + errno = image.ReadSectorsTag(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber, + SectorTagType.CdSectorSubchannel, out sector); + + UpdateProgress2("Hashing subchannel sector {0} of {1}", (long)doneSectors, + (long)(trk.EndSector - trk.StartSector + 1)); + + if(errno != ErrorNumber.NoError) { - EndProgress(); + UpdateStatus($"Error {errno} reading sector {doneSectors}"); EndProgress2(); return; } - byte[] sector; - - if(sectors - doneSectors >= sectorsToRead) - { - errno = image.ReadSectorsLong(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber, - out sector); - - UpdateProgress2("Hashing sector {0} of {1}", (long)doneSectors, - (long)(trk.EndSector - trk.StartSector + 1)); - - if(errno != ErrorNumber.NoError) - { - UpdateStatus($"Error {errno} reading sector {doneSectors}"); - EndProgress2(); - - return; - } - - doneSectors += sectorsToRead; - } - else - { - errno = image.ReadSectorsLong(doneSectors, (uint)(sectors - doneSectors), - xmlTrk.Sequence.TrackNumber, out sector); - - UpdateProgress2("Hashing sector {0} of {1}", (long)doneSectors, - (long)(trk.EndSector - trk.StartSector + 1)); - - if(errno != ErrorNumber.NoError) - { - UpdateStatus($"Error {errno} reading sector {doneSectors}"); - EndProgress2(); - - return; - } - - doneSectors += sectors - doneSectors; - } - - trkChkWorker.Update(sector); + doneSectors += sectorsToRead; } - - List trkChecksums = trkChkWorker.End(); - - xmlTrk.Checksums = trkChecksums.ToArray(); - - EndProgress2(); - } - - if(trk.SubchannelType != TrackSubchannelType.None) - { - xmlTrk.SubChannel = new SubChannelType + else { - Image = new ImageType + errno = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors), + xmlTrk.Sequence.TrackNumber, SectorTagType.CdSectorSubchannel, + out sector); + + UpdateProgress2("Hashing subchannel sector {0} of {1}", (long)doneSectors, + (long)(trk.EndSector - trk.StartSector + 1)); + + if(errno != ErrorNumber.NoError) { - Value = trk.SubchannelFile - }, - - // TODO: Packed subchannel has different size? - Size = (xmlTrk.EndSector - xmlTrk.StartSector + 1) * 96 - }; - - switch(trk.SubchannelType) - { - case TrackSubchannelType.Packed: - case TrackSubchannelType.PackedInterleaved: - xmlTrk.SubChannel.Image.format = "rw"; - - break; - case TrackSubchannelType.Raw: - case TrackSubchannelType.RawInterleaved: - xmlTrk.SubChannel.Image.format = "rw_raw"; - - break; - case TrackSubchannelType.Q16: - case TrackSubchannelType.Q16Interleaved: - xmlTrk.SubChannel.Image.format = "q16"; - - break; - } - - if(trk.FileOffset > 0) - { - xmlTrk.SubChannel.Image.offset = trk.SubchannelOffset; - xmlTrk.SubChannel.Image.offsetSpecified = true; - } - - var subChkWorker = new Checksum(); - - sectors = xmlTrk.EndSector - xmlTrk.StartSector + 1; - doneSectors = 0; - - InitProgress2(); - - while(doneSectors < sectors) - { - if(_aborted) - { - EndProgress(); + UpdateStatus($"Error {errno} reading sector {doneSectors}"); EndProgress2(); return; } - byte[] sector; - - if(sectors - doneSectors >= sectorsToRead) - { - errno = image.ReadSectorsTag(doneSectors, sectorsToRead, xmlTrk.Sequence.TrackNumber, - SectorTagType.CdSectorSubchannel, out sector); - - UpdateProgress2("Hashing subchannel sector {0} of {1}", (long)doneSectors, - (long)(trk.EndSector - trk.StartSector + 1)); - - if(errno != ErrorNumber.NoError) - { - UpdateStatus($"Error {errno} reading sector {doneSectors}"); - EndProgress2(); - - return; - } - - doneSectors += sectorsToRead; - } - else - { - errno = image.ReadSectorsTag(doneSectors, (uint)(sectors - doneSectors), - xmlTrk.Sequence.TrackNumber, SectorTagType.CdSectorSubchannel, - out sector); - - UpdateProgress2("Hashing subchannel sector {0} of {1}", (long)doneSectors, - (long)(trk.EndSector - trk.StartSector + 1)); - - if(errno != ErrorNumber.NoError) - { - UpdateStatus($"Error {errno} reading sector {doneSectors}"); - EndProgress2(); - - return; - } - - doneSectors += sectors - doneSectors; - } - - subChkWorker.Update(sector); + doneSectors += sectors - doneSectors; } - List subChecksums = subChkWorker.End(); - - xmlTrk.SubChannel.Checksums = subChecksums.ToArray(); - - EndProgress2(); + subChkWorker.Update(sector); } - // For fast debugging, skip checksum - //skipChecksum: + List subChecksums = subChkWorker.End(); - List trkPartitions = partitions. - Where(p => p.Start >= trk.StartSector && p.End <= trk.EndSector). - ToList(); + xmlTrk.SubChannel.Checksums = subChecksums.ToArray(); - xmlTrk.FileSystemInformation = new PartitionType[1]; + EndProgress2(); + } - if(trkPartitions.Count > 0) + // For fast debugging, skip checksum + //skipChecksum: + + List trkPartitions = partitions. + Where(p => p.Start >= trk.StartSector && p.End <= trk.EndSector). + ToList(); + + xmlTrk.FileSystemInformation = new PartitionType[1]; + + if(trkPartitions.Count > 0) + { + xmlTrk.FileSystemInformation = new PartitionType[trkPartitions.Count]; + + for(int i = 0; i < trkPartitions.Count; i++) { - xmlTrk.FileSystemInformation = new PartitionType[trkPartitions.Count]; - - for(int i = 0; i < trkPartitions.Count; i++) + xmlTrk.FileSystemInformation[i] = new PartitionType { - xmlTrk.FileSystemInformation[i] = new PartitionType - { - Description = trkPartitions[i].Description, - EndSector = trkPartitions[i].End, - Name = trkPartitions[i].Name, - Sequence = (uint)trkPartitions[i].Sequence, - StartSector = trkPartitions[i].Start, - Type = trkPartitions[i].Type - }; - - List lstFs = new(); - - foreach(IFilesystem plugin in plugins.PluginsList.Values) - try - { - if(_aborted) - { - EndProgress(); - - return; - } - - if(!plugin.Identify(image, trkPartitions[i])) - continue; - - plugin.GetInformation(image, trkPartitions[i], out _, encoding); - lstFs.Add(plugin.XmlFsType); - Statistics.AddFilesystem(plugin.XmlFsType.Type); - - switch(plugin.XmlFsType.Type) - { - case "Opera": - dskType = MediaType.ThreeDO; - - break; - case "PC Engine filesystem": - dskType = MediaType.SuperCDROM2; - - break; - case "Nintendo Wii filesystem": - dskType = MediaType.WOD; - - break; - case "Nintendo Gamecube filesystem": - dskType = MediaType.GOD; - - break; - } - } - #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body - catch - #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body - { - //AaruConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); - } - - if(lstFs.Count > 0) - xmlTrk.FileSystemInformation[i].FileSystems = lstFs.ToArray(); - } - } - else - { - xmlTrk.FileSystemInformation[0] = new PartitionType - { - EndSector = xmlTrk.EndSector, - StartSector = xmlTrk.StartSector + Description = trkPartitions[i].Description, + EndSector = trkPartitions[i].End, + Name = trkPartitions[i].Name, + Sequence = (uint)trkPartitions[i].Sequence, + StartSector = trkPartitions[i].Start, + Type = trkPartitions[i].Type }; List lstFs = new(); - var xmlPart = new Partition - { - Start = xmlTrk.StartSector, - Length = xmlTrk.EndSector - xmlTrk.StartSector + 1, - Type = xmlTrk.TrackType1.ToString(), - Size = xmlTrk.Size, - Sequence = xmlTrk.Sequence.TrackNumber - }; - foreach(IFilesystem plugin in plugins.PluginsList.Values) try { @@ -701,10 +634,10 @@ namespace Aaru.Core return; } - if(!plugin.Identify(image, xmlPart)) + if(!plugin.Identify(image, trkPartitions[i])) continue; - plugin.GetInformation(image, xmlPart, out _, encoding); + plugin.GetInformation(image, trkPartitions[i], out _, encoding); lstFs.Add(plugin.XmlFsType); Statistics.AddFilesystem(plugin.XmlFsType.Type); @@ -736,93 +669,159 @@ namespace Aaru.Core } if(lstFs.Count > 0) - xmlTrk.FileSystemInformation[0].FileSystems = lstFs.ToArray(); + xmlTrk.FileSystemInformation[i].FileSystems = lstFs.ToArray(); } - - errno = image.ReadSectorTag(trk.Sequence, SectorTagType.CdTrackIsrc, out byte[] isrcData); - - if(errno == ErrorNumber.NoError) - xmlTrk.ISRC = Encoding.UTF8.GetString(isrcData); - - errno = image.ReadSectorTag(trk.Sequence, SectorTagType.CdTrackFlags, out byte[] flagsData); - - if(errno == ErrorNumber.NoError) - { - var trackFlags = (CdFlags)flagsData[0]; - - xmlTrk.Flags = new TrackFlagsType - { - PreEmphasis = trackFlags.HasFlag(CdFlags.PreEmphasis), - CopyPermitted = trackFlags.HasFlag(CdFlags.CopyPermitted), - Data = trackFlags.HasFlag(CdFlags.DataTrack), - Quadraphonic = trackFlags.HasFlag(CdFlags.FourChannel) - }; - } - - if(trk.Indexes?.Count > 0) - { - xmlTrk.Indexes = trk.Indexes?.OrderBy(i => i.Key).Select(i => new TrackIndexType - { - index = i.Key, - Value = i.Value - }).ToArray(); - } - - trksLst.Add(xmlTrk); } - - EndProgress(); - - if(trksLst != null) - sidecar.OpticalDisc[0].Track = trksLst.ToArray(); - - // All XGD3 all have the same number of blocks - if(dskType == MediaType.XGD2 && - sidecar.OpticalDisc[0].Track.Length == 1) + else { - ulong blocks = sidecar.OpticalDisc[0].Track[0].EndSector - sidecar.OpticalDisc[0].Track[0].StartSector + - 1; - - if(blocks == 25063 || // Locked (or non compatible drive) - blocks == 4229664 || // Xtreme unlock - blocks == 4246304) // Wxripper unlock - dskType = MediaType.XGD3; - } - - (string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); - sidecar.OpticalDisc[0].DiscType = discType.type; - sidecar.OpticalDisc[0].DiscSubType = discType.subType; - Statistics.AddMedia(dskType, false); - - if(image.DumpHardware != null) - sidecar.OpticalDisc[0].DumpHardwareArray = image.DumpHardware.ToArray(); - else if(!string.IsNullOrEmpty(image.Info.DriveManufacturer) || - !string.IsNullOrEmpty(image.Info.DriveModel) || - !string.IsNullOrEmpty(image.Info.DriveFirmwareRevision) || - !string.IsNullOrEmpty(image.Info.DriveSerialNumber)) - sidecar.OpticalDisc[0].DumpHardwareArray = new[] + xmlTrk.FileSystemInformation[0] = new PartitionType { - new DumpHardwareType + EndSector = xmlTrk.EndSector, + StartSector = xmlTrk.StartSector + }; + + List lstFs = new(); + + var xmlPart = new Partition + { + Start = xmlTrk.StartSector, + Length = xmlTrk.EndSector - xmlTrk.StartSector + 1, + Type = xmlTrk.TrackType1.ToString(), + Size = xmlTrk.Size, + Sequence = xmlTrk.Sequence.TrackNumber + }; + + foreach(IFilesystem plugin in plugins.PluginsList.Values) + try { - Extents = new[] + if(_aborted) { - new ExtentType - { - Start = 0, - End = image.Info.Sectors - } - }, - Manufacturer = image.Info.DriveManufacturer, - Model = image.Info.DriveModel, - Firmware = image.Info.DriveFirmwareRevision, - Serial = image.Info.DriveSerialNumber, - Software = new SoftwareType + EndProgress(); + + return; + } + + if(!plugin.Identify(image, xmlPart)) + continue; + + plugin.GetInformation(image, xmlPart, out _, encoding); + lstFs.Add(plugin.XmlFsType); + Statistics.AddFilesystem(plugin.XmlFsType.Type); + + switch(plugin.XmlFsType.Type) { - Name = image.Info.Application, - Version = image.Info.ApplicationVersion + case "Opera": + dskType = MediaType.ThreeDO; + + break; + case "PC Engine filesystem": + dskType = MediaType.SuperCDROM2; + + break; + case "Nintendo Wii filesystem": + dskType = MediaType.WOD; + + break; + case "Nintendo Gamecube filesystem": + dskType = MediaType.GOD; + + break; } } + #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body + catch + #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body + { + //AaruConsole.DebugWriteLine("Create-sidecar command", "Plugin {0} crashed", _plugin.Name); + } + + if(lstFs.Count > 0) + xmlTrk.FileSystemInformation[0].FileSystems = lstFs.ToArray(); + } + + errno = image.ReadSectorTag(trk.Sequence, SectorTagType.CdTrackIsrc, out byte[] isrcData); + + if(errno == ErrorNumber.NoError) + xmlTrk.ISRC = Encoding.UTF8.GetString(isrcData); + + errno = image.ReadSectorTag(trk.Sequence, SectorTagType.CdTrackFlags, out byte[] flagsData); + + if(errno == ErrorNumber.NoError) + { + var trackFlags = (CdFlags)flagsData[0]; + + xmlTrk.Flags = new TrackFlagsType + { + PreEmphasis = trackFlags.HasFlag(CdFlags.PreEmphasis), + CopyPermitted = trackFlags.HasFlag(CdFlags.CopyPermitted), + Data = trackFlags.HasFlag(CdFlags.DataTrack), + Quadraphonic = trackFlags.HasFlag(CdFlags.FourChannel) }; + } + + if(trk.Indexes?.Count > 0) + { + xmlTrk.Indexes = trk.Indexes?.OrderBy(i => i.Key).Select(i => new TrackIndexType + { + index = i.Key, + Value = i.Value + }).ToArray(); + } + + trksLst.Add(xmlTrk); } + + EndProgress(); + + if(trksLst != null) + sidecar.OpticalDisc[0].Track = trksLst.ToArray(); + + // All XGD3 all have the same number of blocks + if(dskType == MediaType.XGD2 && + sidecar.OpticalDisc[0].Track.Length == 1) + { + ulong blocks = sidecar.OpticalDisc[0].Track[0].EndSector - sidecar.OpticalDisc[0].Track[0].StartSector + + 1; + + if(blocks == 25063 || // Locked (or non compatible drive) + blocks == 4229664 || // Xtreme unlock + blocks == 4246304) // Wxripper unlock + dskType = MediaType.XGD3; + } + + (string type, string subType) discType = CommonTypes.Metadata.MediaType.MediaTypeToString(dskType); + sidecar.OpticalDisc[0].DiscType = discType.type; + sidecar.OpticalDisc[0].DiscSubType = discType.subType; + Statistics.AddMedia(dskType, false); + + if(image.DumpHardware != null) + sidecar.OpticalDisc[0].DumpHardwareArray = image.DumpHardware.ToArray(); + else if(!string.IsNullOrEmpty(image.Info.DriveManufacturer) || + !string.IsNullOrEmpty(image.Info.DriveModel) || + !string.IsNullOrEmpty(image.Info.DriveFirmwareRevision) || + !string.IsNullOrEmpty(image.Info.DriveSerialNumber)) + sidecar.OpticalDisc[0].DumpHardwareArray = new[] + { + new DumpHardwareType + { + Extents = new[] + { + new ExtentType + { + Start = 0, + End = image.Info.Sectors + } + }, + Manufacturer = image.Info.DriveManufacturer, + Model = image.Info.DriveModel, + Firmware = image.Info.DriveFirmwareRevision, + Serial = image.Info.DriveSerialNumber, + Software = new SoftwareType + { + Name = image.Info.Application, + Version = image.Info.ApplicationVersion + } + } + }; } } \ No newline at end of file diff --git a/Aaru.Core/Spectre.cs b/Aaru.Core/Spectre.cs index 08bec295f..1f1232376 100644 --- a/Aaru.Core/Spectre.cs +++ b/Aaru.Core/Spectre.cs @@ -1,11 +1,10 @@ using System; using Spectre.Console; -namespace Aaru.Core +namespace Aaru.Core; + +public static class Spectre { - public static class Spectre - { - public static void ProgressSingleSpinner(Action action) => AnsiConsole.Progress().AutoClear(true).HideCompleted(true). - Columns(new TaskDescriptionColumn(), new SpinnerColumn()).Start(action); - } + public static void ProgressSingleSpinner(Action action) => AnsiConsole.Progress().AutoClear(true).HideCompleted(true). + Columns(new TaskDescriptionColumn(), new SpinnerColumn()).Start(action); } \ No newline at end of file diff --git a/Aaru.Core/Statistics.cs b/Aaru.Core/Statistics.cs index fdba38c87..589ff51a5 100644 --- a/Aaru.Core/Statistics.cs +++ b/Aaru.Core/Statistics.cs @@ -52,1431 +52,1430 @@ using MediaType = Aaru.CommonTypes.MediaType; using OperatingSystem = Aaru.Database.Models.OperatingSystem; using Version = Aaru.Database.Models.Version; -namespace Aaru.Core +namespace Aaru.Core; + +/// Handles anonymous usage statistics +public static class Statistics { - /// Handles anonymous usage statistics - public static class Statistics + /// Statistics file semaphore + static bool _submitStatsLock; + + /// Loads saved statistics from disk + public static void LoadStats() { - /// Statistics file semaphore - static bool _submitStatsLock; - - /// Loads saved statistics from disk - public static void LoadStats() + try { - try - { - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - if(File.Exists(Path.Combine(Settings.Settings.StatsPath, "Statistics.xml"))) - try - { - var allStats = new Stats(); - var xs = new XmlSerializer(allStats.GetType()); - - var sr = new StreamReader(Path.Combine(Settings.Settings.StatsPath, "Statistics.xml")); - - allStats = (Stats)xs.Deserialize(sr); - sr.Close(); - - if(allStats.Commands?.Analyze > 0) - { - Command command = ctx.Commands.FirstOrDefault(c => c.Name == "fs-info" && c.Synchronized) ?? - new Command - { - Name = "fs-info", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.Analyze; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.Checksum > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "checksum" && c.Synchronized) ?? new Command - { - Name = "checksum", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.Checksum; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.Compare > 0) - { - Command command = ctx.Commands.FirstOrDefault(c => c.Name == "compare" && c.Synchronized) ?? - new Command - { - Name = "compare", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.Compare; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.ConvertImage > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "convert-image" && c.Synchronized) ?? - new Command - { - Name = "convert-image", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.ConvertImage; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.CreateSidecar > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "create-sidecar" && c.Synchronized) ?? - new Command - { - Name = "create-sidecar", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.CreateSidecar; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.Decode > 0) - { - Command command = ctx.Commands.FirstOrDefault(c => c.Name == "decode" && c.Synchronized) ?? - new Command - { - Name = "decode", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.Decode; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.DeviceInfo > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "device-info" && c.Synchronized) ?? - new Command - { - Name = "device-info", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.DeviceInfo; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.DeviceReport > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "device-report" && c.Synchronized) ?? - new Command - { - Name = "device-report", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.DeviceReport; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.DumpMedia > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "dump-media" && c.Synchronized) ?? - new Command - { - Name = "dump-media", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.DumpMedia; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.Entropy > 0) - { - Command command = ctx.Commands.FirstOrDefault(c => c.Name == "entropy" && c.Synchronized) ?? - new Command - { - Name = "entropy", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.Entropy; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.ExtractFiles > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "extract-files" && c.Synchronized) ?? - new Command - { - Name = "extract-files", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.ExtractFiles; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.Formats > 0) - { - Command command = ctx.Commands.FirstOrDefault(c => c.Name == "formats" && c.Synchronized) ?? - new Command - { - Name = "formats", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.Formats; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.ImageInfo > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "image-info" && c.Synchronized) ?? - new Command - { - Name = "image-info", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.ImageInfo; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.ListDevices > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "list-devices" && c.Synchronized) ?? - new Command - { - Name = "list-devices", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.ListDevices; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.ListEncodings > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "list-encodings" && c.Synchronized) ?? - new Command - { - Name = "list-encodings", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.ListEncodings; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.Ls > 0) - { - Command command = ctx.Commands.FirstOrDefault(c => c.Name == "ls" && c.Synchronized) ?? - new Command - { - Name = "ls", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.Ls; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.MediaInfo > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "media-info" && c.Synchronized) ?? - new Command - { - Name = "media-info", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.MediaInfo; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.MediaScan > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "media-scan" && c.Synchronized) ?? - new Command - { - Name = "media-scan", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.MediaScan; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.PrintHex > 0) - { - Command command = - ctx.Commands.FirstOrDefault(c => c.Name == "printhex" && c.Synchronized) ?? new Command - { - Name = "printhex", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.PrintHex; - ctx.Commands.Update(command); - } - - if(allStats.Commands?.Verify > 0) - { - Command command = ctx.Commands.FirstOrDefault(c => c.Name == "verify" && c.Synchronized) ?? - new Command - { - Name = "verify", - Synchronized = true - }; - - command.Count += (ulong)allStats.Commands.Verify; - ctx.Commands.Update(command); - } - - if(allStats.OperatingSystems != null) - foreach(OsStats operatingSystem in allStats.OperatingSystems) - { - if(string.IsNullOrWhiteSpace(operatingSystem.name) || - string.IsNullOrWhiteSpace(operatingSystem.version)) - continue; - - OperatingSystem existing = - ctx.OperatingSystems.FirstOrDefault(c => c.Name == operatingSystem.name && - c.Version == operatingSystem.version && - c.Synchronized) ?? new OperatingSystem - { - Name = operatingSystem.name, - Version = operatingSystem.version, - Synchronized = true - }; - - existing.Count += (ulong)operatingSystem.Value; - ctx.OperatingSystems.Update(existing); - } - - if(allStats.Versions != null) - foreach(NameValueStats nvs in allStats.Versions) - { - if(string.IsNullOrWhiteSpace(nvs.name)) - continue; - - Version existing = - ctx.Versions.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? - new Version - { - Name = nvs.name, - Synchronized = true - }; - - existing.Count += (ulong)nvs.Value; - ctx.Versions.Update(existing); - } - - if(allStats.Filesystems != null) - foreach(NameValueStats nvs in allStats.Filesystems) - { - if(string.IsNullOrWhiteSpace(nvs.name)) - continue; - - Filesystem existing = - ctx.Filesystems.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? - new Filesystem - { - Name = nvs.name, - Synchronized = true - }; - - existing.Count += (ulong)nvs.Value; - ctx.Filesystems.Update(existing); - } - - if(allStats.Partitions != null) - foreach(NameValueStats nvs in allStats.Partitions) - { - if(string.IsNullOrWhiteSpace(nvs.name)) - continue; - - Partition existing = - ctx.Partitions.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? - new Partition - { - Name = nvs.name, - Synchronized = true - }; - - existing.Count += (ulong)nvs.Value; - ctx.Partitions.Update(existing); - } - - if(allStats.Filesystems != null) - foreach(NameValueStats nvs in allStats.Filesystems) - { - if(string.IsNullOrWhiteSpace(nvs.name)) - continue; - - Filesystem existing = - ctx.Filesystems.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? - new Filesystem - { - Name = nvs.name, - Synchronized = true - }; - - existing.Count += (ulong)nvs.Value; - ctx.Filesystems.Update(existing); - } - - if(allStats.MediaImages != null) - foreach(NameValueStats nvs in allStats.MediaImages) - { - if(string.IsNullOrWhiteSpace(nvs.name)) - continue; - - MediaFormat existing = - ctx.MediaFormats.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? - new MediaFormat - { - Name = nvs.name, - Synchronized = true - }; - - existing.Count += (ulong)nvs.Value; - ctx.MediaFormats.Update(existing); - } - - if(allStats.Filters != null) - foreach(NameValueStats nvs in allStats.Filters) - { - if(string.IsNullOrWhiteSpace(nvs.name)) - continue; - - Filter existing = - ctx.Filters.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? new Filter - { - Name = nvs.name, - Synchronized = true - }; - - existing.Count += (ulong)nvs.Value; - ctx.Filters.Update(existing); - } - - if(allStats.Devices != null) - foreach(DeviceStats device in allStats.Devices) - { - if(ctx.SeenDevices.Any(d => d.Manufacturer == device.Manufacturer && - d.Model == device.Model && d.Revision == device.Revision && - d.Bus == device.Bus)) - continue; - - ctx.SeenDevices.Add(new DeviceStat - { - Bus = device.Bus, - Manufacturer = device.Manufacturer, - Model = device.Model, - Revision = device.Revision, - Synchronized = true - }); - } - - if(allStats.Medias != null) - foreach(MediaStats media in allStats.Medias) - { - if(string.IsNullOrWhiteSpace(media.type)) - continue; - - Database.Models.Media existing = - ctx.Medias.FirstOrDefault(c => c.Type == media.type && c.Real == media.real && - c.Synchronized) ?? new Database.Models.Media - { - Type = media.type, - Real = media.real, - Synchronized = true - }; - - existing.Count += (ulong)media.Value; - ctx.Medias.Update(existing); - } - - ctx.SaveChanges(); - File.Delete(Path.Combine(Settings.Settings.StatsPath, "Statistics.xml")); - } - catch - { - // Do not care about it - } - - if(Settings.Settings.Current.Stats == null) - return; - - ctx.OperatingSystems.Add(new OperatingSystem - { - Name = DetectOS.GetRealPlatformID().ToString(), - Synchronized = false, - Version = DetectOS.GetVersion(), - Count = 1 - }); - - ctx.Versions.Add(new Version - { - Name = CommonTypes.Interop.Version.GetVersion(), - Synchronized = false, - Count = 1 - }); - - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } - } - - /// Saves statistics to disk - public static void SaveStats() - { - try - { - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } - - if(Settings.Settings.Current.Stats is { ShareStats: true }) - SubmitStats(); - } - - /// Submits statistics to Aaru.Server - static void SubmitStats() - { - var submitThread = new Thread(() => - { - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + if(File.Exists(Path.Combine(Settings.Settings.StatsPath, "Statistics.xml"))) try { - if(_submitStatsLock) - return; + var allStats = new Stats(); + var xs = new XmlSerializer(allStats.GetType()); - _submitStatsLock = true; + var sr = new StreamReader(Path.Combine(Settings.Settings.StatsPath, "Statistics.xml")); - if(ctx.Commands.Any(c => !c.Synchronized) || - ctx.Filesystems.Any(c => !c.Synchronized) || - ctx.Filters.Any(c => !c.Synchronized) || - ctx.MediaFormats.Any(c => !c.Synchronized) || - ctx.Partitions.Any(c => !c.Synchronized) || - ctx.Medias.Any(c => !c.Synchronized) || - ctx.SeenDevices.Any(c => !c.Synchronized) || - ctx.OperatingSystems.Any(c => !c.Synchronized) || - ctx.Versions.Any(c => !c.Synchronized) || - ctx.RemoteApplications.Any(c => !c.Synchronized) || - ctx.RemoteArchitectures.Any(c => !c.Synchronized) || - ctx.RemoteOperatingSystems.Any(c => !c.Synchronized)) + allStats = (Stats)xs.Deserialize(sr); + sr.Close(); + + if(allStats.Commands?.Analyze > 0) { - var dto = new StatsDto(); + Command command = ctx.Commands.FirstOrDefault(c => c.Name == "fs-info" && c.Synchronized) ?? + new Command + { + Name = "fs-info", + Synchronized = true + }; - if(ctx.Commands.Any(c => !c.Synchronized)) + command.Count += (ulong)allStats.Commands.Analyze; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.Checksum > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "checksum" && c.Synchronized) ?? new Command + { + Name = "checksum", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.Checksum; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.Compare > 0) + { + Command command = ctx.Commands.FirstOrDefault(c => c.Name == "compare" && c.Synchronized) ?? + new Command + { + Name = "compare", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.Compare; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.ConvertImage > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "convert-image" && c.Synchronized) ?? + new Command + { + Name = "convert-image", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.ConvertImage; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.CreateSidecar > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "create-sidecar" && c.Synchronized) ?? + new Command + { + Name = "create-sidecar", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.CreateSidecar; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.Decode > 0) + { + Command command = ctx.Commands.FirstOrDefault(c => c.Name == "decode" && c.Synchronized) ?? + new Command + { + Name = "decode", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.Decode; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.DeviceInfo > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "device-info" && c.Synchronized) ?? + new Command + { + Name = "device-info", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.DeviceInfo; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.DeviceReport > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "device-report" && c.Synchronized) ?? + new Command + { + Name = "device-report", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.DeviceReport; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.DumpMedia > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "dump-media" && c.Synchronized) ?? + new Command + { + Name = "dump-media", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.DumpMedia; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.Entropy > 0) + { + Command command = ctx.Commands.FirstOrDefault(c => c.Name == "entropy" && c.Synchronized) ?? + new Command + { + Name = "entropy", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.Entropy; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.ExtractFiles > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "extract-files" && c.Synchronized) ?? + new Command + { + Name = "extract-files", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.ExtractFiles; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.Formats > 0) + { + Command command = ctx.Commands.FirstOrDefault(c => c.Name == "formats" && c.Synchronized) ?? + new Command + { + Name = "formats", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.Formats; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.ImageInfo > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "image-info" && c.Synchronized) ?? + new Command + { + Name = "image-info", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.ImageInfo; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.ListDevices > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "list-devices" && c.Synchronized) ?? + new Command + { + Name = "list-devices", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.ListDevices; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.ListEncodings > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "list-encodings" && c.Synchronized) ?? + new Command + { + Name = "list-encodings", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.ListEncodings; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.Ls > 0) + { + Command command = ctx.Commands.FirstOrDefault(c => c.Name == "ls" && c.Synchronized) ?? + new Command + { + Name = "ls", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.Ls; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.MediaInfo > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "media-info" && c.Synchronized) ?? + new Command + { + Name = "media-info", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.MediaInfo; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.MediaScan > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "media-scan" && c.Synchronized) ?? + new Command + { + Name = "media-scan", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.MediaScan; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.PrintHex > 0) + { + Command command = + ctx.Commands.FirstOrDefault(c => c.Name == "printhex" && c.Synchronized) ?? new Command + { + Name = "printhex", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.PrintHex; + ctx.Commands.Update(command); + } + + if(allStats.Commands?.Verify > 0) + { + Command command = ctx.Commands.FirstOrDefault(c => c.Name == "verify" && c.Synchronized) ?? + new Command + { + Name = "verify", + Synchronized = true + }; + + command.Count += (ulong)allStats.Commands.Verify; + ctx.Commands.Update(command); + } + + if(allStats.OperatingSystems != null) + foreach(OsStats operatingSystem in allStats.OperatingSystems) { - dto.Commands = new List(); + if(string.IsNullOrWhiteSpace(operatingSystem.name) || + string.IsNullOrWhiteSpace(operatingSystem.version)) + continue; - foreach(string nvs in ctx.Commands.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - dto.Commands.Add(new NameValueStats + OperatingSystem existing = + ctx.OperatingSystems.FirstOrDefault(c => c.Name == operatingSystem.name && + c.Version == operatingSystem.version && + c.Synchronized) ?? new OperatingSystem { - name = nvs, - Value = ctx.Commands.LongCount(c => !c.Synchronized && c.Name == nvs) + Name = operatingSystem.name, + Version = operatingSystem.version, + Synchronized = true + }; + + existing.Count += (ulong)operatingSystem.Value; + ctx.OperatingSystems.Update(existing); + } + + if(allStats.Versions != null) + foreach(NameValueStats nvs in allStats.Versions) + { + if(string.IsNullOrWhiteSpace(nvs.name)) + continue; + + Version existing = + ctx.Versions.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? + new Version + { + Name = nvs.name, + Synchronized = true + }; + + existing.Count += (ulong)nvs.Value; + ctx.Versions.Update(existing); + } + + if(allStats.Filesystems != null) + foreach(NameValueStats nvs in allStats.Filesystems) + { + if(string.IsNullOrWhiteSpace(nvs.name)) + continue; + + Filesystem existing = + ctx.Filesystems.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? + new Filesystem + { + Name = nvs.name, + Synchronized = true + }; + + existing.Count += (ulong)nvs.Value; + ctx.Filesystems.Update(existing); + } + + if(allStats.Partitions != null) + foreach(NameValueStats nvs in allStats.Partitions) + { + if(string.IsNullOrWhiteSpace(nvs.name)) + continue; + + Partition existing = + ctx.Partitions.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? + new Partition + { + Name = nvs.name, + Synchronized = true + }; + + existing.Count += (ulong)nvs.Value; + ctx.Partitions.Update(existing); + } + + if(allStats.Filesystems != null) + foreach(NameValueStats nvs in allStats.Filesystems) + { + if(string.IsNullOrWhiteSpace(nvs.name)) + continue; + + Filesystem existing = + ctx.Filesystems.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? + new Filesystem + { + Name = nvs.name, + Synchronized = true + }; + + existing.Count += (ulong)nvs.Value; + ctx.Filesystems.Update(existing); + } + + if(allStats.MediaImages != null) + foreach(NameValueStats nvs in allStats.MediaImages) + { + if(string.IsNullOrWhiteSpace(nvs.name)) + continue; + + MediaFormat existing = + ctx.MediaFormats.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? + new MediaFormat + { + Name = nvs.name, + Synchronized = true + }; + + existing.Count += (ulong)nvs.Value; + ctx.MediaFormats.Update(existing); + } + + if(allStats.Filters != null) + foreach(NameValueStats nvs in allStats.Filters) + { + if(string.IsNullOrWhiteSpace(nvs.name)) + continue; + + Filter existing = + ctx.Filters.FirstOrDefault(c => c.Name == nvs.name && c.Synchronized) ?? new Filter + { + Name = nvs.name, + Synchronized = true + }; + + existing.Count += (ulong)nvs.Value; + ctx.Filters.Update(existing); + } + + if(allStats.Devices != null) + foreach(DeviceStats device in allStats.Devices) + { + if(ctx.SeenDevices.Any(d => d.Manufacturer == device.Manufacturer && + d.Model == device.Model && d.Revision == device.Revision && + d.Bus == device.Bus)) + continue; + + ctx.SeenDevices.Add(new DeviceStat + { + Bus = device.Bus, + Manufacturer = device.Manufacturer, + Model = device.Model, + Revision = device.Revision, + Synchronized = true + }); + } + + if(allStats.Medias != null) + foreach(MediaStats media in allStats.Medias) + { + if(string.IsNullOrWhiteSpace(media.type)) + continue; + + Database.Models.Media existing = + ctx.Medias.FirstOrDefault(c => c.Type == media.type && c.Real == media.real && + c.Synchronized) ?? new Database.Models.Media + { + Type = media.type, + Real = media.real, + Synchronized = true + }; + + existing.Count += (ulong)media.Value; + ctx.Medias.Update(existing); + } + + ctx.SaveChanges(); + File.Delete(Path.Combine(Settings.Settings.StatsPath, "Statistics.xml")); + } + catch + { + // Do not care about it + } + + if(Settings.Settings.Current.Stats == null) + return; + + ctx.OperatingSystems.Add(new OperatingSystem + { + Name = DetectOS.GetRealPlatformID().ToString(), + Synchronized = false, + Version = DetectOS.GetVersion(), + Count = 1 + }); + + ctx.Versions.Add(new Version + { + Name = CommonTypes.Interop.Version.GetVersion(), + Synchronized = false, + Count = 1 + }); + + ctx.SaveChanges(); + } + catch(SqliteException ex) + { + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); + } + } + + /// Saves statistics to disk + public static void SaveStats() + { + try + { + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + ctx.SaveChanges(); + } + catch(SqliteException ex) + { + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); + } + + if(Settings.Settings.Current.Stats is { ShareStats: true }) + SubmitStats(); + } + + /// Submits statistics to Aaru.Server + static void SubmitStats() + { + var submitThread = new Thread(() => + { + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + try + { + if(_submitStatsLock) + return; + + _submitStatsLock = true; + + if(ctx.Commands.Any(c => !c.Synchronized) || + ctx.Filesystems.Any(c => !c.Synchronized) || + ctx.Filters.Any(c => !c.Synchronized) || + ctx.MediaFormats.Any(c => !c.Synchronized) || + ctx.Partitions.Any(c => !c.Synchronized) || + ctx.Medias.Any(c => !c.Synchronized) || + ctx.SeenDevices.Any(c => !c.Synchronized) || + ctx.OperatingSystems.Any(c => !c.Synchronized) || + ctx.Versions.Any(c => !c.Synchronized) || + ctx.RemoteApplications.Any(c => !c.Synchronized) || + ctx.RemoteArchitectures.Any(c => !c.Synchronized) || + ctx.RemoteOperatingSystems.Any(c => !c.Synchronized)) + { + var dto = new StatsDto(); + + if(ctx.Commands.Any(c => !c.Synchronized)) + { + dto.Commands = new List(); + + foreach(string nvs in ctx.Commands.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + dto.Commands.Add(new NameValueStats + { + name = nvs, + Value = ctx.Commands.LongCount(c => !c.Synchronized && c.Name == nvs) + }); + } + + if(ctx.Filesystems.Any(c => !c.Synchronized)) + { + dto.Filesystems = new List(); + + foreach(string nvs in ctx.Filesystems.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + dto.Filesystems.Add(new NameValueStats + { + name = nvs, + Value = ctx.Filesystems.LongCount(c => !c.Synchronized && c.Name == nvs) + }); + } + + if(ctx.Filters.Any(c => !c.Synchronized)) + { + dto.Filters = new List(); + + foreach(string nvs in ctx.Filters.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + dto.Filters.Add(new NameValueStats + { + name = nvs, + Value = ctx.Filters.LongCount(c => !c.Synchronized && c.Name == nvs) + }); + } + + if(ctx.MediaFormats.Any(c => !c.Synchronized)) + { + dto.MediaFormats = new List(); + + foreach(string nvs in ctx.MediaFormats.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + dto.MediaFormats.Add(new NameValueStats + { + name = nvs, + Value = ctx.MediaFormats.LongCount(c => !c.Synchronized && c.Name == nvs) + }); + } + + if(ctx.Partitions.Any(c => !c.Synchronized)) + { + dto.Partitions = new List(); + + foreach(string nvs in ctx.Partitions.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + dto.Partitions.Add(new NameValueStats + { + name = nvs, + Value = ctx.Partitions.LongCount(c => !c.Synchronized && c.Name == nvs) + }); + } + + if(ctx.Versions.Any(c => !c.Synchronized)) + { + dto.Versions = new List(); + + foreach(string nvs in ctx.Versions.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + dto.Versions.Add(new NameValueStats + { + name = nvs, + Value = ctx.Versions.LongCount(c => !c.Synchronized && c.Name == nvs) + }); + } + + if(ctx.Medias.Any(c => !c.Synchronized)) + { + dto.Medias = new List(); + + foreach(string media in ctx.Medias.Where(c => !c.Synchronized).Select(c => c.Type). + Distinct()) + { + if(ctx.Medias.Any(c => !c.Synchronized && c.Type == media && c.Real)) + dto.Medias.Add(new MediaStats + { + real = true, + type = media, + Value = ctx.Medias.LongCount(c => !c.Synchronized && c.Type == media && c.Real) + }); + + if(ctx.Medias.Any(c => !c.Synchronized && c.Type == media && !c.Real)) + dto.Medias.Add(new MediaStats + { + real = false, + type = media, + Value = ctx.Medias.LongCount(c => !c.Synchronized && c.Type == media && !c.Real) }); } + } - if(ctx.Filesystems.Any(c => !c.Synchronized)) + if(ctx.SeenDevices.Any(c => !c.Synchronized)) + { + dto.Devices = new List(); + + foreach(DeviceStat device in ctx.SeenDevices.Where(c => !c.Synchronized)) + dto.Devices.Add(new DeviceStats + { + Bus = device.Bus, + Manufacturer = device.Manufacturer, + ManufacturerSpecified = !(device.Manufacturer is null), + Model = device.Model, + Revision = device.Revision + }); + } + + if(ctx.OperatingSystems.Any(c => !c.Synchronized)) + { + dto.OperatingSystems = new List(); + + foreach(string osName in ctx.OperatingSystems.Where(c => !c.Synchronized). + Select(c => c.Name).Distinct()) { - dto.Filesystems = new List(); - - foreach(string nvs in ctx.Filesystems.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - dto.Filesystems.Add(new NameValueStats + foreach(string osVersion in ctx.OperatingSystems. + Where(c => !c.Synchronized && c.Name == osName). + Select(c => c.Version).Distinct()) + dto.OperatingSystems.Add(new OsStats { - name = nvs, - Value = ctx.Filesystems.LongCount(c => !c.Synchronized && c.Name == nvs) + name = osName, + version = osVersion, + Value = ctx.OperatingSystems.LongCount(c => !c.Synchronized && + c.Name == osName && + c.Version == osVersion) }); } + } - if(ctx.Filters.Any(c => !c.Synchronized)) + if(ctx.RemoteApplications.Any(c => !c.Synchronized)) + { + dto.RemoteApplications = new List(); + + foreach(string remoteAppName in ctx.RemoteApplications.Where(c => !c.Synchronized). + Select(c => c.Name).Distinct()) { - dto.Filters = new List(); - - foreach(string nvs in ctx.Filters.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - dto.Filters.Add(new NameValueStats + foreach(string remoteAppVersion in ctx.RemoteApplications. + Where(c => !c.Synchronized && + c.Name == remoteAppName). + Select(c => c.Version).Distinct()) + dto.RemoteApplications.Add(new OsStats { - name = nvs, - Value = ctx.Filters.LongCount(c => !c.Synchronized && c.Name == nvs) + name = remoteAppName, + version = remoteAppVersion, + Value = ctx.RemoteApplications.LongCount(c => !c.Synchronized && + c.Name == remoteAppName && c.Version == remoteAppVersion) }); } + } - if(ctx.MediaFormats.Any(c => !c.Synchronized)) - { - dto.MediaFormats = new List(); + if(ctx.RemoteArchitectures.Any(c => !c.Synchronized)) + { + dto.RemoteArchitectures = new List(); - foreach(string nvs in ctx.MediaFormats.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - dto.MediaFormats.Add(new NameValueStats - { - name = nvs, - Value = ctx.MediaFormats.LongCount(c => !c.Synchronized && c.Name == nvs) - }); - } - - if(ctx.Partitions.Any(c => !c.Synchronized)) - { - dto.Partitions = new List(); - - foreach(string nvs in ctx.Partitions.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - dto.Partitions.Add(new NameValueStats - { - name = nvs, - Value = ctx.Partitions.LongCount(c => !c.Synchronized && c.Name == nvs) - }); - } - - if(ctx.Versions.Any(c => !c.Synchronized)) - { - dto.Versions = new List(); - - foreach(string nvs in ctx.Versions.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - dto.Versions.Add(new NameValueStats - { - name = nvs, - Value = ctx.Versions.LongCount(c => !c.Synchronized && c.Name == nvs) - }); - } - - if(ctx.Medias.Any(c => !c.Synchronized)) - { - dto.Medias = new List(); - - foreach(string media in ctx.Medias.Where(c => !c.Synchronized).Select(c => c.Type). - Distinct()) + foreach(string nvs in ctx.RemoteArchitectures.Where(c => !c.Synchronized). + Select(c => c.Name).Distinct()) + dto.RemoteArchitectures.Add(new NameValueStats { - if(ctx.Medias.Any(c => !c.Synchronized && c.Type == media && c.Real)) - dto.Medias.Add(new MediaStats - { - real = true, - type = media, - Value = ctx.Medias.LongCount(c => !c.Synchronized && c.Type == media && c.Real) - }); + name = nvs, + Value = ctx.RemoteArchitectures.LongCount(c => !c.Synchronized && c.Name == nvs) + }); + } - if(ctx.Medias.Any(c => !c.Synchronized && c.Type == media && !c.Real)) - dto.Medias.Add(new MediaStats - { - real = false, - type = media, - Value = ctx.Medias.LongCount(c => !c.Synchronized && c.Type == media && !c.Real) - }); - } - } - - if(ctx.SeenDevices.Any(c => !c.Synchronized)) - { - dto.Devices = new List(); - - foreach(DeviceStat device in ctx.SeenDevices.Where(c => !c.Synchronized)) - dto.Devices.Add(new DeviceStats - { - Bus = device.Bus, - Manufacturer = device.Manufacturer, - ManufacturerSpecified = !(device.Manufacturer is null), - Model = device.Model, - Revision = device.Revision - }); - } - - if(ctx.OperatingSystems.Any(c => !c.Synchronized)) - { - dto.OperatingSystems = new List(); - - foreach(string osName in ctx.OperatingSystems.Where(c => !c.Synchronized). - Select(c => c.Name).Distinct()) - { - foreach(string osVersion in ctx.OperatingSystems. - Where(c => !c.Synchronized && c.Name == osName). - Select(c => c.Version).Distinct()) - dto.OperatingSystems.Add(new OsStats - { - name = osName, - version = osVersion, - Value = ctx.OperatingSystems.LongCount(c => !c.Synchronized && - c.Name == osName && - c.Version == osVersion) - }); - } - } - - if(ctx.RemoteApplications.Any(c => !c.Synchronized)) - { - dto.RemoteApplications = new List(); - - foreach(string remoteAppName in ctx.RemoteApplications.Where(c => !c.Synchronized). - Select(c => c.Name).Distinct()) - { - foreach(string remoteAppVersion in ctx.RemoteApplications. - Where(c => !c.Synchronized && - c.Name == remoteAppName). - Select(c => c.Version).Distinct()) - dto.RemoteApplications.Add(new OsStats - { - name = remoteAppName, - version = remoteAppVersion, - Value = ctx.RemoteApplications.LongCount(c => !c.Synchronized && - c.Name == remoteAppName && c.Version == remoteAppVersion) - }); - } - } - - if(ctx.RemoteArchitectures.Any(c => !c.Synchronized)) - { - dto.RemoteArchitectures = new List(); - - foreach(string nvs in ctx.RemoteArchitectures.Where(c => !c.Synchronized). - Select(c => c.Name).Distinct()) - dto.RemoteArchitectures.Add(new NameValueStats - { - name = nvs, - Value = ctx.RemoteArchitectures.LongCount(c => !c.Synchronized && c.Name == nvs) - }); - } - - if(ctx.RemoteOperatingSystems.Any(c => !c.Synchronized)) - { - dto.RemoteOperatingSystems = new List(); - - foreach(string remoteOsName in ctx.RemoteOperatingSystems.Where(c => !c.Synchronized). - Select(c => c.Name).Distinct()) - { - foreach(string remoteOsVersion in ctx.RemoteOperatingSystems. - Where(c => !c.Synchronized && - c.Name == remoteOsName). - Select(c => c.Version).Distinct()) - dto.RemoteOperatingSystems.Add(new OsStats - { - name = remoteOsName, - version = remoteOsVersion, - Value = ctx.RemoteOperatingSystems.LongCount(c => !c.Synchronized && - c.Name == remoteOsName && c.Version == remoteOsVersion) - }); - } - } - - #if DEBUG - System.Console.WriteLine("Uploading statistics"); - #else - Aaru.Console.AaruConsole.DebugWriteLine("Submit stats", "Uploading statistics"); - #endif - - string json = JsonConvert.SerializeObject(dto, Formatting.Indented, new JsonSerializerSettings - { - NullValueHandling = NullValueHandling.Ignore - }); - - byte[] jsonBytes = Encoding.UTF8.GetBytes(json); - var request = WebRequest.Create("https://www.aaru.app/api/uploadstatsv2"); - - ((HttpWebRequest)request).UserAgent = $"Aaru {typeof(Version).Assembly.GetName().Version}"; - - request.Method = "POST"; - request.ContentLength = jsonBytes.Length; - request.ContentType = "application/json"; - Stream reqStream = request.GetRequestStream(); - reqStream.Write(jsonBytes, 0, jsonBytes.Length); - - //jsonStream.CopyTo(reqStream); - reqStream.Close(); - WebResponse response = request.GetResponse(); - - if(((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) - return; - - Stream data = response.GetResponseStream(); - var reader = new StreamReader(data ?? throw new InvalidOperationException()); - - string result = reader.ReadToEnd(); - data.Close(); - response.Close(); - - if(result != "ok") - return; - - if(ctx.Commands.Any(c => !c.Synchronized)) - foreach(string nvs in ctx.Commands.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - { - Command existing = ctx.Commands.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? - new Command - { - Name = nvs, - Synchronized = true - }; - - existing.Count += (ulong)ctx.Commands.LongCount(c => !c.Synchronized && c.Name == nvs); - ctx.Commands.Update(existing); - ctx.Commands.RemoveRange(ctx.Commands.Where(c => !c.Synchronized && c.Name == nvs)); - } - - if(ctx.Filesystems.Any(c => !c.Synchronized)) - foreach(string nvs in ctx.Filesystems.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - { - Filesystem existing = - ctx.Filesystems.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? - new Filesystem - { - Name = nvs, - Synchronized = true - }; - - existing.Count += - (ulong)ctx.Filesystems.LongCount(c => !c.Synchronized && c.Name == nvs); - - ctx.Filesystems.Update(existing); - - ctx.Filesystems.RemoveRange(ctx.Filesystems.Where(c => !c.Synchronized && - c.Name == nvs)); - } - - if(ctx.Filters.Any(c => !c.Synchronized)) - foreach(string nvs in ctx.Filters.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - { - Filter existing = ctx.Filters.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? - new Filter - { - Name = nvs, - Synchronized = true - }; - - existing.Count += (ulong)ctx.Filters.LongCount(c => !c.Synchronized && c.Name == nvs); - ctx.Filters.Update(existing); - ctx.Filters.RemoveRange(ctx.Filters.Where(c => !c.Synchronized && c.Name == nvs)); - } - - if(ctx.MediaFormats.Any(c => !c.Synchronized)) - foreach(string nvs in ctx.MediaFormats.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - { - MediaFormat existing = - ctx.MediaFormats.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? - new MediaFormat - { - Name = nvs, - Synchronized = true - }; - - existing.Count += - (ulong)ctx.MediaFormats.LongCount(c => !c.Synchronized && c.Name == nvs); - - ctx.MediaFormats.Update(existing); - - ctx.MediaFormats.RemoveRange(ctx.MediaFormats.Where(c => !c.Synchronized && - c.Name == nvs)); - } - - if(ctx.Partitions.Any(c => !c.Synchronized)) - foreach(string nvs in ctx.Partitions.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - { - Partition existing = - ctx.Partitions.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? new Partition - { - Name = nvs, - Synchronized = true - }; - - existing.Count += - (ulong)ctx.Partitions.LongCount(c => !c.Synchronized && c.Name == nvs); - - ctx.Partitions.Update(existing); - ctx.Partitions.RemoveRange(ctx.Partitions.Where(c => !c.Synchronized && c.Name == nvs)); - } - - if(ctx.Versions.Any(c => !c.Synchronized)) - foreach(string nvs in ctx.Versions.Where(c => !c.Synchronized).Select(c => c.Name). - Distinct()) - { - Version existing = ctx.Versions.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? - new Version - { - Name = nvs, - Synchronized = true - }; - - existing.Count += (ulong)ctx.Versions.LongCount(c => !c.Synchronized && c.Name == nvs); - ctx.Versions.Update(existing); - ctx.Versions.RemoveRange(ctx.Versions.Where(c => !c.Synchronized && c.Name == nvs)); - } - - if(ctx.Medias.Any(c => !c.Synchronized)) - foreach(string media in ctx.Medias.Where(c => !c.Synchronized).Select(c => c.Type). - Distinct()) - { - if(ctx.Medias.Any(c => !c.Synchronized && c.Type == media && c.Real)) - { - Database.Models.Media existing = - ctx.Medias.FirstOrDefault(c => c.Synchronized && c.Type == media && c.Real) ?? - new Database.Models.Media - { - Synchronized = true, - Type = media, - Real = true - }; - - existing.Count += - (ulong)ctx.Medias.LongCount(c => !c.Synchronized && c.Type == media && c.Real); - - ctx.Medias.Update(existing); - - ctx.Medias.RemoveRange(ctx.Medias.Where(c => !c.Synchronized && c.Type == media && - c.Real)); - } - - if(!ctx.Medias.Any(c => !c.Synchronized && c.Type == media && !c.Real)) - continue; - - { - Database.Models.Media existing = - ctx.Medias.FirstOrDefault(c => c.Synchronized && c.Type == media && !c.Real) ?? - new Database.Models.Media - { - Synchronized = true, - Type = media, - Real = false - }; - - existing.Count += - (ulong)ctx.Medias.LongCount(c => !c.Synchronized && c.Type == media && !c.Real); - - ctx.Medias.Update(existing); - - ctx.Medias.RemoveRange(ctx.Medias.Where(c => !c.Synchronized && c.Type == media && - !c.Real)); - } - } - - if(ctx.SeenDevices.Any(c => !c.Synchronized)) - foreach(DeviceStat device in ctx.SeenDevices.Where(c => !c.Synchronized)) - { - device.Synchronized = true; - ctx.Update(device); - } - - if(ctx.OperatingSystems.Any(c => !c.Synchronized)) - foreach(string osName in ctx.OperatingSystems.Where(c => !c.Synchronized). - Select(c => c.Name).Distinct()) - { - foreach(string osVersion in ctx.OperatingSystems. - Where(c => !c.Synchronized && c.Name == osName). - Select(c => c.Version).Distinct()) - { - OperatingSystem existing = - ctx.OperatingSystems.FirstOrDefault(c => c.Synchronized && c.Name == osName && - c.Version == osVersion) ?? - new OperatingSystem - { - Synchronized = true, - Version = osVersion, - Name = osName - }; - - existing.Count += - (ulong)ctx.OperatingSystems.LongCount(c => !c.Synchronized && - c.Name == osName && - c.Version == osVersion); - - ctx.OperatingSystems.Update(existing); - - ctx.OperatingSystems.RemoveRange(ctx.OperatingSystems.Where(c => !c.Synchronized && - c.Name == osName && - c.Version == osVersion)); - } - } - - if(ctx.RemoteApplications.Any(c => !c.Synchronized)) - foreach(string remoteAppName in ctx.RemoteApplications.Where(c => !c.Synchronized). - Select(c => c.Name).Distinct()) - { - foreach(string remoteAppVersion in ctx.RemoteApplications. - Where(c => !c.Synchronized && - c.Name == remoteAppName). - Select(c => c.Version).Distinct()) - { - RemoteApplication existing = - ctx.RemoteApplications.FirstOrDefault(c => c.Synchronized && - c.Name == remoteAppName && - c.Version == remoteAppVersion) ?? - new RemoteApplication - { - Synchronized = true, - Version = remoteAppVersion, - Name = remoteAppName - }; - - existing.Count += - (ulong)ctx.RemoteApplications.LongCount(c => !c.Synchronized && - c.Name == remoteAppName && - c.Version == remoteAppVersion); - - ctx.RemoteApplications.Update(existing); - - ctx.RemoteApplications.RemoveRange(ctx.RemoteApplications.Where(c => - !c.Synchronized && - c.Name == remoteAppName && - c.Version == remoteAppVersion)); - } - } - - if(ctx.RemoteArchitectures.Any(c => !c.Synchronized)) - foreach(string nvs in ctx.RemoteArchitectures.Where(c => !c.Synchronized). - Select(c => c.Name).Distinct()) - { - RemoteArchitecture existing = - ctx.RemoteArchitectures.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? - new RemoteArchitecture - { - Name = nvs, - Synchronized = true - }; - - existing.Count += - (ulong)ctx.RemoteArchitectures.LongCount(c => !c.Synchronized && c.Name == nvs); - - ctx.RemoteArchitectures.Update(existing); - - ctx.RemoteArchitectures.RemoveRange(ctx.RemoteArchitectures. - Where(c => !c.Synchronized && c.Name == nvs)); - } + if(ctx.RemoteOperatingSystems.Any(c => !c.Synchronized)) + { + dto.RemoteOperatingSystems = new List(); foreach(string remoteOsName in ctx.RemoteOperatingSystems.Where(c => !c.Synchronized). Select(c => c.Name).Distinct()) { foreach(string remoteOsVersion in ctx.RemoteOperatingSystems. - Where(c => !c.Synchronized && c.Name == remoteOsName). + Where(c => !c.Synchronized && + c.Name == remoteOsName). Select(c => c.Version).Distinct()) + dto.RemoteOperatingSystems.Add(new OsStats + { + name = remoteOsName, + version = remoteOsVersion, + Value = ctx.RemoteOperatingSystems.LongCount(c => !c.Synchronized && + c.Name == remoteOsName && c.Version == remoteOsVersion) + }); + } + } + + #if DEBUG + System.Console.WriteLine("Uploading statistics"); + #else + Aaru.Console.AaruConsole.DebugWriteLine("Submit stats", "Uploading statistics"); + #endif + + string json = JsonConvert.SerializeObject(dto, Formatting.Indented, new JsonSerializerSettings + { + NullValueHandling = NullValueHandling.Ignore + }); + + byte[] jsonBytes = Encoding.UTF8.GetBytes(json); + var request = WebRequest.Create("https://www.aaru.app/api/uploadstatsv2"); + + ((HttpWebRequest)request).UserAgent = $"Aaru {typeof(Version).Assembly.GetName().Version}"; + + request.Method = "POST"; + request.ContentLength = jsonBytes.Length; + request.ContentType = "application/json"; + Stream reqStream = request.GetRequestStream(); + reqStream.Write(jsonBytes, 0, jsonBytes.Length); + + //jsonStream.CopyTo(reqStream); + reqStream.Close(); + WebResponse response = request.GetResponse(); + + if(((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) + return; + + Stream data = response.GetResponseStream(); + var reader = new StreamReader(data ?? throw new InvalidOperationException()); + + string result = reader.ReadToEnd(); + data.Close(); + response.Close(); + + if(result != "ok") + return; + + if(ctx.Commands.Any(c => !c.Synchronized)) + foreach(string nvs in ctx.Commands.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + { + Command existing = ctx.Commands.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? + new Command + { + Name = nvs, + Synchronized = true + }; + + existing.Count += (ulong)ctx.Commands.LongCount(c => !c.Synchronized && c.Name == nvs); + ctx.Commands.Update(existing); + ctx.Commands.RemoveRange(ctx.Commands.Where(c => !c.Synchronized && c.Name == nvs)); + } + + if(ctx.Filesystems.Any(c => !c.Synchronized)) + foreach(string nvs in ctx.Filesystems.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + { + Filesystem existing = + ctx.Filesystems.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? + new Filesystem + { + Name = nvs, + Synchronized = true + }; + + existing.Count += + (ulong)ctx.Filesystems.LongCount(c => !c.Synchronized && c.Name == nvs); + + ctx.Filesystems.Update(existing); + + ctx.Filesystems.RemoveRange(ctx.Filesystems.Where(c => !c.Synchronized && + c.Name == nvs)); + } + + if(ctx.Filters.Any(c => !c.Synchronized)) + foreach(string nvs in ctx.Filters.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + { + Filter existing = ctx.Filters.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? + new Filter + { + Name = nvs, + Synchronized = true + }; + + existing.Count += (ulong)ctx.Filters.LongCount(c => !c.Synchronized && c.Name == nvs); + ctx.Filters.Update(existing); + ctx.Filters.RemoveRange(ctx.Filters.Where(c => !c.Synchronized && c.Name == nvs)); + } + + if(ctx.MediaFormats.Any(c => !c.Synchronized)) + foreach(string nvs in ctx.MediaFormats.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + { + MediaFormat existing = + ctx.MediaFormats.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? + new MediaFormat + { + Name = nvs, + Synchronized = true + }; + + existing.Count += + (ulong)ctx.MediaFormats.LongCount(c => !c.Synchronized && c.Name == nvs); + + ctx.MediaFormats.Update(existing); + + ctx.MediaFormats.RemoveRange(ctx.MediaFormats.Where(c => !c.Synchronized && + c.Name == nvs)); + } + + if(ctx.Partitions.Any(c => !c.Synchronized)) + foreach(string nvs in ctx.Partitions.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + { + Partition existing = + ctx.Partitions.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? new Partition + { + Name = nvs, + Synchronized = true + }; + + existing.Count += + (ulong)ctx.Partitions.LongCount(c => !c.Synchronized && c.Name == nvs); + + ctx.Partitions.Update(existing); + ctx.Partitions.RemoveRange(ctx.Partitions.Where(c => !c.Synchronized && c.Name == nvs)); + } + + if(ctx.Versions.Any(c => !c.Synchronized)) + foreach(string nvs in ctx.Versions.Where(c => !c.Synchronized).Select(c => c.Name). + Distinct()) + { + Version existing = ctx.Versions.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? + new Version + { + Name = nvs, + Synchronized = true + }; + + existing.Count += (ulong)ctx.Versions.LongCount(c => !c.Synchronized && c.Name == nvs); + ctx.Versions.Update(existing); + ctx.Versions.RemoveRange(ctx.Versions.Where(c => !c.Synchronized && c.Name == nvs)); + } + + if(ctx.Medias.Any(c => !c.Synchronized)) + foreach(string media in ctx.Medias.Where(c => !c.Synchronized).Select(c => c.Type). + Distinct()) + { + if(ctx.Medias.Any(c => !c.Synchronized && c.Type == media && c.Real)) { - RemoteOperatingSystem existing = - ctx.RemoteOperatingSystems.FirstOrDefault(c => c.Synchronized && - c.Name == remoteOsName && - c.Version == remoteOsVersion) ?? - new RemoteOperatingSystem + Database.Models.Media existing = + ctx.Medias.FirstOrDefault(c => c.Synchronized && c.Type == media && c.Real) ?? + new Database.Models.Media { Synchronized = true, - Version = remoteOsVersion, - Name = remoteOsName + Type = media, + Real = true }; existing.Count += - (ulong)ctx.RemoteOperatingSystems.LongCount(c => !c.Synchronized && - c.Name == remoteOsName && - c.Version == remoteOsVersion); + (ulong)ctx.Medias.LongCount(c => !c.Synchronized && c.Type == media && c.Real); - ctx.RemoteOperatingSystems.Update(existing); + ctx.Medias.Update(existing); - ctx.RemoteOperatingSystems.RemoveRange(ctx.RemoteOperatingSystems.Where(c => - !c.Synchronized && - c.Name == remoteOsName && - c.Version == remoteOsVersion)); + ctx.Medias.RemoveRange(ctx.Medias.Where(c => !c.Synchronized && c.Type == media && + c.Real)); + } + + if(!ctx.Medias.Any(c => !c.Synchronized && c.Type == media && !c.Real)) + continue; + + { + Database.Models.Media existing = + ctx.Medias.FirstOrDefault(c => c.Synchronized && c.Type == media && !c.Real) ?? + new Database.Models.Media + { + Synchronized = true, + Type = media, + Real = false + }; + + existing.Count += + (ulong)ctx.Medias.LongCount(c => !c.Synchronized && c.Type == media && !c.Real); + + ctx.Medias.Update(existing); + + ctx.Medias.RemoveRange(ctx.Medias.Where(c => !c.Synchronized && c.Type == media && + !c.Real)); } } - ctx.SaveChanges(); + if(ctx.SeenDevices.Any(c => !c.Synchronized)) + foreach(DeviceStat device in ctx.SeenDevices.Where(c => !c.Synchronized)) + { + device.Synchronized = true; + ctx.Update(device); + } + + if(ctx.OperatingSystems.Any(c => !c.Synchronized)) + foreach(string osName in ctx.OperatingSystems.Where(c => !c.Synchronized). + Select(c => c.Name).Distinct()) + { + foreach(string osVersion in ctx.OperatingSystems. + Where(c => !c.Synchronized && c.Name == osName). + Select(c => c.Version).Distinct()) + { + OperatingSystem existing = + ctx.OperatingSystems.FirstOrDefault(c => c.Synchronized && c.Name == osName && + c.Version == osVersion) ?? + new OperatingSystem + { + Synchronized = true, + Version = osVersion, + Name = osName + }; + + existing.Count += + (ulong)ctx.OperatingSystems.LongCount(c => !c.Synchronized && + c.Name == osName && + c.Version == osVersion); + + ctx.OperatingSystems.Update(existing); + + ctx.OperatingSystems.RemoveRange(ctx.OperatingSystems.Where(c => !c.Synchronized && + c.Name == osName && + c.Version == osVersion)); + } + } + + if(ctx.RemoteApplications.Any(c => !c.Synchronized)) + foreach(string remoteAppName in ctx.RemoteApplications.Where(c => !c.Synchronized). + Select(c => c.Name).Distinct()) + { + foreach(string remoteAppVersion in ctx.RemoteApplications. + Where(c => !c.Synchronized && + c.Name == remoteAppName). + Select(c => c.Version).Distinct()) + { + RemoteApplication existing = + ctx.RemoteApplications.FirstOrDefault(c => c.Synchronized && + c.Name == remoteAppName && + c.Version == remoteAppVersion) ?? + new RemoteApplication + { + Synchronized = true, + Version = remoteAppVersion, + Name = remoteAppName + }; + + existing.Count += + (ulong)ctx.RemoteApplications.LongCount(c => !c.Synchronized && + c.Name == remoteAppName && + c.Version == remoteAppVersion); + + ctx.RemoteApplications.Update(existing); + + ctx.RemoteApplications.RemoveRange(ctx.RemoteApplications.Where(c => + !c.Synchronized && + c.Name == remoteAppName && + c.Version == remoteAppVersion)); + } + } + + if(ctx.RemoteArchitectures.Any(c => !c.Synchronized)) + foreach(string nvs in ctx.RemoteArchitectures.Where(c => !c.Synchronized). + Select(c => c.Name).Distinct()) + { + RemoteArchitecture existing = + ctx.RemoteArchitectures.FirstOrDefault(c => c.Synchronized && c.Name == nvs) ?? + new RemoteArchitecture + { + Name = nvs, + Synchronized = true + }; + + existing.Count += + (ulong)ctx.RemoteArchitectures.LongCount(c => !c.Synchronized && c.Name == nvs); + + ctx.RemoteArchitectures.Update(existing); + + ctx.RemoteArchitectures.RemoveRange(ctx.RemoteArchitectures. + Where(c => !c.Synchronized && c.Name == nvs)); + } + + foreach(string remoteOsName in ctx.RemoteOperatingSystems.Where(c => !c.Synchronized). + Select(c => c.Name).Distinct()) + { + foreach(string remoteOsVersion in ctx.RemoteOperatingSystems. + Where(c => !c.Synchronized && c.Name == remoteOsName). + Select(c => c.Version).Distinct()) + { + RemoteOperatingSystem existing = + ctx.RemoteOperatingSystems.FirstOrDefault(c => c.Synchronized && + c.Name == remoteOsName && + c.Version == remoteOsVersion) ?? + new RemoteOperatingSystem + { + Synchronized = true, + Version = remoteOsVersion, + Name = remoteOsName + }; + + existing.Count += + (ulong)ctx.RemoteOperatingSystems.LongCount(c => !c.Synchronized && + c.Name == remoteOsName && + c.Version == remoteOsVersion); + + ctx.RemoteOperatingSystems.Update(existing); + + ctx.RemoteOperatingSystems.RemoveRange(ctx.RemoteOperatingSystems.Where(c => + !c.Synchronized && + c.Name == remoteOsName && + c.Version == remoteOsVersion)); + } } + + ctx.SaveChanges(); + } + } + catch(WebException) + { + // Can't connect to the server, do nothing + } + catch(DbUpdateConcurrencyException) + { + // Ignore db concurrency errors + } + + // ReSharper disable once RedundantCatchClause + catch + { + #if DEBUG + _submitStatsLock = false; + + if(Debugger.IsAttached) + throw; + #endif + } + + IEnumerable statsFiles = + Directory.EnumerateFiles(Settings.Settings.StatsPath, "PartialStats_*.xml", + SearchOption.TopDirectoryOnly); + + foreach(string statsFile in statsFiles) + try + { + if(!File.Exists(statsFile)) + continue; + + var stats = new Stats(); + + // This can execute before debug console has been inited + #if DEBUG + System.Console.WriteLine("Uploading partial statistics file {0}", statsFile); + #else + Aaru.Console.AaruConsole.DebugWriteLine("Submit stats", "Uploading partial statistics file {0}", statsFile); + #endif + + var fs = new FileStream(statsFile, FileMode.Open, FileAccess.Read); + var xs = new XmlSerializer(stats.GetType()); + xs.Deserialize(fs); // Just to test validity of stats file + fs.Seek(0, SeekOrigin.Begin); + + var request = WebRequest.Create("https://www.aaru.app/api/uploadstats"); + + ((HttpWebRequest)request).UserAgent = + $"Aaru {typeof(CommonTypes.Interop.Version).Assembly.GetName().Version}"; + + request.Method = "POST"; + request.ContentLength = fs.Length; + request.ContentType = "application/xml"; + Stream reqStream = request.GetRequestStream(); + fs.CopyTo(reqStream); + reqStream.Close(); + WebResponse response = request.GetResponse(); + + if(((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) + return; + + Stream data = response.GetResponseStream(); + var reader = new StreamReader(data ?? throw new InvalidOperationException()); + + string responseFromServer = reader.ReadToEnd(); + data.Close(); + response.Close(); + fs.Close(); + + if(responseFromServer == "ok") + File.Delete(statsFile); } catch(WebException) { - // Can't connect to the server, do nothing + // Can't connect to the server, postpone til next try + break; } - catch(DbUpdateConcurrencyException) - { - // Ignore db concurrency errors - } - - // ReSharper disable once RedundantCatchClause catch { #if DEBUG + if(!Debugger.IsAttached) + continue; + _submitStatsLock = false; - if(Debugger.IsAttached) - throw; + throw; #endif } - IEnumerable statsFiles = - Directory.EnumerateFiles(Settings.Settings.StatsPath, "PartialStats_*.xml", - SearchOption.TopDirectoryOnly); + _submitStatsLock = false; + }); - foreach(string statsFile in statsFiles) - try - { - if(!File.Exists(statsFile)) - continue; + submitThread.Start(); + } - var stats = new Stats(); + /// Adds the execution of a command to statistics + /// Command + public static void AddCommand(string command) + { + if(string.IsNullOrWhiteSpace(command)) + return; - // This can execute before debug console has been inited - #if DEBUG - System.Console.WriteLine("Uploading partial statistics file {0}", statsFile); - #else - Aaru.Console.AaruConsole.DebugWriteLine("Submit stats", "Uploading partial statistics file {0}", statsFile); - #endif + if(Settings.Settings.Current.Stats == null || + !Settings.Settings.Current.Stats.DeviceStats) + return; - var fs = new FileStream(statsFile, FileMode.Open, FileAccess.Read); - var xs = new XmlSerializer(stats.GetType()); - xs.Deserialize(fs); // Just to test validity of stats file - fs.Seek(0, SeekOrigin.Begin); + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - var request = WebRequest.Create("https://www.aaru.app/api/uploadstats"); - - ((HttpWebRequest)request).UserAgent = - $"Aaru {typeof(CommonTypes.Interop.Version).Assembly.GetName().Version}"; - - request.Method = "POST"; - request.ContentLength = fs.Length; - request.ContentType = "application/xml"; - Stream reqStream = request.GetRequestStream(); - fs.CopyTo(reqStream); - reqStream.Close(); - WebResponse response = request.GetResponse(); - - if(((HttpWebResponse)response).StatusCode != HttpStatusCode.OK) - return; - - Stream data = response.GetResponseStream(); - var reader = new StreamReader(data ?? throw new InvalidOperationException()); - - string responseFromServer = reader.ReadToEnd(); - data.Close(); - response.Close(); - fs.Close(); - - if(responseFromServer == "ok") - File.Delete(statsFile); - } - catch(WebException) - { - // Can't connect to the server, postpone til next try - break; - } - catch - { - #if DEBUG - if(!Debugger.IsAttached) - continue; - - _submitStatsLock = false; - - throw; - #endif - } - - _submitStatsLock = false; - }); - - submitThread.Start(); - } - - /// Adds the execution of a command to statistics - /// Command - public static void AddCommand(string command) + ctx.Commands.Add(new Command { - if(string.IsNullOrWhiteSpace(command)) - return; + Name = command, + Synchronized = false, + Count = 1 + }); - if(Settings.Settings.Current.Stats == null || - !Settings.Settings.Current.Stats.DeviceStats) - return; - - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - ctx.Commands.Add(new Command - { - Name = command, - Synchronized = false, - Count = 1 - }); - - try - { - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } - } - - /// Adds a new filesystem to statistics - /// Filesystem name - public static void AddFilesystem(string filesystem) + try { - if(string.IsNullOrWhiteSpace(filesystem)) - return; - - if(Settings.Settings.Current.Stats == null || - !Settings.Settings.Current.Stats.FilesystemStats) - return; - - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - ctx.Filesystems.Add(new Filesystem - { - Name = filesystem, - Synchronized = false, - Count = 1 - }); - - try - { - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } + ctx.SaveChanges(); } - - /// Adds a new partition scheme to statistics - /// Partition scheme name - internal static void AddPartition(string partition) + catch(SqliteException ex) { - if(string.IsNullOrWhiteSpace(partition)) - return; - - if(Settings.Settings.Current.Stats == null || - !Settings.Settings.Current.Stats.PartitionStats) - return; - - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - ctx.Partitions.Add(new Partition - { - Name = partition, - Synchronized = false, - Count = 1 - }); - - try - { - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); } + } - /// Adds a new filter to statistics - /// Filter name - public static void AddFilter(string filter) + /// Adds a new filesystem to statistics + /// Filesystem name + public static void AddFilesystem(string filesystem) + { + if(string.IsNullOrWhiteSpace(filesystem)) + return; + + if(Settings.Settings.Current.Stats == null || + !Settings.Settings.Current.Stats.FilesystemStats) + return; + + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + ctx.Filesystems.Add(new Filesystem { - if(string.IsNullOrWhiteSpace(filter)) - return; + Name = filesystem, + Synchronized = false, + Count = 1 + }); - if(Settings.Settings.Current.Stats == null || - !Settings.Settings.Current.Stats.FilterStats) - return; - - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - ctx.Filters.Add(new Filter - { - Name = filter, - Synchronized = false, - Count = 1 - }); - - try - { - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } + try + { + ctx.SaveChanges(); } - - /// Ads a new media image to statistics - /// Media image name - public static void AddMediaFormat(string format) + catch(SqliteException ex) { - if(string.IsNullOrWhiteSpace(format)) - return; - - if(Settings.Settings.Current.Stats == null || - !Settings.Settings.Current.Stats.MediaImageStats) - return; - - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - ctx.MediaFormats.Add(new MediaFormat - { - Name = format, - Synchronized = false, - Count = 1 - }); - - try - { - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); } + } - /// Adds a new device to statistics - /// Device - public static void AddDevice(Device dev) + /// Adds a new partition scheme to statistics + /// Partition scheme name + internal static void AddPartition(string partition) + { + if(string.IsNullOrWhiteSpace(partition)) + return; + + if(Settings.Settings.Current.Stats == null || + !Settings.Settings.Current.Stats.PartitionStats) + return; + + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + ctx.Partitions.Add(new Partition { - if(Settings.Settings.Current.Stats == null || - !Settings.Settings.Current.Stats.DeviceStats) - return; + Name = partition, + Synchronized = false, + Count = 1 + }); - string deviceBus; - - if(dev.IsUsb) - deviceBus = "USB"; - else if(dev.IsFireWire) - deviceBus = "FireWire"; - else - deviceBus = dev.Type.ToString(); - - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - if(ctx.SeenDevices.Any(d => d.Manufacturer == dev.Manufacturer && d.Model == dev.Model && - d.Revision == dev.FirmwareRevision && d.Bus == deviceBus)) - return; - - ctx.SeenDevices.Add(new DeviceStat - { - Bus = deviceBus, - Manufacturer = dev.Manufacturer, - Model = dev.Model, - Revision = dev.FirmwareRevision, - Synchronized = false - }); - - try - { - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } + try + { + ctx.SaveChanges(); } - - /// Adds a new media type to statistics - /// Media type - /// Set if media was found on a real device, otherwise found on a media image - public static void AddMedia(MediaType type, bool real) + catch(SqliteException ex) { - if(Settings.Settings.Current.Stats == null || - !Settings.Settings.Current.Stats.MediaStats) - return; - - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - ctx.Medias.Add(new Database.Models.Media - { - Real = real, - Synchronized = false, - Type = type.ToString(), - Count = 1 - }); - - try - { - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); } + } - /// Adds a new remote to statistics - public static void AddRemote(string serverApplication, string serverVersion, string serverOperatingSystem, - string serverOperatingSystemVersion, string serverArchitecture) + /// Adds a new filter to statistics + /// Filter name + public static void AddFilter(string filter) + { + if(string.IsNullOrWhiteSpace(filter)) + return; + + if(Settings.Settings.Current.Stats == null || + !Settings.Settings.Current.Stats.FilterStats) + return; + + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + ctx.Filters.Add(new Filter { - if(Settings.Settings.Current.Stats == null || - !Settings.Settings.Current.Stats.MediaStats) - return; + Name = filter, + Synchronized = false, + Count = 1 + }); - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + try + { + ctx.SaveChanges(); + } + catch(SqliteException ex) + { + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); + } + } - ctx.RemoteApplications.Add(new RemoteApplication - { - Count = 1, - Name = serverApplication, - Synchronized = false, - Version = serverVersion - }); + /// Ads a new media image to statistics + /// Media image name + public static void AddMediaFormat(string format) + { + if(string.IsNullOrWhiteSpace(format)) + return; - ctx.RemoteArchitectures.Add(new RemoteArchitecture - { - Count = 1, - Name = serverArchitecture, - Synchronized = false - }); + if(Settings.Settings.Current.Stats == null || + !Settings.Settings.Current.Stats.MediaImageStats) + return; - ctx.RemoteOperatingSystems.Add(new RemoteOperatingSystem - { - Count = 1, - Name = serverOperatingSystem, - Synchronized = false, - Version = serverOperatingSystemVersion - }); + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - try - { - ctx.SaveChanges(); - } - catch(SqliteException ex) - { - AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); - AaruConsole.DebugWriteLine("Stats", "{0}", ex); - } + ctx.MediaFormats.Add(new MediaFormat + { + Name = format, + Synchronized = false, + Count = 1 + }); + + try + { + ctx.SaveChanges(); + } + catch(SqliteException ex) + { + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); + } + } + + /// Adds a new device to statistics + /// Device + public static void AddDevice(Device dev) + { + if(Settings.Settings.Current.Stats == null || + !Settings.Settings.Current.Stats.DeviceStats) + return; + + string deviceBus; + + if(dev.IsUsb) + deviceBus = "USB"; + else if(dev.IsFireWire) + deviceBus = "FireWire"; + else + deviceBus = dev.Type.ToString(); + + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + if(ctx.SeenDevices.Any(d => d.Manufacturer == dev.Manufacturer && d.Model == dev.Model && + d.Revision == dev.FirmwareRevision && d.Bus == deviceBus)) + return; + + ctx.SeenDevices.Add(new DeviceStat + { + Bus = deviceBus, + Manufacturer = dev.Manufacturer, + Model = dev.Model, + Revision = dev.FirmwareRevision, + Synchronized = false + }); + + try + { + ctx.SaveChanges(); + } + catch(SqliteException ex) + { + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); + } + } + + /// Adds a new media type to statistics + /// Media type + /// Set if media was found on a real device, otherwise found on a media image + public static void AddMedia(MediaType type, bool real) + { + if(Settings.Settings.Current.Stats == null || + !Settings.Settings.Current.Stats.MediaStats) + return; + + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + ctx.Medias.Add(new Database.Models.Media + { + Real = real, + Synchronized = false, + Type = type.ToString(), + Count = 1 + }); + + try + { + ctx.SaveChanges(); + } + catch(SqliteException ex) + { + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); + } + } + + /// Adds a new remote to statistics + public static void AddRemote(string serverApplication, string serverVersion, string serverOperatingSystem, + string serverOperatingSystemVersion, string serverArchitecture) + { + if(Settings.Settings.Current.Stats == null || + !Settings.Settings.Current.Stats.MediaStats) + return; + + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + ctx.RemoteApplications.Add(new RemoteApplication + { + Count = 1, + Name = serverApplication, + Synchronized = false, + Version = serverVersion + }); + + ctx.RemoteArchitectures.Add(new RemoteArchitecture + { + Count = 1, + Name = serverArchitecture, + Synchronized = false + }); + + ctx.RemoteOperatingSystems.Add(new RemoteOperatingSystem + { + Count = 1, + Name = serverOperatingSystem, + Synchronized = false, + Version = serverOperatingSystemVersion + }); + + try + { + ctx.SaveChanges(); + } + catch(SqliteException ex) + { + AaruConsole.DebugWriteLine("Stats", "Exception while trying to save statistics:"); + AaruConsole.DebugWriteLine("Stats", "{0}", ex); } } } \ No newline at end of file diff --git a/Aaru.Database/ContextFactory.cs b/Aaru.Database/ContextFactory.cs index 090c02a69..ec39e035e 100644 --- a/Aaru.Database/ContextFactory.cs +++ b/Aaru.Database/ContextFactory.cs @@ -32,16 +32,15 @@ using Microsoft.EntityFrameworkCore.Design; -namespace Aaru.Database +namespace Aaru.Database; + +/// +/// Database context factory, for design time +public class AaruContextFactory : IDesignTimeDbContextFactory { /// - /// Database context factory, for design time - public class AaruContextFactory : IDesignTimeDbContextFactory - { - /// - /// Creates a database context - /// Ignored parameters - /// A database context - public AaruContext CreateDbContext(string[] args) => AaruContext.Create("aaru.db"); - } + /// Creates a database context + /// Ignored parameters + /// A database context + public AaruContext CreateDbContext(string[] args) => AaruContext.Create("aaru.db"); } \ No newline at end of file diff --git a/Aaru.Database/Models/BaseModel.cs b/Aaru.Database/Models/BaseModel.cs index 02189e377..4aaf1388b 100644 --- a/Aaru.Database/Models/BaseModel.cs +++ b/Aaru.Database/Models/BaseModel.cs @@ -32,13 +32,12 @@ using System.ComponentModel.DataAnnotations; -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// Base database model +public abstract class BaseModel { - /// Base database model - public abstract class BaseModel - { - /// Database ID - [Key] - public int Id { get; set; } - } + /// Database ID + [Key] + public int Id { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/BaseOperatingSystem.cs b/Aaru.Database/Models/BaseOperatingSystem.cs index 3ddecfe0e..307403f74 100644 --- a/Aaru.Database/Models/BaseOperatingSystem.cs +++ b/Aaru.Database/Models/BaseOperatingSystem.cs @@ -30,19 +30,18 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// +/// Operating system statistics +public abstract class BaseOperatingSystem : BaseModel { - /// - /// Operating system statistics - public abstract class BaseOperatingSystem : BaseModel - { - /// Operating system name - public string Name { get; set; } - /// Operating system version - public string Version { get; set; } - /// Has already been synchronized with Aaru's server - public bool Synchronized { get; set; } - /// Statistical count - public ulong Count { get; set; } - } + /// Operating system name + public string Name { get; set; } + /// Operating system version + public string Version { get; set; } + /// Has already been synchronized with Aaru's server + public bool Synchronized { get; set; } + /// Statistical count + public ulong Count { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/CdOffset.cs b/Aaru.Database/Models/CdOffset.cs index 5433e1605..266f1b99c 100644 --- a/Aaru.Database/Models/CdOffset.cs +++ b/Aaru.Database/Models/CdOffset.cs @@ -32,48 +32,47 @@ using System; -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// +/// CD read offset +public class CdOffset : CommonTypes.Metadata.CdOffset { - /// - /// CD read offset - public class CdOffset : CommonTypes.Metadata.CdOffset + /// Builds an empty CD read offset + public CdOffset() {} + + /// Builds a CD read offset with the specified parameters + /// Manufacturer + /// Model + /// Read offset + /// Number of submissions + /// Percentage of agreement of submissions + public CdOffset(string manufacturer, string model, short offset, int submissions, float agreement) { - /// Builds an empty CD read offset - public CdOffset() {} - - /// Builds a CD read offset with the specified parameters - /// Manufacturer - /// Model - /// Read offset - /// Number of submissions - /// Percentage of agreement of submissions - public CdOffset(string manufacturer, string model, short offset, int submissions, float agreement) - { - Manufacturer = manufacturer; - Model = model; - Offset = offset; - Submissions = submissions; - Agreement = agreement; - AddedWhen = ModifiedWhen = DateTime.UtcNow; - } - - /// Builds a CD read offset from the metadata type - /// Read offset metadata - public CdOffset(CommonTypes.Metadata.CdOffset offset) - { - Manufacturer = offset.Manufacturer; - Model = offset.Model; - Offset = offset.Offset; - Submissions = offset.Submissions; - Agreement = offset.Agreement; - AddedWhen = ModifiedWhen = DateTime.UtcNow; - } - - /// Database ID - public int Id { get; set; } - /// Date when model has been added to the database - public DateTime AddedWhen { get; set; } - /// Date when model was last modified - public DateTime ModifiedWhen { get; set; } + Manufacturer = manufacturer; + Model = model; + Offset = offset; + Submissions = submissions; + Agreement = agreement; + AddedWhen = ModifiedWhen = DateTime.UtcNow; } + + /// Builds a CD read offset from the metadata type + /// Read offset metadata + public CdOffset(CommonTypes.Metadata.CdOffset offset) + { + Manufacturer = offset.Manufacturer; + Model = offset.Model; + Offset = offset.Offset; + Submissions = offset.Submissions; + Agreement = offset.Agreement; + AddedWhen = ModifiedWhen = DateTime.UtcNow; + } + + /// Database ID + public int Id { get; set; } + /// Date when model has been added to the database + public DateTime AddedWhen { get; set; } + /// Date when model was last modified + public DateTime ModifiedWhen { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/Command.cs b/Aaru.Database/Models/Command.cs index 9100145b7..d280bfc12 100644 --- a/Aaru.Database/Models/Command.cs +++ b/Aaru.Database/Models/Command.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Command statistics. - public class Command : NameCountModel {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Command statistics. +public class Command : NameCountModel {} \ No newline at end of file diff --git a/Aaru.Database/Models/Device.cs b/Aaru.Database/Models/Device.cs index 1b3b2a915..605b1fa05 100644 --- a/Aaru.Database/Models/Device.cs +++ b/Aaru.Database/Models/Device.cs @@ -35,45 +35,44 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using Aaru.CommonTypes.Metadata; -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// Known device +public class Device : DeviceReportV2 { - /// Known device - public class Device : DeviceReportV2 + /// Builds an empty device + public Device() => LastSynchronized = DateTime.UtcNow; + + /// Builds a device from a device report + /// Device report + [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] + public Device(DeviceReportV2 report) { - /// Builds an empty device - public Device() => LastSynchronized = DateTime.UtcNow; - - /// Builds a device from a device report - /// Device report - [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] - public Device(DeviceReportV2 report) - { - ATA = report.ATA; - ATAPI = report.ATAPI; - CompactFlash = report.CompactFlash; - FireWire = report.FireWire; - LastSynchronized = DateTime.UtcNow; - MultiMediaCard = report.MultiMediaCard; - PCMCIA = report.PCMCIA; - SCSI = report.SCSI; - SecureDigital = report.SecureDigital; - USB = report.USB; - Manufacturer = report.Manufacturer; - Model = report.Model; - Revision = report.Revision; - Type = report.Type; - GdRomSwapDiscCapabilities = report.GdRomSwapDiscCapabilities; - } - - /// When this known device was last synchronized with the server - public DateTime LastSynchronized { get; set; } - - /// Optimal number of blocks to read at once - [DefaultValue(0)] - public int OptimalMultipleSectorsRead { get; set; } - - /// Can read GD-ROM using swap trick? - [DefaultValue(null)] - public bool? CanReadGdRomUsingSwapDisc { get; set; } + ATA = report.ATA; + ATAPI = report.ATAPI; + CompactFlash = report.CompactFlash; + FireWire = report.FireWire; + LastSynchronized = DateTime.UtcNow; + MultiMediaCard = report.MultiMediaCard; + PCMCIA = report.PCMCIA; + SCSI = report.SCSI; + SecureDigital = report.SecureDigital; + USB = report.USB; + Manufacturer = report.Manufacturer; + Model = report.Model; + Revision = report.Revision; + Type = report.Type; + GdRomSwapDiscCapabilities = report.GdRomSwapDiscCapabilities; } + + /// When this known device was last synchronized with the server + public DateTime LastSynchronized { get; set; } + + /// Optimal number of blocks to read at once + [DefaultValue(0)] + public int OptimalMultipleSectorsRead { get; set; } + + /// Can read GD-ROM using swap trick? + [DefaultValue(null)] + public bool? CanReadGdRomUsingSwapDisc { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/DeviceStat.cs b/Aaru.Database/Models/DeviceStat.cs index 719981925..3ec61b6cb 100644 --- a/Aaru.Database/Models/DeviceStat.cs +++ b/Aaru.Database/Models/DeviceStat.cs @@ -30,21 +30,20 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// +/// Device found in usage +public class DeviceStat : BaseModel { - /// - /// Device found in usage - public class DeviceStat : BaseModel - { - /// Manufacturer - public string Manufacturer { get; set; } - /// Model - public string Model { get; set; } - /// Revision or firmware version - public string Revision { get; set; } - /// Bus - public string Bus { get; set; } - /// Has already been synchronized with Aaru's server - public bool Synchronized { get; set; } - } + /// Manufacturer + public string Manufacturer { get; set; } + /// Model + public string Model { get; set; } + /// Revision or firmware version + public string Revision { get; set; } + /// Bus + public string Bus { get; set; } + /// Has already been synchronized with Aaru's server + public bool Synchronized { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/Filesystem.cs b/Aaru.Database/Models/Filesystem.cs index 275ab9447..516c52330 100644 --- a/Aaru.Database/Models/Filesystem.cs +++ b/Aaru.Database/Models/Filesystem.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Filesystem found - public class Filesystem : NameCountModel {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Filesystem found +public class Filesystem : NameCountModel {} \ No newline at end of file diff --git a/Aaru.Database/Models/Filter.cs b/Aaru.Database/Models/Filter.cs index 44749399c..082f44285 100644 --- a/Aaru.Database/Models/Filter.cs +++ b/Aaru.Database/Models/Filter.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Filter used - public class Filter : NameCountModel {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Filter used +public class Filter : NameCountModel {} \ No newline at end of file diff --git a/Aaru.Database/Models/Media.cs b/Aaru.Database/Models/Media.cs index 9e7256ed4..9bf096d47 100644 --- a/Aaru.Database/Models/Media.cs +++ b/Aaru.Database/Models/Media.cs @@ -30,19 +30,18 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// +/// Media type found +public class Media : BaseModel { - /// - /// Media type found - public class Media : BaseModel - { - /// Media type name - public string Type { get; set; } - /// Found physically, or in image - public bool Real { get; set; } - /// Has already been synchronized with Aaru's server - public bool Synchronized { get; set; } - /// Count of times found - public ulong Count { get; set; } - } + /// Media type name + public string Type { get; set; } + /// Found physically, or in image + public bool Real { get; set; } + /// Has already been synchronized with Aaru's server + public bool Synchronized { get; set; } + /// Count of times found + public ulong Count { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/MediaFormat.cs b/Aaru.Database/Models/MediaFormat.cs index a1f99e026..804344375 100644 --- a/Aaru.Database/Models/MediaFormat.cs +++ b/Aaru.Database/Models/MediaFormat.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Media image format - public class MediaFormat : NameCountModel {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Media image format +public class MediaFormat : NameCountModel {} \ No newline at end of file diff --git a/Aaru.Database/Models/NameCountModel.cs b/Aaru.Database/Models/NameCountModel.cs index c2b91620c..6d0d0c90b 100644 --- a/Aaru.Database/Models/NameCountModel.cs +++ b/Aaru.Database/Models/NameCountModel.cs @@ -30,17 +30,16 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// +/// Model for name-count values. +public abstract class NameCountModel : BaseModel { - /// - /// Model for name-count values. - public abstract class NameCountModel : BaseModel - { - /// Value name - public string Name { get; set; } - /// Has already been synchronized with Aaru's server - public bool Synchronized { get; set; } - /// Value count - public ulong Count { get; set; } - } + /// Value name + public string Name { get; set; } + /// Has already been synchronized with Aaru's server + public bool Synchronized { get; set; } + /// Value count + public ulong Count { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/OperatingSystem.cs b/Aaru.Database/Models/OperatingSystem.cs index 58387620d..a15980f4e 100644 --- a/Aaru.Database/Models/OperatingSystem.cs +++ b/Aaru.Database/Models/OperatingSystem.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Operating system - public class OperatingSystem : BaseOperatingSystem {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Operating system +public class OperatingSystem : BaseOperatingSystem {} \ No newline at end of file diff --git a/Aaru.Database/Models/Partition.cs b/Aaru.Database/Models/Partition.cs index 082b69008..15919a6ef 100644 --- a/Aaru.Database/Models/Partition.cs +++ b/Aaru.Database/Models/Partition.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Partitioning scheme - public class Partition : NameCountModel {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Partitioning scheme +public class Partition : NameCountModel {} \ No newline at end of file diff --git a/Aaru.Database/Models/RemoteApplication.cs b/Aaru.Database/Models/RemoteApplication.cs index 641b38cf6..a42bc609b 100644 --- a/Aaru.Database/Models/RemoteApplication.cs +++ b/Aaru.Database/Models/RemoteApplication.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Remote application - public class RemoteApplication : BaseOperatingSystem {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Remote application +public class RemoteApplication : BaseOperatingSystem {} \ No newline at end of file diff --git a/Aaru.Database/Models/RemoteArchitecture.cs b/Aaru.Database/Models/RemoteArchitecture.cs index 814a7667c..6aa2556a5 100644 --- a/Aaru.Database/Models/RemoteArchitecture.cs +++ b/Aaru.Database/Models/RemoteArchitecture.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Remote architecture - public class RemoteArchitecture : NameCountModel {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Remote architecture +public class RemoteArchitecture : NameCountModel {} \ No newline at end of file diff --git a/Aaru.Database/Models/RemoteOperatingSystem.cs b/Aaru.Database/Models/RemoteOperatingSystem.cs index dbe42fe25..56a8be39b 100644 --- a/Aaru.Database/Models/RemoteOperatingSystem.cs +++ b/Aaru.Database/Models/RemoteOperatingSystem.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Remote operating system - public class RemoteOperatingSystem : BaseOperatingSystem {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Remote operating system +public class RemoteOperatingSystem : BaseOperatingSystem {} \ No newline at end of file diff --git a/Aaru.Database/Models/Report.cs b/Aaru.Database/Models/Report.cs index 4d075a3c2..6c4fb5486 100644 --- a/Aaru.Database/Models/Report.cs +++ b/Aaru.Database/Models/Report.cs @@ -34,43 +34,42 @@ using System; using System.Diagnostics.CodeAnalysis; using Aaru.CommonTypes.Metadata; -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// Device report +public class Report : DeviceReportV2 { - /// Device report - public class Report : DeviceReportV2 + /// Builds an empty device report + public Report() { - /// Builds an empty device report - public Report() - { - Created = DateTime.UtcNow; - Uploaded = false; - } - - /// Builds a device report model from a device report - /// Device report - [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] - public Report(DeviceReportV2 report) - { - ATA = report.ATA; - ATAPI = report.ATAPI; - CompactFlash = report.CompactFlash; - FireWire = report.FireWire; - Created = DateTime.UtcNow; - MultiMediaCard = report.MultiMediaCard; - PCMCIA = report.PCMCIA; - SCSI = report.SCSI; - SecureDigital = report.SecureDigital; - USB = report.USB; - Uploaded = false; - Manufacturer = report.Manufacturer; - Model = report.Model; - Revision = report.Revision; - Type = report.Type; - } - - /// Date when the device report was created - public DateTime Created { get; set; } - /// If this model has already been upload - public bool Uploaded { get; set; } + Created = DateTime.UtcNow; + Uploaded = false; } + + /// Builds a device report model from a device report + /// Device report + [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] + public Report(DeviceReportV2 report) + { + ATA = report.ATA; + ATAPI = report.ATAPI; + CompactFlash = report.CompactFlash; + FireWire = report.FireWire; + Created = DateTime.UtcNow; + MultiMediaCard = report.MultiMediaCard; + PCMCIA = report.PCMCIA; + SCSI = report.SCSI; + SecureDigital = report.SecureDigital; + USB = report.USB; + Uploaded = false; + Manufacturer = report.Manufacturer; + Model = report.Model; + Revision = report.Revision; + Type = report.Type; + } + + /// Date when the device report was created + public DateTime Created { get; set; } + /// If this model has already been upload + public bool Uploaded { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/UsbProduct.cs b/Aaru.Database/Models/UsbProduct.cs index ef430157a..9b05b8fa1 100644 --- a/Aaru.Database/Models/UsbProduct.cs +++ b/Aaru.Database/Models/UsbProduct.cs @@ -33,40 +33,39 @@ using System; using System.ComponentModel.DataAnnotations; -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// USB product +public class UsbProduct { - /// USB product - public class UsbProduct + /// Builds an empty USB product + public UsbProduct() {} + + /// Builds a USB product with the specified parameters + /// Vendor ID + /// Product ID + /// Product name + public UsbProduct(ushort vendorId, ushort id, string product) { - /// Builds an empty USB product - public UsbProduct() {} - - /// Builds a USB product with the specified parameters - /// Vendor ID - /// Product ID - /// Product name - public UsbProduct(ushort vendorId, ushort id, string product) - { - VendorId = vendorId; - ProductId = id; - Product = product; - AddedWhen = ModifiedWhen = DateTime.UtcNow; - } - - /// Database ID - [Key] - public int Id { get; set; } - /// Product ID - public ushort ProductId { get; set; } - /// Product name - public string Product { get; set; } - /// Date when model has been added to the database - public DateTime AddedWhen { get; set; } - /// Date when model was last modified - public DateTime ModifiedWhen { get; set; } - /// USB vendor ID - public ushort VendorId { get; set; } - /// Database link to USB vendor - public virtual UsbVendor Vendor { get; set; } + VendorId = vendorId; + ProductId = id; + Product = product; + AddedWhen = ModifiedWhen = DateTime.UtcNow; } + + /// Database ID + [Key] + public int Id { get; set; } + /// Product ID + public ushort ProductId { get; set; } + /// Product name + public string Product { get; set; } + /// Date when model has been added to the database + public DateTime AddedWhen { get; set; } + /// Date when model was last modified + public DateTime ModifiedWhen { get; set; } + /// USB vendor ID + public ushort VendorId { get; set; } + /// Database link to USB vendor + public virtual UsbVendor Vendor { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/UsbVendor.cs b/Aaru.Database/Models/UsbVendor.cs index 0246428f7..253826ee1 100644 --- a/Aaru.Database/Models/UsbVendor.cs +++ b/Aaru.Database/Models/UsbVendor.cs @@ -34,35 +34,34 @@ using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; -namespace Aaru.Database.Models +namespace Aaru.Database.Models; + +/// USB vendor +public class UsbVendor { - /// USB vendor - public class UsbVendor + /// Builds an empty USB vendor + public UsbVendor() {} + + /// Builds a USB vendor with the specified parameters + /// Vendor ID + /// Vendor name + public UsbVendor(ushort id, string vendor) { - /// Builds an empty USB vendor - public UsbVendor() {} - - /// Builds a USB vendor with the specified parameters - /// Vendor ID - /// Vendor name - public UsbVendor(ushort id, string vendor) - { - Id = id; - Vendor = vendor; - AddedWhen = ModifiedWhen = DateTime.UtcNow; - } - - /// Database ID - [Key] - public ushort Id { get; set; } - /// Vendor name - public string Vendor { get; set; } - /// Date when model has been added to the database - public DateTime AddedWhen { get; set; } - /// Date when model was last modified - public DateTime ModifiedWhen { get; set; } - - /// List of products from this vendor - public virtual ICollection Products { get; set; } + Id = id; + Vendor = vendor; + AddedWhen = ModifiedWhen = DateTime.UtcNow; } + + /// Database ID + [Key] + public ushort Id { get; set; } + /// Vendor name + public string Vendor { get; set; } + /// Date when model has been added to the database + public DateTime AddedWhen { get; set; } + /// Date when model was last modified + public DateTime ModifiedWhen { get; set; } + + /// List of products from this vendor + public virtual ICollection Products { get; set; } } \ No newline at end of file diff --git a/Aaru.Database/Models/Version.cs b/Aaru.Database/Models/Version.cs index 118263d3e..09f070fca 100644 --- a/Aaru.Database/Models/Version.cs +++ b/Aaru.Database/Models/Version.cs @@ -30,9 +30,8 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Database.Models -{ - /// - /// Aaru version - public class Version : NameCountModel {} -} \ No newline at end of file +namespace Aaru.Database.Models; + +/// +/// Aaru version +public class Version : NameCountModel {} \ No newline at end of file diff --git a/Aaru.Devices/Command.cs b/Aaru.Devices/Command.cs index 84819dd07..b3e349e7e 100644 --- a/Aaru.Devices/Command.cs +++ b/Aaru.Devices/Command.cs @@ -39,407 +39,406 @@ using PlatformID = Aaru.CommonTypes.Interop.PlatformID; // ReSharper disable UnusedMember.Global -namespace Aaru.Devices +namespace Aaru.Devices; + +internal static class Command { - internal static class Command + /// Sends a SCSI command + /// 0 if no error occurred, otherwise, errno + /// File handle + /// SCSI CDB + /// Buffer for SCSI command response + /// Buffer with the SCSI sense + /// Timeout in seconds + /// SCSI command transfer direction + /// Time it took to execute the command in milliseconds + /// + /// True if SCSI error returned non-OK status and contains SCSI + /// sense + /// + /// If the specified platform is not supported + internal static int SendScsiCommand(object fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, + uint timeout, ScsiDirection direction, out double duration, out bool sense) { - /// Sends a SCSI command - /// 0 if no error occurred, otherwise, errno - /// File handle - /// SCSI CDB - /// Buffer for SCSI command response - /// Buffer with the SCSI sense - /// Timeout in seconds - /// SCSI command transfer direction - /// Time it took to execute the command in milliseconds - /// - /// True if SCSI error returned non-OK status and contains SCSI - /// sense - /// - /// If the specified platform is not supported - internal static int SendScsiCommand(object fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, - uint timeout, ScsiDirection direction, out double duration, out bool sense) - { - PlatformID ptId = DetectOS.GetRealPlatformID(); + PlatformID ptId = DetectOS.GetRealPlatformID(); - return SendScsiCommand(ptId, fd, cdb, ref buffer, out senseBuffer, timeout, direction, out duration, - out sense); - } + return SendScsiCommand(ptId, fd, cdb, ref buffer, out senseBuffer, timeout, direction, out duration, + out sense); + } - /// Sends a SCSI command - /// 0 if no error occurred, otherwise, errno - /// Platform ID for executing the command - /// File handle - /// SCSI CDB - /// Buffer for SCSI command response - /// Buffer with the SCSI sense - /// Timeout in seconds - /// SCSI command transfer direction - /// Time it took to execute the command in milliseconds - /// - /// True if SCSI error returned non-OK status and contains SCSI - /// sense - /// - /// If the specified platform is not supported - internal static int SendScsiCommand(PlatformID ptId, object fd, byte[] cdb, ref byte[] buffer, - out byte[] senseBuffer, uint timeout, ScsiDirection direction, - out double duration, out bool sense) + /// Sends a SCSI command + /// 0 if no error occurred, otherwise, errno + /// Platform ID for executing the command + /// File handle + /// SCSI CDB + /// Buffer for SCSI command response + /// Buffer with the SCSI sense + /// Timeout in seconds + /// SCSI command transfer direction + /// Time it took to execute the command in milliseconds + /// + /// True if SCSI error returned non-OK status and contains SCSI + /// sense + /// + /// If the specified platform is not supported + internal static int SendScsiCommand(PlatformID ptId, object fd, byte[] cdb, ref byte[] buffer, + out byte[] senseBuffer, uint timeout, ScsiDirection direction, + out double duration, out bool sense) + { + switch(ptId) { - switch(ptId) + case PlatformID.Win32NT: { - case PlatformID.Win32NT: + ScsiIoctlDirection dir; + + switch(direction) { - ScsiIoctlDirection dir; + case ScsiDirection.In: + dir = ScsiIoctlDirection.In; - switch(direction) - { - case ScsiDirection.In: - dir = ScsiIoctlDirection.In; + break; + case ScsiDirection.Out: + dir = ScsiIoctlDirection.Out; - break; - case ScsiDirection.Out: - dir = ScsiIoctlDirection.Out; + break; + default: + dir = ScsiIoctlDirection.Unspecified; - break; - default: - dir = ScsiIoctlDirection.Unspecified; - - break; - } - - return Windows.Command.SendScsiCommand((SafeFileHandle)fd, cdb, ref buffer, out senseBuffer, - timeout, dir, out duration, out sense); + break; } - case PlatformID.Linux: + + return Windows.Command.SendScsiCommand((SafeFileHandle)fd, cdb, ref buffer, out senseBuffer, + timeout, dir, out duration, out sense); + } + case PlatformID.Linux: + { + Linux.ScsiIoctlDirection dir; + + switch(direction) { - Linux.ScsiIoctlDirection dir; + case ScsiDirection.In: + dir = Linux.ScsiIoctlDirection.In; - switch(direction) - { - case ScsiDirection.In: - dir = Linux.ScsiIoctlDirection.In; + break; + case ScsiDirection.Out: + dir = Linux.ScsiIoctlDirection.Out; - break; - case ScsiDirection.Out: - dir = Linux.ScsiIoctlDirection.Out; + break; + case ScsiDirection.Bidirectional: + dir = Linux.ScsiIoctlDirection.Unspecified; - break; - case ScsiDirection.Bidirectional: - dir = Linux.ScsiIoctlDirection.Unspecified; + break; + case ScsiDirection.None: + dir = Linux.ScsiIoctlDirection.None; - break; - case ScsiDirection.None: - dir = Linux.ScsiIoctlDirection.None; + break; + default: + dir = Linux.ScsiIoctlDirection.Unknown; - break; - default: - dir = Linux.ScsiIoctlDirection.Unknown; - - break; - } - - return Linux.Command.SendScsiCommand((int)fd, cdb, ref buffer, out senseBuffer, timeout, dir, - out duration, out sense); + break; } - default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); + + return Linux.Command.SendScsiCommand((int)fd, cdb, ref buffer, out senseBuffer, timeout, dir, + out duration, out sense); } + default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); } + } - /// Sends an ATA command in CHS format - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA returned non-OK status - /// Registers to send to the device - /// Registers returned by the device - /// ATA protocol to use - /// What register contains the transfer length - /// Set to true if the transfer length is in block, otherwise it is in bytes - /// If the specified platform is not supported - internal static int SendAtaCommand(object fd, AtaRegistersChs registers, - out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) + /// Sends an ATA command in CHS format + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA returned non-OK status + /// Registers to send to the device + /// Registers returned by the device + /// ATA protocol to use + /// What register contains the transfer length + /// Set to true if the transfer length is in block, otherwise it is in bytes + /// If the specified platform is not supported + internal static int SendAtaCommand(object fd, AtaRegistersChs registers, + out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + PlatformID ptId = DetectOS.GetRealPlatformID(); + + return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, + timeout, transferBlocks, out duration, out sense); + } + + /// Sends an ATA command in CHS format + /// 0 if no error occurred, otherwise, errno + /// Platform ID for executing the command + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA returned non-OK status + /// Registers to send to the device + /// Registers returned by the device + /// ATA protocol to use + /// What register contains the transfer length + /// Set to true if the transfer length is in block, otherwise it is in bytes + /// If the specified platform is not supported + internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersChs registers, + out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + switch(ptId) { - PlatformID ptId = DetectOS.GetRealPlatformID(); - - return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, - timeout, transferBlocks, out duration, out sense); - } - - /// Sends an ATA command in CHS format - /// 0 if no error occurred, otherwise, errno - /// Platform ID for executing the command - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA returned non-OK status - /// Registers to send to the device - /// Registers returned by the device - /// ATA protocol to use - /// What register contains the transfer length - /// Set to true if the transfer length is in block, otherwise it is in bytes - /// If the specified platform is not supported - internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersChs registers, - out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) - { - switch(ptId) + case PlatformID.Win32NT: { - case PlatformID.Win32NT: - { - if((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1 && - (Environment.OSVersion.ServicePack == "Service Pack 1" || - Environment.OSVersion.ServicePack == "")) || - (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)) - throw new InvalidOperationException("Windows XP or earlier is not supported."); + if((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1 && + (Environment.OSVersion.ServicePack == "Service Pack 1" || + Environment.OSVersion.ServicePack == "")) || + (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)) + throw new InvalidOperationException("Windows XP or earlier is not supported."); - // Windows NT 4 or earlier, requires special ATA pass thru SCSI. But Aaru cannot run there (or can it?) - if(Environment.OSVersion.Version.Major <= 4) - throw new InvalidOperationException("Windows NT 4.0 or earlier is not supported."); + // Windows NT 4 or earlier, requires special ATA pass thru SCSI. But Aaru cannot run there (or can it?) + if(Environment.OSVersion.Version.Major <= 4) + throw new InvalidOperationException("Windows NT 4.0 or earlier is not supported."); - return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, - ref buffer, timeout, out duration, out sense); - } - case PlatformID.Linux: - { - return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, - transferRegister, ref buffer, timeout, transferBlocks, - out duration, out sense); - } - default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); + return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, + ref buffer, timeout, out duration, out sense); } - } - - /// Sends an ATA command in CHS format - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA returned non-OK status - /// Registers to send to the device - /// Registers returned by the device - /// ATA protocol to use - /// What register contains the transfer length - /// Set to true if the transfer length is in block, otherwise it is in bytes - /// If the specified platform is not supported - internal static int SendAtaCommand(object fd, AtaRegistersLba28 registers, - out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) - { - PlatformID ptId = DetectOS.GetRealPlatformID(); - - return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, - timeout, transferBlocks, out duration, out sense); - } - - /// Sends an ATA command in 28-bit LBA format - /// 0 if no error occurred, otherwise, errno - /// Platform ID for executing the command - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA returned non-OK status - /// Registers to send to the device - /// Registers returned by the device - /// ATA protocol to use - /// What register contains the transfer length - /// Set to true if the transfer length is in block, otherwise it is in bytes - /// If the specified platform is not supported - internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersLba28 registers, - out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) - { - switch(ptId) + case PlatformID.Linux: { - case PlatformID.Win32NT: - { - if((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1 && - (Environment.OSVersion.ServicePack == "Service Pack 1" || - Environment.OSVersion.ServicePack == "")) || - (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)) - throw new InvalidOperationException("Windows XP or earlier is not supported."); - - // Windows NT 4 or earlier, requires special ATA pass thru SCSI. But Aaru cannot run there (or can it?) - if(Environment.OSVersion.Version.Major <= 4) - throw new InvalidOperationException("Windows NT 4.0 or earlier is not supported."); - - return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, - ref buffer, timeout, out duration, out sense); - } - case PlatformID.Linux: - { - return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, - transferRegister, ref buffer, timeout, transferBlocks, - out duration, out sense); - } - default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); + return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, + out duration, out sense); } + default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); } + } - /// Sends an ATA command in 48-bit LBA format - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA returned non-OK status - /// Registers to send to the device - /// Registers returned by the device - /// ATA protocol to use - /// What register contains the transfer length - /// Set to true if the transfer length is in block, otherwise it is in bytes - /// If the specified platform is not supported - internal static int SendAtaCommand(object fd, AtaRegistersLba48 registers, - out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) + /// Sends an ATA command in CHS format + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA returned non-OK status + /// Registers to send to the device + /// Registers returned by the device + /// ATA protocol to use + /// What register contains the transfer length + /// Set to true if the transfer length is in block, otherwise it is in bytes + /// If the specified platform is not supported + internal static int SendAtaCommand(object fd, AtaRegistersLba28 registers, + out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + PlatformID ptId = DetectOS.GetRealPlatformID(); + + return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, + timeout, transferBlocks, out duration, out sense); + } + + /// Sends an ATA command in 28-bit LBA format + /// 0 if no error occurred, otherwise, errno + /// Platform ID for executing the command + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA returned non-OK status + /// Registers to send to the device + /// Registers returned by the device + /// ATA protocol to use + /// What register contains the transfer length + /// Set to true if the transfer length is in block, otherwise it is in bytes + /// If the specified platform is not supported + internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersLba28 registers, + out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + switch(ptId) { - PlatformID ptId = DetectOS.GetRealPlatformID(); - - return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, - timeout, transferBlocks, out duration, out sense); - } - - /// Sends an ATA command in 48-bit format - /// 0 if no error occurred, otherwise, errno - /// Platform ID for executing the command - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA returned non-OK status - /// Registers to send to the device - /// Registers returned by the device - /// ATA protocol to use - /// What register contains the transfer length - /// Set to true if the transfer length is in block, otherwise it is in bytes - /// If the specified platform is not supported - internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersLba48 registers, - out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) - { - switch(ptId) + case PlatformID.Win32NT: { - case PlatformID.Win32NT: - // No check for Windows version. A 48-bit ATA disk simply does not work on earlier systems - return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, - ref buffer, timeout, out duration, out sense); - case PlatformID.Linux: - return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, - transferRegister, ref buffer, timeout, transferBlocks, - out duration, out sense); - default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); + if((Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1 && + (Environment.OSVersion.ServicePack == "Service Pack 1" || + Environment.OSVersion.ServicePack == "")) || + (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 0)) + throw new InvalidOperationException("Windows XP or earlier is not supported."); + + // Windows NT 4 or earlier, requires special ATA pass thru SCSI. But Aaru cannot run there (or can it?) + if(Environment.OSVersion.Version.Major <= 4) + throw new InvalidOperationException("Windows NT 4.0 or earlier is not supported."); + + return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, + ref buffer, timeout, out duration, out sense); } - } - - /// Sends a MMC/SD command - /// The result of the command. - /// File handle - /// MMC/SD opcode - /// Buffer for MMC/SD command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if MMC/SD returned non-OK status - /// True if data is sent from host to card - /// True if command should be preceded with CMD55 - /// Flags indicating kind and place of response - /// How many blocks to transfer - /// Command argument - /// Response registers - /// Size of block in bytes - /// If the specified platform is not supported - internal static int SendMmcCommand(object fd, MmcCommands command, bool write, bool isApplication, - MmcFlags flags, uint argument, uint blockSize, uint blocks, - ref byte[] buffer, out uint[] response, out double duration, out bool sense, - uint timeout = 0) - { - PlatformID ptId = DetectOS.GetRealPlatformID(); - - return SendMmcCommand(ptId, (int)fd, command, write, isApplication, flags, argument, blockSize, blocks, - ref buffer, out response, out duration, out sense, timeout); - } - - /// Sends a MMC/SD command - /// The result of the command. - /// Platform ID for executing the command - /// File handle - /// MMC/SD opcode - /// Buffer for MMC/SD command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if MMC/SD returned non-OK status - /// True if data is sent from host to card - /// True if command should be preceded with CMD55 - /// Flags indicating kind and place of response - /// How many blocks to transfer - /// Command argument - /// Response registers - /// Size of block in bytes - /// If the specified platform is not supported - internal static int SendMmcCommand(PlatformID ptId, object fd, MmcCommands command, bool write, - bool isApplication, MmcFlags flags, uint argument, uint blockSize, - uint blocks, ref byte[] buffer, out uint[] response, out double duration, - out bool sense, uint timeout = 0) - { - switch(ptId) + case PlatformID.Linux: { - case PlatformID.Win32NT: - return Windows.Command.SendMmcCommand((SafeFileHandle)fd, command, write, isApplication, flags, - argument, blockSize, blocks, ref buffer, out response, - out duration, out sense, timeout); - case PlatformID.Linux: - return Linux.Command.SendMmcCommand((int)fd, command, write, isApplication, flags, argument, - blockSize, blocks, ref buffer, out response, out duration, - out sense, timeout); - default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); + return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, + out duration, out sense); } + default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); } + } - internal static int SendMultipleMmcCommands(PlatformID ptId, object fd, Device.MmcSingleCommand[] commands, - out double duration, out bool sense, uint timeout = 0) + /// Sends an ATA command in 48-bit LBA format + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA returned non-OK status + /// Registers to send to the device + /// Registers returned by the device + /// ATA protocol to use + /// What register contains the transfer length + /// Set to true if the transfer length is in block, otherwise it is in bytes + /// If the specified platform is not supported + internal static int SendAtaCommand(object fd, AtaRegistersLba48 registers, + out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + PlatformID ptId = DetectOS.GetRealPlatformID(); + + return SendAtaCommand(ptId, fd, registers, out errorRegisters, protocol, transferRegister, ref buffer, + timeout, transferBlocks, out duration, out sense); + } + + /// Sends an ATA command in 48-bit format + /// 0 if no error occurred, otherwise, errno + /// Platform ID for executing the command + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA returned non-OK status + /// Registers to send to the device + /// Registers returned by the device + /// ATA protocol to use + /// What register contains the transfer length + /// Set to true if the transfer length is in block, otherwise it is in bytes + /// If the specified platform is not supported + internal static int SendAtaCommand(PlatformID ptId, object fd, AtaRegistersLba48 registers, + out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + switch(ptId) { - switch(ptId) - { - case PlatformID.Win32NT: - return Windows.Command.SendMultipleMmcCommands((SafeFileHandle)fd, commands, out duration, - out sense, timeout); - case PlatformID.Linux: - return Linux.Command.SendMultipleMmcCommands((int)fd, commands, out duration, out sense, timeout); - default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); - } + case PlatformID.Win32NT: + // No check for Windows version. A 48-bit ATA disk simply does not work on earlier systems + return Windows.Command.SendAtaCommand((SafeFileHandle)fd, registers, out errorRegisters, protocol, + ref buffer, timeout, out duration, out sense); + case PlatformID.Linux: + return Linux.Command.SendAtaCommand((int)fd, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, + out duration, out sense); + default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); } + } - internal static int ReOpen(PlatformID ptId, string devicePath, object fd, out object newFd) + /// Sends a MMC/SD command + /// The result of the command. + /// File handle + /// MMC/SD opcode + /// Buffer for MMC/SD command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if MMC/SD returned non-OK status + /// True if data is sent from host to card + /// True if command should be preceded with CMD55 + /// Flags indicating kind and place of response + /// How many blocks to transfer + /// Command argument + /// Response registers + /// Size of block in bytes + /// If the specified platform is not supported + internal static int SendMmcCommand(object fd, MmcCommands command, bool write, bool isApplication, + MmcFlags flags, uint argument, uint blockSize, uint blocks, + ref byte[] buffer, out uint[] response, out double duration, out bool sense, + uint timeout = 0) + { + PlatformID ptId = DetectOS.GetRealPlatformID(); + + return SendMmcCommand(ptId, (int)fd, command, write, isApplication, flags, argument, blockSize, blocks, + ref buffer, out response, out duration, out sense, timeout); + } + + /// Sends a MMC/SD command + /// The result of the command. + /// Platform ID for executing the command + /// File handle + /// MMC/SD opcode + /// Buffer for MMC/SD command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if MMC/SD returned non-OK status + /// True if data is sent from host to card + /// True if command should be preceded with CMD55 + /// Flags indicating kind and place of response + /// How many blocks to transfer + /// Command argument + /// Response registers + /// Size of block in bytes + /// If the specified platform is not supported + internal static int SendMmcCommand(PlatformID ptId, object fd, MmcCommands command, bool write, + bool isApplication, MmcFlags flags, uint argument, uint blockSize, + uint blocks, ref byte[] buffer, out uint[] response, out double duration, + out bool sense, uint timeout = 0) + { + switch(ptId) { - switch(ptId) - { - case PlatformID.Win32NT: return Windows.Command.ReOpen(devicePath, (SafeFileHandle)fd, out newFd); - case PlatformID.Linux: return Linux.Command.ReOpen(devicePath, (int)fd, out newFd); - default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); - } + case PlatformID.Win32NT: + return Windows.Command.SendMmcCommand((SafeFileHandle)fd, command, write, isApplication, flags, + argument, blockSize, blocks, ref buffer, out response, + out duration, out sense, timeout); + case PlatformID.Linux: + return Linux.Command.SendMmcCommand((int)fd, command, write, isApplication, flags, argument, + blockSize, blocks, ref buffer, out response, out duration, + out sense, timeout); + default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); } + } - internal static int BufferedOsRead(PlatformID ptId, object fd, out byte[] buffer, long offset, uint length, - out double duration) + internal static int SendMultipleMmcCommands(PlatformID ptId, object fd, Device.MmcSingleCommand[] commands, + out double duration, out bool sense, uint timeout = 0) + { + switch(ptId) { - switch(ptId) - { - case PlatformID.Win32NT: - return Windows.Command.BufferedOsRead((SafeFileHandle)fd, out buffer, offset, length, out duration); - case PlatformID.Linux: - return Linux.Command.BufferedOsRead((int)fd, out buffer, offset, length, out duration); - default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); - } + case PlatformID.Win32NT: + return Windows.Command.SendMultipleMmcCommands((SafeFileHandle)fd, commands, out duration, + out sense, timeout); + case PlatformID.Linux: + return Linux.Command.SendMultipleMmcCommands((int)fd, commands, out duration, out sense, timeout); + default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); + } + } + + internal static int ReOpen(PlatformID ptId, string devicePath, object fd, out object newFd) + { + switch(ptId) + { + case PlatformID.Win32NT: return Windows.Command.ReOpen(devicePath, (SafeFileHandle)fd, out newFd); + case PlatformID.Linux: return Linux.Command.ReOpen(devicePath, (int)fd, out newFd); + default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); + } + } + + internal static int BufferedOsRead(PlatformID ptId, object fd, out byte[] buffer, long offset, uint length, + out double duration) + { + switch(ptId) + { + case PlatformID.Win32NT: + return Windows.Command.BufferedOsRead((SafeFileHandle)fd, out buffer, offset, length, out duration); + case PlatformID.Linux: + return Linux.Command.BufferedOsRead((int)fd, out buffer, offset, length, out duration); + default: throw new InvalidOperationException($"Platform {ptId} not yet supported."); } } } \ No newline at end of file diff --git a/Aaru.Devices/Device/AtaCommands/Ata28.cs b/Aaru.Devices/Device/AtaCommands/Ata28.cs index e83504df1..4f1ac5261 100644 --- a/Aaru.Devices/Device/AtaCommands/Ata28.cs +++ b/Aaru.Devices/Device/AtaCommands/Ata28.cs @@ -34,317 +34,316 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Reads the drive buffer using PIO transfer + /// Buffer that contains the read data + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadBuffer(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint timeout, + out double duration) { - /// Reads the drive buffer using PIO transfer - /// Buffer that contains the read data - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadBuffer(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint timeout, - out double duration) + buffer = new byte[512]; + + var registers = new AtaRegistersLba28 { - buffer = new byte[512]; + Command = (byte)AtaCommands.ReadBuffer + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.ReadBuffer - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "READ BUFFER took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ BUFFER took {0} ms.", duration); + return sense; + } - return sense; + /// Reads the drive buffer using DMA transfer + /// Buffer that contains the read data + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadBufferDma(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint timeout, + out double duration) + { + buffer = new byte[512]; + + var registers = new AtaRegistersLba28 + { + Command = (byte)AtaCommands.ReadBufferDma + }; + + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.NoTransfer, + ref buffer, timeout, false, out duration, out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("ATA Device", "READ BUFFER DMA took {0} ms.", duration); + + return sense; + } + + /// Reads sectors using 28-bit addressing and DMA transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// LBA of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadDma(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, byte count, + uint timeout, out double duration) => + ReadDma(out buffer, out statusRegisters, true, lba, count, timeout, out duration); + + /// Reads sectors using 48-bit addressing and DMA transfer + /// Buffer that contains the read data + /// Returned status registers + /// Retry on error + /// LBA of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadDma(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, bool retry, uint lba, + byte count, uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; + + var registers = new AtaRegistersLba28 + { + SectorCount = count, + DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), + LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), + LbaMid = (byte)((lba & 0xFF00) / 0x100), + LbaLow = (byte)((lba & 0xFF) / 0x1), + Command = retry ? (byte)AtaCommands.ReadDmaRetry : (byte)AtaCommands.ReadDma + }; + + registers.DeviceHead += 0x40; + + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.SectorCount, + ref buffer, timeout, true, out duration, out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("ATA Device", "READ DMA took {0} ms.", duration); + + return sense; + } + + /// + /// Reads sectors using 28-bit addressing and PIO transfer, sending an interrupt only after all the sectors have + /// been transferred + /// + /// Buffer that contains the read data + /// Returned status registers + /// LBA of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadMultiple(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, byte count, + uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; + + var registers = new AtaRegistersLba28 + { + Command = (byte)AtaCommands.ReadMultiple, + SectorCount = count, + DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), + LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), + LbaMid = (byte)((lba & 0xFF00) / 0x100), + LbaLow = (byte)((lba & 0xFF) / 0x1) + }; + + registers.DeviceHead += 0x40; + + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("ATA Device", "READ MULTIPLE took {0} ms.", duration); + + return sense; + } + + /// Reads native max address using 28-bit addressing + /// Maximum addressable block + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadNativeMaxAddress(out uint lba, out AtaErrorRegistersLba28 statusRegisters, uint timeout, + out double duration) + { + lba = 0; + byte[] buffer = Array.Empty(); + + var registers = new AtaRegistersLba28 + { + Command = (byte)AtaCommands.ReadNativeMaxAddress + }; + + registers.DeviceHead += 0x40; + + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); + + Error = LastError != 0; + + if((statusRegisters.Status & 0x23) == 0) + { + lba += (uint)(statusRegisters.DeviceHead & 0xF); + lba *= 0x1000000; + lba += (uint)(statusRegisters.LbaHigh << 16); + lba += (uint)(statusRegisters.LbaMid << 8); + lba += statusRegisters.LbaLow; } - /// Reads the drive buffer using DMA transfer - /// Buffer that contains the read data - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadBufferDma(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint timeout, - out double duration) + AaruConsole.DebugWriteLine("ATA Device", "READ NATIVE MAX ADDRESS took {0} ms.", duration); + + return sense; + } + + /// Reads sectors using 28-bit addressing and PIO transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// LBA of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool Read(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, byte count, + uint timeout, out double duration) => + Read(out buffer, out statusRegisters, true, lba, count, timeout, out duration); + + /// Reads sectors using 28-bit addressing and PIO transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// Retry on error + /// LBA of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool Read(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, bool retry, uint lba, + byte count, uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; + + var registers = new AtaRegistersLba28 { - buffer = new byte[512]; + SectorCount = count, + DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), + LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), + LbaMid = (byte)((lba & 0xFF00) / 0x100), + LbaLow = (byte)((lba & 0xFF) / 0x1), + Command = retry ? (byte)AtaCommands.ReadRetry : (byte)AtaCommands.Read + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.ReadBufferDma - }; + registers.DeviceHead += 0x40; - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.NoTransfer, - ref buffer, timeout, false, out duration, out bool sense); + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("ATA Device", "READ BUFFER DMA took {0} ms.", duration); + AaruConsole.DebugWriteLine("ATA Device", "READ SECTORS took {0} ms.", duration); - return sense; - } + return sense; + } - /// Reads sectors using 28-bit addressing and DMA transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// LBA of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadDma(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, byte count, - uint timeout, out double duration) => - ReadDma(out buffer, out statusRegisters, true, lba, count, timeout, out duration); - - /// Reads sectors using 48-bit addressing and DMA transfer - /// Buffer that contains the read data - /// Returned status registers - /// Retry on error - /// LBA of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadDma(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, bool retry, uint lba, - byte count, uint timeout, out double duration) - { - buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; - - var registers = new AtaRegistersLba28 - { - SectorCount = count, - DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), - LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), - LbaMid = (byte)((lba & 0xFF00) / 0x100), - LbaLow = (byte)((lba & 0xFF) / 0x1), - Command = retry ? (byte)AtaCommands.ReadDmaRetry : (byte)AtaCommands.ReadDma - }; - - registers.DeviceHead += 0x40; - - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.SectorCount, - ref buffer, timeout, true, out duration, out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("ATA Device", "READ DMA took {0} ms.", duration); - - return sense; - } - - /// - /// Reads sectors using 28-bit addressing and PIO transfer, sending an interrupt only after all the sectors have - /// been transferred - /// - /// Buffer that contains the read data - /// Returned status registers - /// LBA of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadMultiple(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, byte count, - uint timeout, out double duration) - { - buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; - - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.ReadMultiple, - SectorCount = count, - DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), - LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), - LbaMid = (byte)((lba & 0xFF00) / 0x100), - LbaLow = (byte)((lba & 0xFF) / 0x1) - }; - - registers.DeviceHead += 0x40; - - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("ATA Device", "READ MULTIPLE took {0} ms.", duration); - - return sense; - } - - /// Reads native max address using 28-bit addressing - /// Maximum addressable block - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadNativeMaxAddress(out uint lba, out AtaErrorRegistersLba28 statusRegisters, uint timeout, - out double duration) - { - lba = 0; - byte[] buffer = Array.Empty(); - - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.ReadNativeMaxAddress - }; - - registers.DeviceHead += 0x40; - - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); - - Error = LastError != 0; - - if((statusRegisters.Status & 0x23) == 0) - { - lba += (uint)(statusRegisters.DeviceHead & 0xF); - lba *= 0x1000000; - lba += (uint)(statusRegisters.LbaHigh << 16); - lba += (uint)(statusRegisters.LbaMid << 8); - lba += statusRegisters.LbaLow; - } - - AaruConsole.DebugWriteLine("ATA Device", "READ NATIVE MAX ADDRESS took {0} ms.", duration); - - return sense; - } - - /// Reads sectors using 28-bit addressing and PIO transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// LBA of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool Read(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, byte count, + /// Reads a long sector using 28-bit addressing and PIO transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// LBA of read start + /// Size in bytes of the long sector + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadLong(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, uint blockSize, uint timeout, out double duration) => - Read(out buffer, out statusRegisters, true, lba, count, timeout, out duration); + ReadLong(out buffer, out statusRegisters, true, lba, blockSize, timeout, out duration); - /// Reads sectors using 28-bit addressing and PIO transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// Retry on error - /// LBA of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool Read(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, bool retry, uint lba, - byte count, uint timeout, out double duration) + /// Reads a long sector using 28-bit addressing and PIO transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// Retry on error + /// LBA of read start + /// Size in bytes of the long sector + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadLong(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, bool retry, uint lba, + uint blockSize, uint timeout, out double duration) + { + buffer = new byte[blockSize]; + + var registers = new AtaRegistersLba28 { - buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; + SectorCount = 1, + DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), + LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), + LbaMid = (byte)((lba & 0xFF00) / 0x100), + LbaLow = (byte)((lba & 0xFF) / 0x1), + Command = retry ? (byte)AtaCommands.ReadLongRetry : (byte)AtaCommands.ReadLong + }; - var registers = new AtaRegistersLba28 - { - SectorCount = count, - DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), - LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), - LbaMid = (byte)((lba & 0xFF00) / 0x100), - LbaLow = (byte)((lba & 0xFF) / 0x1), - Command = retry ? (byte)AtaCommands.ReadRetry : (byte)AtaCommands.Read - }; + registers.DeviceHead += 0x40; - registers.DeviceHead += 0x40; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "READ LONG took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ SECTORS took {0} ms.", duration); + return sense; + } - return sense; - } + /// Sets the reading mechanism ready to read the specified block using 28-bit LBA addressing + /// Returned status registers + /// LBA to position reading mechanism ready to read + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool Seek(out AtaErrorRegistersLba28 statusRegisters, uint lba, uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Reads a long sector using 28-bit addressing and PIO transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// LBA of read start - /// Size in bytes of the long sector - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadLong(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, uint blockSize, - uint timeout, out double duration) => - ReadLong(out buffer, out statusRegisters, true, lba, blockSize, timeout, out duration); - - /// Reads a long sector using 28-bit addressing and PIO transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// Retry on error - /// LBA of read start - /// Size in bytes of the long sector - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadLong(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, bool retry, uint lba, - uint blockSize, uint timeout, out double duration) + var registers = new AtaRegistersLba28 { - buffer = new byte[blockSize]; + Command = (byte)AtaCommands.Seek, + DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), + LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), + LbaMid = (byte)((lba & 0xFF00) / 0x100), + LbaLow = (byte)((lba & 0xFF) / 0x1) + }; - var registers = new AtaRegistersLba28 - { - SectorCount = 1, - DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), - LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), - LbaMid = (byte)((lba & 0xFF00) / 0x100), - LbaLow = (byte)((lba & 0xFF) / 0x1), - Command = retry ? (byte)AtaCommands.ReadLongRetry : (byte)AtaCommands.ReadLong - }; + registers.DeviceHead += 0x40; - registers.DeviceHead += 0x40; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SEEK took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ LONG took {0} ms.", duration); - - return sense; - } - - /// Sets the reading mechanism ready to read the specified block using 28-bit LBA addressing - /// Returned status registers - /// LBA to position reading mechanism ready to read - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool Seek(out AtaErrorRegistersLba28 statusRegisters, uint lba, uint timeout, out double duration) - { - byte[] buffer = Array.Empty(); - - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Seek, - DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), - LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), - LbaMid = (byte)((lba & 0xFF00) / 0x100), - LbaLow = (byte)((lba & 0xFF) / 0x1) - }; - - registers.DeviceHead += 0x40; - - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("ATA Device", "SEEK took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/AtaCommands/Ata48.cs b/Aaru.Devices/Device/AtaCommands/Ata48.cs index 2c399d82d..8c6fdc2d1 100644 --- a/Aaru.Devices/Device/AtaCommands/Ata48.cs +++ b/Aaru.Devices/Device/AtaCommands/Ata48.cs @@ -34,274 +34,273 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Gets native max address using 48-bit addressing + /// Maximum addressable block + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool GetNativeMaxAddressExt(out ulong lba, out AtaErrorRegistersLba48 statusRegisters, uint timeout, + out double duration) { - /// Gets native max address using 48-bit addressing - /// Maximum addressable block - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool GetNativeMaxAddressExt(out ulong lba, out AtaErrorRegistersLba48 statusRegisters, uint timeout, - out double duration) + lba = 0; + byte[] buffer = Array.Empty(); + + var registers = new AtaRegistersLba48 { - lba = 0; - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.NativeMaxAddress, + Feature = 0x0000 + }; - var registers = new AtaRegistersLba48 - { - Command = (byte)AtaCommands.NativeMaxAddress, - Feature = 0x0000 - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + if((statusRegisters.Status & 0x23) == 0) + { + lba = (ulong)((statusRegisters.LbaHighCurrent << 16) + (statusRegisters.LbaMidCurrent << 8) + + statusRegisters.LbaLowCurrent); - if((statusRegisters.Status & 0x23) == 0) - { - lba = (ulong)((statusRegisters.LbaHighCurrent << 16) + (statusRegisters.LbaMidCurrent << 8) + - statusRegisters.LbaLowCurrent); + lba <<= 24; - lba <<= 24; - - lba += (ulong)((statusRegisters.LbaHighPrevious << 16) + (statusRegisters.LbaMidPrevious << 8) + - statusRegisters.LbaLowPrevious); - } - - AaruConsole.DebugWriteLine("ATA Device", "GET NATIVE MAX ADDRESS EXT took {0} ms.", duration); - - return sense; + lba += (ulong)((statusRegisters.LbaHighPrevious << 16) + (statusRegisters.LbaMidPrevious << 8) + + statusRegisters.LbaLowPrevious); } - /// Reads sectors using 48-bit addressing and DMA transfer - /// Buffer that contains the read data - /// Returned status registers - /// LBA of read start - /// How many blocks to read, or 0 to indicate 65536 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadDma(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, ulong lba, ushort count, - uint timeout, out double duration) + AaruConsole.DebugWriteLine("ATA Device", "GET NATIVE MAX ADDRESS EXT took {0} ms.", duration); + + return sense; + } + + /// Reads sectors using 48-bit addressing and DMA transfer + /// Buffer that contains the read data + /// Returned status registers + /// LBA of read start + /// How many blocks to read, or 0 to indicate 65536 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadDma(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, ulong lba, ushort count, + uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 65536] : new byte[512 * count]; + + var registers = new AtaRegistersLba48 { - buffer = count == 0 ? new byte[512 * 65536] : new byte[512 * count]; + Command = (byte)AtaCommands.ReadDmaExt, + SectorCount = count, + LbaHighPrevious = (byte)((lba & 0xFF0000000000) / 0x10000000000), + LbaMidPrevious = (byte)((lba & 0xFF00000000) / 0x100000000), + LbaLowPrevious = (byte)((lba & 0xFF000000) / 0x1000000), + LbaHighCurrent = (byte)((lba & 0xFF0000) / 0x10000), + LbaMidCurrent = (byte)((lba & 0xFF00) / 0x100), + LbaLowCurrent = (byte)(lba & 0xFF) + }; - var registers = new AtaRegistersLba48 - { - Command = (byte)AtaCommands.ReadDmaExt, - SectorCount = count, - LbaHighPrevious = (byte)((lba & 0xFF0000000000) / 0x10000000000), - LbaMidPrevious = (byte)((lba & 0xFF00000000) / 0x100000000), - LbaLowPrevious = (byte)((lba & 0xFF000000) / 0x1000000), - LbaHighCurrent = (byte)((lba & 0xFF0000) / 0x10000), - LbaMidCurrent = (byte)((lba & 0xFF00) / 0x100), - LbaLowCurrent = (byte)(lba & 0xFF) - }; + registers.DeviceHead += 0x40; - registers.DeviceHead += 0x40; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.SectorCount, + ref buffer, timeout, true, out duration, out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.SectorCount, - ref buffer, timeout, true, out duration, out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "READ DMA EXT took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ DMA EXT took {0} ms.", duration); + return sense; + } - return sense; + /// Reads a drive log using PIO transfer + /// Buffer that contains the read data + /// Returned status registers + /// Log address + /// Log page number + /// How log blocks to read + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadLog(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, byte logAddress, + ushort pageNumber, ushort count, uint timeout, out double duration) + { + buffer = new byte[512 * count]; + + var registers = new AtaRegistersLba48 + { + Command = (byte)AtaCommands.ReadLogExt, + SectorCount = count, + LbaMidCurrent = (byte)(pageNumber & 0xFF), + LbaMidPrevious = (byte)((pageNumber & 0xFF00) / 0x100) + }; + + registers.LbaLowCurrent = logAddress; + + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("ATA Device", "READ LOG EXT took {0} ms.", duration); + + return sense; + } + + /// Reads a drive log using DMA transfer + /// Buffer that contains the read data + /// Returned status registers + /// Log address + /// Log page number + /// How log blocks to read + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadLogDma(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, byte logAddress, + ushort pageNumber, ushort count, uint timeout, out double duration) + { + buffer = new byte[512 * count]; + + var registers = new AtaRegistersLba48 + { + Command = (byte)AtaCommands.ReadLogDmaExt, + SectorCount = count, + LbaMidCurrent = (byte)(pageNumber & 0xFF), + LbaMidPrevious = (byte)((pageNumber & 0xFF00) / 0x100) + }; + + registers.LbaLowCurrent = logAddress; + + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.SectorCount, + ref buffer, timeout, true, out duration, out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("ATA Device", "READ LOG DMA EXT took {0} ms.", duration); + + return sense; + } + + /// + /// Reads sectors using 48-bit addressing and PIO transfer, sending an interrupt only after all the sectors have + /// been transferred + /// + /// Buffer that contains the read data + /// Returned status registers + /// LBA of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadMultiple(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, ulong lba, ushort count, + uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 65536] : new byte[512 * count]; + + var registers = new AtaRegistersLba48 + { + Command = (byte)AtaCommands.ReadMultipleExt, + SectorCount = count, + LbaHighPrevious = (byte)((lba & 0xFF0000000000) / 0x10000000000), + LbaMidPrevious = (byte)((lba & 0xFF00000000) / 0x100000000), + LbaLowPrevious = (byte)((lba & 0xFF000000) / 0x1000000), + LbaHighCurrent = (byte)((lba & 0xFF0000) / 0x10000), + LbaMidCurrent = (byte)((lba & 0xFF00) / 0x100), + LbaLowCurrent = (byte)(lba & 0xFF) + }; + + registers.DeviceHead += 0x40; + + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("ATA Device", "READ MULTIPLE EXT took {0} ms.", duration); + + return sense; + } + + /// Reads native max address using 48-bit addressing + /// Maximum addressable block + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadNativeMaxAddress(out ulong lba, out AtaErrorRegistersLba48 statusRegisters, uint timeout, + out double duration) + { + lba = 0; + byte[] buffer = Array.Empty(); + + var registers = new AtaRegistersLba48 + { + Command = (byte)AtaCommands.ReadNativeMaxAddressExt + }; + + registers.DeviceHead += 0x40; + + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); + + Error = LastError != 0; + + if((statusRegisters.Status & 0x23) == 0) + { + lba = (ulong)((statusRegisters.LbaHighCurrent << 16) + (statusRegisters.LbaMidCurrent << 8) + + statusRegisters.LbaLowCurrent); + + lba <<= 24; + + lba += (ulong)((statusRegisters.LbaHighPrevious << 16) + (statusRegisters.LbaMidPrevious << 8) + + statusRegisters.LbaLowPrevious); } - /// Reads a drive log using PIO transfer - /// Buffer that contains the read data - /// Returned status registers - /// Log address - /// Log page number - /// How log blocks to read - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadLog(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, byte logAddress, - ushort pageNumber, ushort count, uint timeout, out double duration) + AaruConsole.DebugWriteLine("ATA Device", "READ NATIVE MAX ADDRESS EXT took {0} ms.", duration); + + return sense; + } + + /// Reads sectors using 48-bit addressing and PIO transfer + /// Buffer that contains the read data + /// Returned status registers + /// LBA of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool Read(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, ulong lba, ushort count, + uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 65536] : new byte[512 * count]; + + var registers = new AtaRegistersLba48 { - buffer = new byte[512 * count]; + Command = (byte)AtaCommands.ReadExt, + SectorCount = count, + LbaHighPrevious = (byte)((lba & 0xFF0000000000) / 0x10000000000), + LbaMidPrevious = (byte)((lba & 0xFF00000000) / 0x100000000), + LbaLowPrevious = (byte)((lba & 0xFF000000) / 0x1000000), + LbaHighCurrent = (byte)((lba & 0xFF0000) / 0x10000), + LbaMidCurrent = (byte)((lba & 0xFF00) / 0x100), + LbaLowCurrent = (byte)(lba & 0xFF) + }; - var registers = new AtaRegistersLba48 - { - Command = (byte)AtaCommands.ReadLogExt, - SectorCount = count, - LbaMidCurrent = (byte)(pageNumber & 0xFF), - LbaMidPrevious = (byte)((pageNumber & 0xFF00) / 0x100) - }; + registers.DeviceHead += 0x40; - registers.LbaLowCurrent = logAddress; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "READ SECTORS EXT took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ LOG EXT took {0} ms.", duration); - - return sense; - } - - /// Reads a drive log using DMA transfer - /// Buffer that contains the read data - /// Returned status registers - /// Log address - /// Log page number - /// How log blocks to read - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadLogDma(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, byte logAddress, - ushort pageNumber, ushort count, uint timeout, out double duration) - { - buffer = new byte[512 * count]; - - var registers = new AtaRegistersLba48 - { - Command = (byte)AtaCommands.ReadLogDmaExt, - SectorCount = count, - LbaMidCurrent = (byte)(pageNumber & 0xFF), - LbaMidPrevious = (byte)((pageNumber & 0xFF00) / 0x100) - }; - - registers.LbaLowCurrent = logAddress; - - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.SectorCount, - ref buffer, timeout, true, out duration, out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("ATA Device", "READ LOG DMA EXT took {0} ms.", duration); - - return sense; - } - - /// - /// Reads sectors using 48-bit addressing and PIO transfer, sending an interrupt only after all the sectors have - /// been transferred - /// - /// Buffer that contains the read data - /// Returned status registers - /// LBA of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadMultiple(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, ulong lba, ushort count, - uint timeout, out double duration) - { - buffer = count == 0 ? new byte[512 * 65536] : new byte[512 * count]; - - var registers = new AtaRegistersLba48 - { - Command = (byte)AtaCommands.ReadMultipleExt, - SectorCount = count, - LbaHighPrevious = (byte)((lba & 0xFF0000000000) / 0x10000000000), - LbaMidPrevious = (byte)((lba & 0xFF00000000) / 0x100000000), - LbaLowPrevious = (byte)((lba & 0xFF000000) / 0x1000000), - LbaHighCurrent = (byte)((lba & 0xFF0000) / 0x10000), - LbaMidCurrent = (byte)((lba & 0xFF00) / 0x100), - LbaLowCurrent = (byte)(lba & 0xFF) - }; - - registers.DeviceHead += 0x40; - - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("ATA Device", "READ MULTIPLE EXT took {0} ms.", duration); - - return sense; - } - - /// Reads native max address using 48-bit addressing - /// Maximum addressable block - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadNativeMaxAddress(out ulong lba, out AtaErrorRegistersLba48 statusRegisters, uint timeout, - out double duration) - { - lba = 0; - byte[] buffer = Array.Empty(); - - var registers = new AtaRegistersLba48 - { - Command = (byte)AtaCommands.ReadNativeMaxAddressExt - }; - - registers.DeviceHead += 0x40; - - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); - - Error = LastError != 0; - - if((statusRegisters.Status & 0x23) == 0) - { - lba = (ulong)((statusRegisters.LbaHighCurrent << 16) + (statusRegisters.LbaMidCurrent << 8) + - statusRegisters.LbaLowCurrent); - - lba <<= 24; - - lba += (ulong)((statusRegisters.LbaHighPrevious << 16) + (statusRegisters.LbaMidPrevious << 8) + - statusRegisters.LbaLowPrevious); - } - - AaruConsole.DebugWriteLine("ATA Device", "READ NATIVE MAX ADDRESS EXT took {0} ms.", duration); - - return sense; - } - - /// Reads sectors using 48-bit addressing and PIO transfer - /// Buffer that contains the read data - /// Returned status registers - /// LBA of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool Read(out byte[] buffer, out AtaErrorRegistersLba48 statusRegisters, ulong lba, ushort count, - uint timeout, out double duration) - { - buffer = count == 0 ? new byte[512 * 65536] : new byte[512 * count]; - - var registers = new AtaRegistersLba48 - { - Command = (byte)AtaCommands.ReadExt, - SectorCount = count, - LbaHighPrevious = (byte)((lba & 0xFF0000000000) / 0x10000000000), - LbaMidPrevious = (byte)((lba & 0xFF00000000) / 0x100000000), - LbaLowPrevious = (byte)((lba & 0xFF000000) / 0x1000000), - LbaHighCurrent = (byte)((lba & 0xFF0000) / 0x10000), - LbaMidCurrent = (byte)((lba & 0xFF00) / 0x100), - LbaLowCurrent = (byte)(lba & 0xFF) - }; - - registers.DeviceHead += 0x40; - - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("ATA Device", "READ SECTORS EXT took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/AtaCommands/AtaCHS.cs b/Aaru.Devices/Device/AtaCommands/AtaCHS.cs index 6ae9a79ec..7dc66d2b9 100644 --- a/Aaru.Devices/Device/AtaCommands/AtaCHS.cs +++ b/Aaru.Devices/Device/AtaCommands/AtaCHS.cs @@ -34,404 +34,403 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the ATA IDENTIFY DEVICE command to the device, using default device timeout + /// true if the command failed and contains the error registers. + /// Buffer. + /// Status registers. + public bool AtaIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters) => + AtaIdentify(out buffer, out statusRegisters, Timeout); + + /// Sends the ATA IDENTIFY DEVICE command to the device, using default device timeout + /// true if the command failed and contains the error registers. + /// Buffer. + /// Status registers. + /// Duration. + public bool AtaIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, out double duration) => + AtaIdentify(out buffer, out statusRegisters, Timeout, out duration); + + /// Sends the ATA IDENTIFY DEVICE command to the device + /// true if the command failed and contains the error registers. + /// Buffer. + /// Status registers. + /// Timeout. + public bool AtaIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, uint timeout) => + AtaIdentify(out buffer, out statusRegisters, timeout, out _); + + /// Sends the ATA IDENTIFY DEVICE command to the device + /// true if the command failed and contains the error registers. + /// Buffer. + /// Status registers. + /// Timeout. + /// Duration. + public bool AtaIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, uint timeout, + out double duration) { - /// Sends the ATA IDENTIFY DEVICE command to the device, using default device timeout - /// true if the command failed and contains the error registers. - /// Buffer. - /// Status registers. - public bool AtaIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters) => - AtaIdentify(out buffer, out statusRegisters, Timeout); + buffer = new byte[512]; - /// Sends the ATA IDENTIFY DEVICE command to the device, using default device timeout - /// true if the command failed and contains the error registers. - /// Buffer. - /// Status registers. - /// Duration. - public bool AtaIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, out double duration) => - AtaIdentify(out buffer, out statusRegisters, Timeout, out duration); - - /// Sends the ATA IDENTIFY DEVICE command to the device - /// true if the command failed and contains the error registers. - /// Buffer. - /// Status registers. - /// Timeout. - public bool AtaIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, uint timeout) => - AtaIdentify(out buffer, out statusRegisters, timeout, out _); - - /// Sends the ATA IDENTIFY DEVICE command to the device - /// true if the command failed and contains the error registers. - /// Buffer. - /// Status registers. - /// Timeout. - /// Duration. - public bool AtaIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, uint timeout, - out double duration) + var registers = new AtaRegistersChs { - buffer = new byte[512]; + Command = (byte)AtaCommands.IdentifyDevice + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.IdentifyDevice - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "IDENTIFY DEVICE took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "IDENTIFY DEVICE took {0} ms.", duration); + return sense; + } - return sense; - } + /// Reads sectors using CHS addressing and DMA transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// Cylinder of read start + /// Head of read start + /// Sector of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadDma(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, byte head, + byte sector, byte count, uint timeout, out double duration) => + ReadDma(out buffer, out statusRegisters, true, cylinder, head, sector, count, timeout, out duration); - /// Reads sectors using CHS addressing and DMA transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// Cylinder of read start - /// Head of read start - /// Sector of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadDma(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, byte head, - byte sector, byte count, uint timeout, out double duration) => - ReadDma(out buffer, out statusRegisters, true, cylinder, head, sector, count, timeout, out duration); + /// Reads sectors using CHS addressing and DMA transfer + /// Buffer that contains the read data + /// Returned status registers + /// Retry on error + /// Cylinder of read start + /// Head of read start + /// Sector of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadDma(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, bool retry, ushort cylinder, + byte head, byte sector, byte count, uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; - /// Reads sectors using CHS addressing and DMA transfer - /// Buffer that contains the read data - /// Returned status registers - /// Retry on error - /// Cylinder of read start - /// Head of read start - /// Sector of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadDma(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, bool retry, ushort cylinder, - byte head, byte sector, byte count, uint timeout, out double duration) + var registers = new AtaRegistersChs { - buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; + SectorCount = count, + CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), + CylinderLow = (byte)((cylinder & 0xFF) / 0x1), + DeviceHead = (byte)(head & 0x0F), + Sector = sector, + Command = retry ? (byte)AtaCommands.ReadDmaRetry : (byte)AtaCommands.ReadDma + }; - var registers = new AtaRegistersChs - { - SectorCount = count, - CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), - CylinderLow = (byte)((cylinder & 0xFF) / 0x1), - DeviceHead = (byte)(head & 0x0F), - Sector = sector, - Command = retry ? (byte)AtaCommands.ReadDmaRetry : (byte)AtaCommands.ReadDma - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.SectorCount, + ref buffer, timeout, true, out duration, out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.Dma, AtaTransferRegister.SectorCount, - ref buffer, timeout, true, out duration, out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "READ DMA took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ DMA took {0} ms.", duration); + return sense; + } - return sense; - } + /// + /// Reads sectors using CHS addressing and PIO transfer, sending an interrupt only after all the sectors have been + /// transferred + /// + /// Buffer that contains the read data + /// Returned status registers + /// Cylinder of read start + /// Head of read start + /// Sector of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadMultiple(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, + byte head, byte sector, byte count, uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; - /// - /// Reads sectors using CHS addressing and PIO transfer, sending an interrupt only after all the sectors have been - /// transferred - /// - /// Buffer that contains the read data - /// Returned status registers - /// Cylinder of read start - /// Head of read start - /// Sector of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadMultiple(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, - byte head, byte sector, byte count, uint timeout, out double duration) + var registers = new AtaRegistersChs { - buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; + Command = (byte)AtaCommands.ReadMultiple, + SectorCount = count, + CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), + CylinderLow = (byte)((cylinder & 0xFF) / 0x1), + DeviceHead = (byte)(head & 0x0F), + Sector = sector + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.ReadMultiple, - SectorCount = count, - CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), - CylinderLow = (byte)((cylinder & 0xFF) / 0x1), - DeviceHead = (byte)(head & 0x0F), - Sector = sector - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "READ MULTIPLE took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ MULTIPLE took {0} ms.", duration); + return sense; + } - return sense; - } + /// Reads sectors using CHS addressing and PIO transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// Cylinder of read start + /// Head of read start + /// Sector of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool Read(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, byte head, + byte sector, byte count, uint timeout, out double duration) => + Read(out buffer, out statusRegisters, true, cylinder, head, sector, count, timeout, out duration); - /// Reads sectors using CHS addressing and PIO transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// Cylinder of read start - /// Head of read start - /// Sector of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool Read(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, byte head, - byte sector, byte count, uint timeout, out double duration) => - Read(out buffer, out statusRegisters, true, cylinder, head, sector, count, timeout, out duration); + /// Reads sectors using CHS addressing and PIO transfer + /// Buffer that contains the read data + /// Returned status registers + /// Retry on error + /// Cylinder of read start + /// Head of read start + /// Sector of read start + /// How many blocks to read, or 0 to indicate 256 blocks + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool Read(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, bool retry, ushort cylinder, + byte head, byte sector, byte count, uint timeout, out double duration) + { + buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; - /// Reads sectors using CHS addressing and PIO transfer - /// Buffer that contains the read data - /// Returned status registers - /// Retry on error - /// Cylinder of read start - /// Head of read start - /// Sector of read start - /// How many blocks to read, or 0 to indicate 256 blocks - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool Read(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, bool retry, ushort cylinder, - byte head, byte sector, byte count, uint timeout, out double duration) + var registers = new AtaRegistersChs { - buffer = count == 0 ? new byte[512 * 256] : new byte[512 * count]; + Command = retry ? (byte)AtaCommands.ReadRetry : (byte)AtaCommands.Read, + SectorCount = count, + CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), + CylinderLow = (byte)((cylinder & 0xFF) / 0x1), + DeviceHead = (byte)(head & 0x0F), + Sector = sector + }; - var registers = new AtaRegistersChs - { - Command = retry ? (byte)AtaCommands.ReadRetry : (byte)AtaCommands.Read, - SectorCount = count, - CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), - CylinderLow = (byte)((cylinder & 0xFF) / 0x1), - DeviceHead = (byte)(head & 0x0F), - Sector = sector - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "READ SECTORS took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ SECTORS took {0} ms.", duration); + return sense; + } - return sense; - } + /// Reads a long sector using CHS addressing and PIO transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// Cylinder of read start + /// Head of read start + /// Sector of read start + /// Size in bytes of the long sector + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadLong(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, byte head, + byte sector, uint blockSize, uint timeout, out double duration) => + ReadLong(out buffer, out statusRegisters, true, cylinder, head, sector, blockSize, timeout, out duration); - /// Reads a long sector using CHS addressing and PIO transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// Cylinder of read start - /// Head of read start - /// Sector of read start - /// Size in bytes of the long sector - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadLong(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, byte head, - byte sector, uint blockSize, uint timeout, out double duration) => - ReadLong(out buffer, out statusRegisters, true, cylinder, head, sector, blockSize, timeout, out duration); + /// Reads a long sector using CHS addressing and PIO transfer, retrying on error + /// Buffer that contains the read data + /// Returned status registers + /// Retry on error + /// Cylinder of read start + /// Head of read start + /// Sector of read start + /// Size in bytes of the long sector + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadLong(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, bool retry, ushort cylinder, + byte head, byte sector, uint blockSize, uint timeout, out double duration) + { + buffer = new byte[blockSize]; - /// Reads a long sector using CHS addressing and PIO transfer, retrying on error - /// Buffer that contains the read data - /// Returned status registers - /// Retry on error - /// Cylinder of read start - /// Head of read start - /// Sector of read start - /// Size in bytes of the long sector - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadLong(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, bool retry, ushort cylinder, - byte head, byte sector, uint blockSize, uint timeout, out double duration) + var registers = new AtaRegistersChs { - buffer = new byte[blockSize]; + Command = retry ? (byte)AtaCommands.ReadLongRetry : (byte)AtaCommands.ReadLong, + SectorCount = 1, + CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), + CylinderLow = (byte)((cylinder & 0xFF) / 0x1), + DeviceHead = (byte)(head & 0x0F), + Sector = sector + }; - var registers = new AtaRegistersChs - { - Command = retry ? (byte)AtaCommands.ReadLongRetry : (byte)AtaCommands.ReadLong, - SectorCount = 1, - CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), - CylinderLow = (byte)((cylinder & 0xFF) / 0x1), - DeviceHead = (byte)(head & 0x0F), - Sector = sector - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.SectorCount, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "READ LONG took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "READ LONG took {0} ms.", duration); + return sense; + } - return sense; - } + /// Sets the reading mechanism ready to read the specified block using CHS addressing + /// Returned status registers + /// Cylinder to position reading mechanism ready to read + /// Head to position reading mechanism ready to read + /// Sector to position reading mechanism ready to read + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool Seek(out AtaErrorRegistersChs statusRegisters, ushort cylinder, byte head, byte sector, + uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Sets the reading mechanism ready to read the specified block using CHS addressing - /// Returned status registers - /// Cylinder to position reading mechanism ready to read - /// Head to position reading mechanism ready to read - /// Sector to position reading mechanism ready to read - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool Seek(out AtaErrorRegistersChs statusRegisters, ushort cylinder, byte head, byte sector, - uint timeout, out double duration) + var registers = new AtaRegistersChs { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.Seek, + CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), + CylinderLow = (byte)((cylinder & 0xFF) / 0x1), + DeviceHead = (byte)(head & 0x0F), + Sector = sector + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.Seek, - CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), - CylinderLow = (byte)((cylinder & 0xFF) / 0x1), - DeviceHead = (byte)(head & 0x0F), - Sector = sector - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SEEK took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SEEK took {0} ms.", duration); + return sense; + } - return sense; - } + /// Enables drive features + /// Returned status registers + /// Feature to enable + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SetFeatures(out AtaErrorRegistersChs statusRegisters, AtaFeatures feature, uint timeout, + out double duration) => + SetFeatures(out statusRegisters, feature, 0, 0, 0, 0, timeout, out duration); - /// Enables drive features - /// Returned status registers - /// Feature to enable - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SetFeatures(out AtaErrorRegistersChs statusRegisters, AtaFeatures feature, uint timeout, - out double duration) => - SetFeatures(out statusRegisters, feature, 0, 0, 0, 0, timeout, out duration); + /// Enables drive features + /// Returned status registers + /// Feature to enable + /// Value for the cylinder register + /// Value for the head register + /// Value for the sector register + /// Value for the sector count register + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SetFeatures(out AtaErrorRegistersChs statusRegisters, AtaFeatures feature, ushort cylinder, + byte head, byte sector, byte sectorCount, uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Enables drive features - /// Returned status registers - /// Feature to enable - /// Value for the cylinder register - /// Value for the head register - /// Value for the sector register - /// Value for the sector count register - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SetFeatures(out AtaErrorRegistersChs statusRegisters, AtaFeatures feature, ushort cylinder, - byte head, byte sector, byte sectorCount, uint timeout, out double duration) + var registers = new AtaRegistersChs { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.SetFeatures, + CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), + CylinderLow = (byte)((cylinder & 0xFF) / 0x1), + DeviceHead = (byte)(head & 0x0F), + Sector = sector, + SectorCount = sectorCount, + Feature = (byte)feature + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.SetFeatures, - CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), - CylinderLow = (byte)((cylinder & 0xFF) / 0x1), - DeviceHead = (byte)(head & 0x0F), - Sector = sector, - SectorCount = sectorCount, - Feature = (byte)feature - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SET FEATURES took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SET FEATURES took {0} ms.", duration); + return sense; + } - return sense; - } + /// Prevents ejection of the media inserted in the drive + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool DoorLock(out AtaErrorRegistersChs statusRegisters, uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Prevents ejection of the media inserted in the drive - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool DoorLock(out AtaErrorRegistersChs statusRegisters, uint timeout, out double duration) + var registers = new AtaRegistersChs { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.DoorLock + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.DoorLock - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "DOOR LOCK took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "DOOR LOCK took {0} ms.", duration); + return sense; + } - return sense; - } + /// Allows ejection of the media inserted in the drive + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool DoorUnlock(out AtaErrorRegistersChs statusRegisters, uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Allows ejection of the media inserted in the drive - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool DoorUnlock(out AtaErrorRegistersChs statusRegisters, uint timeout, out double duration) + var registers = new AtaRegistersChs { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.DoorUnLock + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.DoorUnLock - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "DOOR UNLOCK took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "DOOR UNLOCK took {0} ms.", duration); + return sense; + } - return sense; - } + /// Ejects the media inserted in the drive + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool MediaEject(out AtaErrorRegistersChs statusRegisters, uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Ejects the media inserted in the drive - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool MediaEject(out AtaErrorRegistersChs statusRegisters, uint timeout, out double duration) + var registers = new AtaRegistersChs { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.MediaEject + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.MediaEject - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, true, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "MEDIA EJECT took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "MEDIA EJECT took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/AtaCommands/Atapi.cs b/Aaru.Devices/Device/AtaCommands/Atapi.cs index 28b70fd06..352e9c653 100644 --- a/Aaru.Devices/Device/AtaCommands/Atapi.cs +++ b/Aaru.Devices/Device/AtaCommands/Atapi.cs @@ -33,58 +33,57 @@ using Aaru.Console; using Aaru.Decoders.ATA; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the ATA IDENTIFY PACKET DEVICE command to the device, using default device timeout + /// true if the command failed and contains the error registers. + /// Buffer. + /// Status registers. + public bool AtapiIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters) => + AtapiIdentify(out buffer, out statusRegisters, Timeout); + + /// Sends the ATA IDENTIFY PACKET DEVICE command to the device, using default device timeout + /// true if the command failed and contains the error registers. + /// Buffer. + /// Status registers. + /// Duration. + public bool AtapiIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, out double duration) => + AtapiIdentify(out buffer, out statusRegisters, Timeout, out duration); + + /// Sends the ATA IDENTIFY PACKET DEVICE command to the device + /// true if the command failed and contains the error registers. + /// Buffer. + /// Status registers. + /// Timeout. + public bool AtapiIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, uint timeout) => + AtapiIdentify(out buffer, out statusRegisters, timeout, out _); + + /// Sends the ATA IDENTIFY PACKET DEVICE command to the device + /// true if the command failed and contains the error registers. + /// Buffer. + /// Status registers. + /// Timeout. + /// Duration. + public bool AtapiIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, uint timeout, + out double duration) { - /// Sends the ATA IDENTIFY PACKET DEVICE command to the device, using default device timeout - /// true if the command failed and contains the error registers. - /// Buffer. - /// Status registers. - public bool AtapiIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters) => - AtapiIdentify(out buffer, out statusRegisters, Timeout); + buffer = new byte[512]; - /// Sends the ATA IDENTIFY PACKET DEVICE command to the device, using default device timeout - /// true if the command failed and contains the error registers. - /// Buffer. - /// Status registers. - /// Duration. - public bool AtapiIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, out double duration) => - AtapiIdentify(out buffer, out statusRegisters, Timeout, out duration); - - /// Sends the ATA IDENTIFY PACKET DEVICE command to the device - /// true if the command failed and contains the error registers. - /// Buffer. - /// Status registers. - /// Timeout. - public bool AtapiIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, uint timeout) => - AtapiIdentify(out buffer, out statusRegisters, timeout, out _); - - /// Sends the ATA IDENTIFY PACKET DEVICE command to the device - /// true if the command failed and contains the error registers. - /// Buffer. - /// Status registers. - /// Timeout. - /// Duration. - public bool AtapiIdentify(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, uint timeout, - out double duration) + var registers = new AtaRegistersChs { - buffer = new byte[512]; + Command = (byte)AtaCommands.IdentifyPacketDevice + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.IdentifyPacketDevice - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "IDENTIFY PACKET DEVICE took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "IDENTIFY PACKET DEVICE took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/AtaCommands/Cfa.cs b/Aaru.Devices/Device/AtaCommands/Cfa.cs index 54964094d..20a168187 100644 --- a/Aaru.Devices/Device/AtaCommands/Cfa.cs +++ b/Aaru.Devices/Device/AtaCommands/Cfa.cs @@ -34,105 +34,104 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Requests to translate an LBA to a card physical address + /// Data buffer + /// Returned status registers + /// LBA to start reading from + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool TranslateSector(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, + uint timeout, out double duration) { - /// Requests to translate an LBA to a card physical address - /// Data buffer - /// Returned status registers - /// LBA to start reading from - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool TranslateSector(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint lba, - uint timeout, out double duration) + buffer = new byte[512]; + + var registers = new AtaRegistersLba28 { - buffer = new byte[512]; + Command = (byte)AtaCommands.TranslateSector, + DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), + LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), + LbaMid = (byte)((lba & 0xFF00) / 0x100), + LbaLow = (byte)((lba & 0xFF) / 0x1) + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.TranslateSector, - DeviceHead = (byte)((lba & 0xF000000) / 0x1000000), - LbaHigh = (byte)((lba & 0xFF0000) / 0x10000), - LbaMid = (byte)((lba & 0xFF00) / 0x100), - LbaLow = (byte)((lba & 0xFF) / 0x1) - }; + registers.DeviceHead += 0x40; - registers.DeviceHead += 0x40; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "CFA TRANSLATE SECTOR took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "CFA TRANSLATE SECTOR took {0} ms.", duration); + return sense; + } - return sense; - } + /// Requests to translate a CHS to a card physical address + /// Data buffer + /// Returned status registers + /// Cylinder + /// Head + /// Sector + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool TranslateSector(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, + byte head, byte sector, uint timeout, out double duration) + { + buffer = new byte[512]; - /// Requests to translate a CHS to a card physical address - /// Data buffer - /// Returned status registers - /// Cylinder - /// Head - /// Sector - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool TranslateSector(out byte[] buffer, out AtaErrorRegistersChs statusRegisters, ushort cylinder, - byte head, byte sector, uint timeout, out double duration) + var registers = new AtaRegistersChs { - buffer = new byte[512]; + Command = (byte)AtaCommands.TranslateSector, + CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), + CylinderLow = (byte)((cylinder & 0xFF) / 0x1), + Sector = sector, + DeviceHead = (byte)(head & 0x0F) + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.TranslateSector, - CylinderHigh = (byte)((cylinder & 0xFF00) / 0x100), - CylinderLow = (byte)((cylinder & 0xFF) / 0x1), - Sector = sector, - DeviceHead = (byte)(head & 0x0F) - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "CFA TRANSLATE SECTOR took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "CFA TRANSLATE SECTOR took {0} ms.", duration); + return sense; + } - return sense; - } + /// Requests an extended error code + /// Error code + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool RequestExtendedErrorCode(out byte errorCode, out AtaErrorRegistersLba28 statusRegisters, + uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Requests an extended error code - /// Error code - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool RequestExtendedErrorCode(out byte errorCode, out AtaErrorRegistersLba28 statusRegisters, - uint timeout, out double duration) + var registers = new AtaRegistersLba28 { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.RequestSense + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.RequestSense - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + errorCode = statusRegisters.Error; - errorCode = statusRegisters.Error; + AaruConsole.DebugWriteLine("ATA Device", "CFA REQUEST EXTENDED ERROR CODE took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "CFA REQUEST EXTENDED ERROR CODE took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/AtaCommands/MCPT.cs b/Aaru.Devices/Device/AtaCommands/MCPT.cs index de98128fc..65a357398 100644 --- a/Aaru.Devices/Device/AtaCommands/MCPT.cs +++ b/Aaru.Devices/Device/AtaCommands/MCPT.cs @@ -34,54 +34,53 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Enables media card pass through + /// Status registers. + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool EnableMediaCardPassThrough(out AtaErrorRegistersChs statusRegisters, uint timeout, + out double duration) => + CheckMediaCardType(1, out statusRegisters, timeout, out duration); + + /// Disables media card pass through + /// Status registers. + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool DisableMediaCardPassThrough(out AtaErrorRegistersChs statusRegisters, uint timeout, + out double duration) => + CheckMediaCardType(0, out statusRegisters, timeout, out duration); + + /// Checks media card pass through + /// Feature + /// Status registers. + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool CheckMediaCardType(byte feature, out AtaErrorRegistersChs statusRegisters, uint timeout, + out double duration) { - /// Enables media card pass through - /// Status registers. - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool EnableMediaCardPassThrough(out AtaErrorRegistersChs statusRegisters, uint timeout, - out double duration) => - CheckMediaCardType(1, out statusRegisters, timeout, out duration); + byte[] buffer = Array.Empty(); - /// Disables media card pass through - /// Status registers. - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool DisableMediaCardPassThrough(out AtaErrorRegistersChs statusRegisters, uint timeout, - out double duration) => - CheckMediaCardType(0, out statusRegisters, timeout, out duration); - - /// Checks media card pass through - /// Feature - /// Status registers. - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool CheckMediaCardType(byte feature, out AtaErrorRegistersChs statusRegisters, uint timeout, - out double duration) + var registers = new AtaRegistersChs { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.CheckMediaCardType, + Feature = feature + }; - var registers = new AtaRegistersChs - { - Command = (byte)AtaCommands.CheckMediaCardType, - Feature = feature - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "CHECK MEDIA CARD TYPE took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "CHECK MEDIA CARD TYPE took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/AtaCommands/Smart.cs b/Aaru.Devices/Device/AtaCommands/Smart.cs index 2cb4ef7b0..f24686614 100644 --- a/Aaru.Devices/Device/AtaCommands/Smart.cs +++ b/Aaru.Devices/Device/AtaCommands/Smart.cs @@ -34,244 +34,243 @@ using System; using Aaru.Console; using Aaru.Decoders.ATA; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Disables S.M.A.R.T. + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SmartDisable(out AtaErrorRegistersLba28 statusRegisters, uint timeout, out double duration) { - /// Disables S.M.A.R.T. - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SmartDisable(out AtaErrorRegistersLba28 statusRegisters, uint timeout, out double duration) + byte[] buffer = Array.Empty(); + + var registers = new AtaRegistersLba28 { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.Smart, + Feature = (byte)AtaSmartSubCommands.Disable, + LbaHigh = 0xC2, + LbaMid = 0x4F + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Smart, - Feature = (byte)AtaSmartSubCommands.Disable, - LbaHigh = 0xC2, - LbaMid = 0x4F - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SMART DISABLE OPERATIONS took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SMART DISABLE OPERATIONS took {0} ms.", duration); + return sense; + } - return sense; - } + /// Enables auto-saving of S.M.A.R.T. attributes + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SmartEnableAttributeAutosave(out AtaErrorRegistersLba28 statusRegisters, uint timeout, + out double duration) + { + byte[] buffer = Array.Empty(); - /// Enables auto-saving of S.M.A.R.T. attributes - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SmartEnableAttributeAutosave(out AtaErrorRegistersLba28 statusRegisters, uint timeout, - out double duration) + var registers = new AtaRegistersLba28 { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.Smart, + Feature = (byte)AtaSmartSubCommands.EnableDisableAttributeAutosave, + LbaHigh = 0xC2, + LbaMid = 0x4F, + SectorCount = 0xF1 + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Smart, - Feature = (byte)AtaSmartSubCommands.EnableDisableAttributeAutosave, - LbaHigh = 0xC2, - LbaMid = 0x4F, - SectorCount = 0xF1 - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SMART ENABLE ATTRIBUTE AUTOSAVE took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SMART ENABLE ATTRIBUTE AUTOSAVE took {0} ms.", duration); + return sense; + } - return sense; - } + /// Disables auto-saving of S.M.A.R.T. attributes + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SmartDisableAttributeAutosave(out AtaErrorRegistersLba28 statusRegisters, uint timeout, + out double duration) + { + byte[] buffer = Array.Empty(); - /// Disables auto-saving of S.M.A.R.T. attributes - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SmartDisableAttributeAutosave(out AtaErrorRegistersLba28 statusRegisters, uint timeout, - out double duration) + var registers = new AtaRegistersLba28 { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.Smart, + Feature = (byte)AtaSmartSubCommands.EnableDisableAttributeAutosave, + LbaHigh = 0xC2, + LbaMid = 0x4F + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Smart, - Feature = (byte)AtaSmartSubCommands.EnableDisableAttributeAutosave, - LbaHigh = 0xC2, - LbaMid = 0x4F - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SMART DISABLE ATTRIBUTE AUTOSAVE took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SMART DISABLE ATTRIBUTE AUTOSAVE took {0} ms.", duration); + return sense; + } - return sense; - } + /// Enables S.M.A.R.T. + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SmartEnable(out AtaErrorRegistersLba28 statusRegisters, uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Enables S.M.A.R.T. - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SmartEnable(out AtaErrorRegistersLba28 statusRegisters, uint timeout, out double duration) + var registers = new AtaRegistersLba28 { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.Smart, + Feature = (byte)AtaSmartSubCommands.Enable, + LbaHigh = 0xC2, + LbaMid = 0x4F + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Smart, - Feature = (byte)AtaSmartSubCommands.Enable, - LbaHigh = 0xC2, - LbaMid = 0x4F - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SMART ENABLE OPERATIONS took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SMART ENABLE OPERATIONS took {0} ms.", duration); + return sense; + } - return sense; - } + /// Requests drive to execute offline immediate S.M.A.R.T. test + /// Subcommand + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SmartExecuteOffLineImmediate(out AtaErrorRegistersLba28 statusRegisters, byte subcommand, + uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Requests drive to execute offline immediate S.M.A.R.T. test - /// Subcommand - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SmartExecuteOffLineImmediate(out AtaErrorRegistersLba28 statusRegisters, byte subcommand, - uint timeout, out double duration) + var registers = new AtaRegistersLba28 { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.Smart, + Feature = (byte)AtaSmartSubCommands.ExecuteOfflineImmediate, + LbaHigh = 0xC2, + LbaMid = 0x4F, + LbaLow = subcommand + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Smart, - Feature = (byte)AtaSmartSubCommands.ExecuteOfflineImmediate, - LbaHigh = 0xC2, - LbaMid = 0x4F, - LbaLow = subcommand - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SMART EXECUTE OFF-LINE IMMEDIATE took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SMART EXECUTE OFF-LINE IMMEDIATE took {0} ms.", duration); + return sense; + } - return sense; - } + /// Reads S.M.A.R.T. data + /// Buffer containing data + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SmartReadData(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint timeout, + out double duration) + { + buffer = new byte[512]; - /// Reads S.M.A.R.T. data - /// Buffer containing data - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SmartReadData(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, uint timeout, - out double duration) + var registers = new AtaRegistersLba28 { - buffer = new byte[512]; + Command = (byte)AtaCommands.Smart, + Feature = (byte)AtaSmartSubCommands.ReadData, + LbaHigh = 0xC2, + LbaMid = 0x4F + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Smart, - Feature = (byte)AtaSmartSubCommands.ReadData, - LbaHigh = 0xC2, - LbaMid = 0x4F - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SMART READ DATA took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SMART READ DATA took {0} ms.", duration); + return sense; + } - return sense; - } + /// Reads S.M.A.R.T. log + /// Buffer containing log + /// Returned status registers + /// Log address + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SmartReadLog(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, byte logAddress, + uint timeout, out double duration) + { + buffer = new byte[512]; - /// Reads S.M.A.R.T. log - /// Buffer containing log - /// Returned status registers - /// Log address - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SmartReadLog(out byte[] buffer, out AtaErrorRegistersLba28 statusRegisters, byte logAddress, - uint timeout, out double duration) + var registers = new AtaRegistersLba28 { - buffer = new byte[512]; + Command = (byte)AtaCommands.Smart, + Feature = (byte)AtaSmartSubCommands.ReadLog, + LbaHigh = 0xC2, + LbaMid = 0x4F, + LbaLow = logAddress + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Smart, - Feature = (byte)AtaSmartSubCommands.ReadLog, - LbaHigh = 0xC2, - LbaMid = 0x4F, - LbaLow = logAddress - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.PioIn, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SMART READ LOG took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SMART READ LOG took {0} ms.", duration); + return sense; + } - return sense; - } + /// Retrieves S.M.A.R.T. status + /// Returned status registers + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SmartReturnStatus(out AtaErrorRegistersLba28 statusRegisters, uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); - /// Retrieves S.M.A.R.T. status - /// Returned status registers - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SmartReturnStatus(out AtaErrorRegistersLba28 statusRegisters, uint timeout, out double duration) + var registers = new AtaRegistersLba28 { - byte[] buffer = Array.Empty(); + Command = (byte)AtaCommands.Smart, + Feature = (byte)AtaSmartSubCommands.ReturnStatus, + LbaHigh = 0xC2, + LbaMid = 0x4F + }; - var registers = new AtaRegistersLba28 - { - Command = (byte)AtaCommands.Smart, - Feature = (byte)AtaSmartSubCommands.ReturnStatus, - LbaHigh = 0xC2, - LbaMid = 0x4F - }; + LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, + AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, + out bool sense); - LastError = SendAtaCommand(registers, out statusRegisters, AtaProtocol.NonData, - AtaTransferRegister.NoTransfer, ref buffer, timeout, false, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("ATA Device", "SMART RETURN STATUS took {0} ms.", duration); - AaruConsole.DebugWriteLine("ATA Device", "SMART RETURN STATUS took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/Commands.cs b/Aaru.Devices/Device/Commands.cs index 22f9a9f21..e40791a8f 100644 --- a/Aaru.Devices/Device/Commands.cs +++ b/Aaru.Devices/Device/Commands.cs @@ -34,319 +34,318 @@ using System; using System.Diagnostics.CodeAnalysis; using Aaru.Decoders.ATA; -namespace Aaru.Devices +namespace Aaru.Devices; + +[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +public sealed partial class Device { - [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - public sealed partial class Device + /// Sends a SCSI command to this device + /// 0 if no error occurred, otherwise, errno + /// SCSI CDB + /// Buffer for SCSI command response + /// Buffer with the SCSI sense + /// Timeout in seconds + /// SCSI command transfer direction + /// Time it took to execute the command in milliseconds + /// + /// True if SCSI command returned non-OK status and contains + /// SCSI sense + /// + public int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, + ScsiDirection direction, out double duration, out bool sense) { - /// Sends a SCSI command to this device - /// 0 if no error occurred, otherwise, errno - /// SCSI CDB - /// Buffer for SCSI command response - /// Buffer with the SCSI sense - /// Timeout in seconds - /// SCSI command transfer direction - /// Time it took to execute the command in milliseconds - /// - /// True if SCSI command returned non-OK status and contains - /// SCSI sense - /// - public int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, - ScsiDirection direction, out double duration, out bool sense) + // We need a timeout + if(timeout == 0) + timeout = Timeout > 0 ? Timeout : 15; + + if(!(_remote is null)) + return _remote.SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, direction, out duration, + out sense); + + return Command.SendScsiCommand(PlatformId, FileHandle, cdb, ref buffer, out senseBuffer, timeout, direction, + out duration, out sense); + } + + /// Sends an ATA/ATAPI command to this device using CHS addressing + /// 0 if no error occurred, otherwise, errno + /// ATA registers. + /// Status/error registers. + /// ATA Protocol. + /// Indicates which register indicates the transfer length + /// Buffer for ATA/ATAPI command response + /// Timeout in seconds + /// + /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in + /// bytes. + /// + /// Time it took to execute the command in milliseconds + /// True if ATA/ATAPI command returned non-OK status + public int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, + AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, + uint timeout, bool transferBlocks, out double duration, out bool sense) + { + // We need a timeout + if(timeout == 0) + timeout = Timeout > 0 ? Timeout : 15; + + if(!(_remote is null)) + return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, + timeout, transferBlocks, out duration, out sense); + + return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, + out sense); + } + + /// Sends an ATA/ATAPI command to this device using 28-bit LBA addressing + /// 0 if no error occurred, otherwise, errno + /// ATA registers. + /// Status/error registers. + /// ATA Protocol. + /// Indicates which register indicates the transfer length + /// Buffer for ATA/ATAPI command response + /// Timeout in seconds + /// + /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in + /// bytes. + /// + /// Time it took to execute the command in milliseconds + /// True if ATA/ATAPI command returned non-OK status + public int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters, + AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, + uint timeout, bool transferBlocks, out double duration, out bool sense) + { + // We need a timeout + if(timeout == 0) + timeout = Timeout > 0 ? Timeout : 15; + + if(!(_remote is null)) + return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, + timeout, transferBlocks, out duration, out sense); + + return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, + out sense); + } + + /// Sends an ATA/ATAPI command to this device using 48-bit LBA addressing + /// 0 if no error occurred, otherwise, errno + /// ATA registers. + /// Status/error registers. + /// ATA Protocol. + /// Indicates which register indicates the transfer length + /// Buffer for ATA/ATAPI command response + /// Timeout in seconds + /// + /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in + /// bytes. + /// + /// Time it took to execute the command in milliseconds + /// True if ATA/ATAPI command returned non-OK status + public int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters, + AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, + uint timeout, bool transferBlocks, out double duration, out bool sense) + { + // We need a timeout + if(timeout == 0) + timeout = Timeout > 0 ? Timeout : 15; + + if(!(_remote is null)) + return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, + timeout, transferBlocks, out duration, out sense); + + return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, + transferRegister, ref buffer, timeout, transferBlocks, out duration, + out sense); + } + + /// Sends a MMC/SD command to this device + /// The result of the command. + /// MMC/SD opcode + /// Buffer for MMC/SD command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if MMC/SD returned non-OK status + /// True if data is sent from host to card + /// True if command should be preceded with CMD55 + /// Flags indicating kind and place of response + /// How many blocks to transfer + /// Command argument + /// Response registers + /// Size of block in bytes + public int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, + uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, + out double duration, out bool sense, uint timeout = 15) + { + // We need a timeout + if(timeout == 0) + timeout = Timeout > 0 ? Timeout : 15; + + switch(command) { - // We need a timeout - if(timeout == 0) - timeout = Timeout > 0 ? Timeout : 15; - - if(!(_remote is null)) - return _remote.SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, direction, out duration, - out sense); - - return Command.SendScsiCommand(PlatformId, FileHandle, cdb, ref buffer, out senseBuffer, timeout, direction, - out duration, out sense); - } - - /// Sends an ATA/ATAPI command to this device using CHS addressing - /// 0 if no error occurred, otherwise, errno - /// ATA registers. - /// Status/error registers. - /// ATA Protocol. - /// Indicates which register indicates the transfer length - /// Buffer for ATA/ATAPI command response - /// Timeout in seconds - /// - /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in - /// bytes. - /// - /// Time it took to execute the command in milliseconds - /// True if ATA/ATAPI command returned non-OK status - public int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, - AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, - uint timeout, bool transferBlocks, out double duration, out bool sense) - { - // We need a timeout - if(timeout == 0) - timeout = Timeout > 0 ? Timeout : 15; - - if(!(_remote is null)) - return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, - timeout, transferBlocks, out duration, out sense); - - return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, - transferRegister, ref buffer, timeout, transferBlocks, out duration, - out sense); - } - - /// Sends an ATA/ATAPI command to this device using 28-bit LBA addressing - /// 0 if no error occurred, otherwise, errno - /// ATA registers. - /// Status/error registers. - /// ATA Protocol. - /// Indicates which register indicates the transfer length - /// Buffer for ATA/ATAPI command response - /// Timeout in seconds - /// - /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in - /// bytes. - /// - /// Time it took to execute the command in milliseconds - /// True if ATA/ATAPI command returned non-OK status - public int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters, - AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, - uint timeout, bool transferBlocks, out double duration, out bool sense) - { - // We need a timeout - if(timeout == 0) - timeout = Timeout > 0 ? Timeout : 15; - - if(!(_remote is null)) - return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, - timeout, transferBlocks, out duration, out sense); - - return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, - transferRegister, ref buffer, timeout, transferBlocks, out duration, - out sense); - } - - /// Sends an ATA/ATAPI command to this device using 48-bit LBA addressing - /// 0 if no error occurred, otherwise, errno - /// ATA registers. - /// Status/error registers. - /// ATA Protocol. - /// Indicates which register indicates the transfer length - /// Buffer for ATA/ATAPI command response - /// Timeout in seconds - /// - /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in - /// bytes. - /// - /// Time it took to execute the command in milliseconds - /// True if ATA/ATAPI command returned non-OK status - public int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters, - AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, - uint timeout, bool transferBlocks, out double duration, out bool sense) - { - // We need a timeout - if(timeout == 0) - timeout = Timeout > 0 ? Timeout : 15; - - if(!(_remote is null)) - return _remote.SendAtaCommand(registers, out errorRegisters, protocol, transferRegister, ref buffer, - timeout, transferBlocks, out duration, out sense); - - return Command.SendAtaCommand(PlatformId, FileHandle, registers, out errorRegisters, protocol, - transferRegister, ref buffer, timeout, transferBlocks, out duration, - out sense); - } - - /// Sends a MMC/SD command to this device - /// The result of the command. - /// MMC/SD opcode - /// Buffer for MMC/SD command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if MMC/SD returned non-OK status - /// True if data is sent from host to card - /// True if command should be preceded with CMD55 - /// Flags indicating kind and place of response - /// How many blocks to transfer - /// Command argument - /// Response registers - /// Size of block in bytes - public int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, - uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, - out double duration, out bool sense, uint timeout = 15) - { - // We need a timeout - if(timeout == 0) - timeout = Timeout > 0 ? Timeout : 15; - - switch(command) + case MmcCommands.SendCid when _cachedCid != null: { - case MmcCommands.SendCid when _cachedCid != null: - { - DateTime start = DateTime.Now; - buffer = new byte[_cachedCid.Length]; - Array.Copy(_cachedCid, buffer, buffer.Length); - response = new uint[4]; - sense = false; - DateTime end = DateTime.Now; - duration = (end - start).TotalMilliseconds; + DateTime start = DateTime.Now; + buffer = new byte[_cachedCid.Length]; + Array.Copy(_cachedCid, buffer, buffer.Length); + response = new uint[4]; + sense = false; + DateTime end = DateTime.Now; + duration = (end - start).TotalMilliseconds; - return 0; - } - case MmcCommands.SendCsd when _cachedCid != null: - { - DateTime start = DateTime.Now; - buffer = new byte[_cachedCsd.Length]; - Array.Copy(_cachedCsd, buffer, buffer.Length); - response = new uint[4]; - sense = false; - DateTime end = DateTime.Now; - duration = (end - start).TotalMilliseconds; - - return 0; - } - case (MmcCommands)SecureDigitalCommands.SendScr when _cachedScr != null: - { - DateTime start = DateTime.Now; - buffer = new byte[_cachedScr.Length]; - Array.Copy(_cachedScr, buffer, buffer.Length); - response = new uint[4]; - sense = false; - DateTime end = DateTime.Now; - duration = (end - start).TotalMilliseconds; - - return 0; - } - case (MmcCommands)SecureDigitalCommands.SendOperatingCondition when _cachedOcr != null: - case MmcCommands.SendOpCond when _cachedOcr != null: - { - DateTime start = DateTime.Now; - buffer = new byte[_cachedOcr.Length]; - Array.Copy(_cachedOcr, buffer, buffer.Length); - response = new uint[4]; - sense = false; - DateTime end = DateTime.Now; - duration = (end - start).TotalMilliseconds; - - return 0; - } + return 0; } - - return _remote is null - ? Command.SendMmcCommand(PlatformId, FileHandle, command, write, isApplication, flags, argument, - blockSize, blocks, ref buffer, out response, out duration, out sense, - timeout) : _remote.SendMmcCommand(command, write, isApplication, flags, - argument, blockSize, blocks, ref buffer, out response, out duration, out sense, - timeout); - } - - /// Encapsulates a single MMC command to send in a queue - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "MemberCanBeInternal")] - public class MmcSingleCommand - { - /// Command argument - public uint argument; - /// How many blocks to transfer - public uint blocks; - /// Size of block in bytes - public uint blockSize; - /// Buffer for MMC/SD command response - public byte[] buffer; - /// MMC/SD opcode - public MmcCommands command; - /// Flags indicating kind and place of response - public MmcFlags flags; - /// True if command should be preceded with CMD55 - public bool isApplication; - /// Response registers - public uint[] response; - /// True if data is sent from host to card - public bool write; - } - - /// - /// Concatenates a queue of commands to be send to a remote SecureDigital or MultiMediaCard attached to an SDHCI - /// controller - /// - /// List of commands - /// Duration to execute all commands, in milliseconds - /// Set to true if any of the commands returned an error status, false otherwise - /// Maximum allowed time to execute a single command - /// 0 if no error occurred, otherwise, errno - public int SendMultipleMmcCommands(MmcSingleCommand[] commands, out double duration, out bool sense, - uint timeout = 15) - { - // We need a timeout - if(timeout == 0) - timeout = Timeout > 0 ? Timeout : 15; - - if(_remote is null) - return Command.SendMultipleMmcCommands(PlatformId, FileHandle, commands, out duration, out sense, - timeout); - - if(_remote.ServerProtocolVersion >= 2) - return _remote.SendMultipleMmcCommands(commands, out duration, out sense, timeout); - - int error = 0; - duration = 0; - sense = false; - - foreach(MmcSingleCommand command in commands) + case MmcCommands.SendCsd when _cachedCid != null: { - int singleError = _remote.SendMmcCommand(command.command, command.write, command.isApplication, - command.flags, command.argument, command.blockSize, - command.blocks, ref command.buffer, out command.response, - out double cmdDuration, out bool cmdSense, timeout); + DateTime start = DateTime.Now; + buffer = new byte[_cachedCsd.Length]; + Array.Copy(_cachedCsd, buffer, buffer.Length); + response = new uint[4]; + sense = false; + DateTime end = DateTime.Now; + duration = (end - start).TotalMilliseconds; - if(error == 0 && - singleError != 0) - error = singleError; - - duration += cmdDuration; - - if(cmdSense) - sense = true; + return 0; } + case (MmcCommands)SecureDigitalCommands.SendScr when _cachedScr != null: + { + DateTime start = DateTime.Now; + buffer = new byte[_cachedScr.Length]; + Array.Copy(_cachedScr, buffer, buffer.Length); + response = new uint[4]; + sense = false; + DateTime end = DateTime.Now; + duration = (end - start).TotalMilliseconds; - return error; + return 0; + } + case (MmcCommands)SecureDigitalCommands.SendOperatingCondition when _cachedOcr != null: + case MmcCommands.SendOpCond when _cachedOcr != null: + { + DateTime start = DateTime.Now; + buffer = new byte[_cachedOcr.Length]; + Array.Copy(_cachedOcr, buffer, buffer.Length); + response = new uint[4]; + sense = false; + DateTime end = DateTime.Now; + duration = (end - start).TotalMilliseconds; + + return 0; + } } - /// Closes then immediately reopens a device - /// Returned error number if any - public bool ReOpen() + return _remote is null + ? Command.SendMmcCommand(PlatformId, FileHandle, command, write, isApplication, flags, argument, + blockSize, blocks, ref buffer, out response, out duration, out sense, + timeout) : _remote.SendMmcCommand(command, write, isApplication, flags, + argument, blockSize, blocks, ref buffer, out response, out duration, out sense, + timeout); + } + + /// Encapsulates a single MMC command to send in a queue + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "MemberCanBeInternal")] + public class MmcSingleCommand + { + /// Command argument + public uint argument; + /// How many blocks to transfer + public uint blocks; + /// Size of block in bytes + public uint blockSize; + /// Buffer for MMC/SD command response + public byte[] buffer; + /// MMC/SD opcode + public MmcCommands command; + /// Flags indicating kind and place of response + public MmcFlags flags; + /// True if command should be preceded with CMD55 + public bool isApplication; + /// Response registers + public uint[] response; + /// True if data is sent from host to card + public bool write; + } + + /// + /// Concatenates a queue of commands to be send to a remote SecureDigital or MultiMediaCard attached to an SDHCI + /// controller + /// + /// List of commands + /// Duration to execute all commands, in milliseconds + /// Set to true if any of the commands returned an error status, false otherwise + /// Maximum allowed time to execute a single command + /// 0 if no error occurred, otherwise, errno + public int SendMultipleMmcCommands(MmcSingleCommand[] commands, out double duration, out bool sense, + uint timeout = 15) + { + // We need a timeout + if(timeout == 0) + timeout = Timeout > 0 ? Timeout : 15; + + if(_remote is null) + return Command.SendMultipleMmcCommands(PlatformId, FileHandle, commands, out duration, out sense, + timeout); + + if(_remote.ServerProtocolVersion >= 2) + return _remote.SendMultipleMmcCommands(commands, out duration, out sense, timeout); + + int error = 0; + duration = 0; + sense = false; + + foreach(MmcSingleCommand command in commands) { - if(!(_remote is null)) - return _remote.ReOpen(); + int singleError = _remote.SendMmcCommand(command.command, command.write, command.isApplication, + command.flags, command.argument, command.blockSize, + command.blocks, ref command.buffer, out command.response, + out double cmdDuration, out bool cmdSense, timeout); - int ret = Command.ReOpen(PlatformId, _devicePath, FileHandle, out object newFileHandle); + if(error == 0 && + singleError != 0) + error = singleError; - FileHandle = newFileHandle; + duration += cmdDuration; - Error = ret != 0; - LastError = ret; - - return Error; + if(cmdSense) + sense = true; } - /// Reads data using operating system buffers. - /// Data buffer - /// Offset in remote device to start reading, in bytes - /// Number of bytes to read - /// Total time in milliseconds the reading took - /// true if there was an error, false otherwise - public bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration) - { - if(!(_remote is null)) - return _remote.BufferedOsRead(out buffer, offset, length, out duration); + return error; + } - int ret = Command.BufferedOsRead(PlatformId, FileHandle, out buffer, offset, length, out duration); + /// Closes then immediately reopens a device + /// Returned error number if any + public bool ReOpen() + { + if(!(_remote is null)) + return _remote.ReOpen(); - Error = ret != 0; - LastError = ret; + int ret = Command.ReOpen(PlatformId, _devicePath, FileHandle, out object newFileHandle); - return Error; - } + FileHandle = newFileHandle; + + Error = ret != 0; + LastError = ret; + + return Error; + } + + /// Reads data using operating system buffers. + /// Data buffer + /// Offset in remote device to start reading, in bytes + /// Number of bytes to read + /// Total time in milliseconds the reading took + /// true if there was an error, false otherwise + public bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration) + { + if(!(_remote is null)) + return _remote.BufferedOsRead(out buffer, offset, length, out duration); + + int ret = Command.BufferedOsRead(PlatformId, FileHandle, out buffer, offset, length, out duration); + + Error = ret != 0; + LastError = ret; + + return Error; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/Constructor.cs b/Aaru.Devices/Device/Constructor.cs index 1b9f75040..c812d2dbc 100644 --- a/Aaru.Devices/Device/Constructor.cs +++ b/Aaru.Devices/Device/Constructor.cs @@ -56,887 +56,886 @@ using Marshal = System.Runtime.InteropServices.Marshal; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; using VendorString = Aaru.Decoders.SecureDigital.VendorString; -namespace Aaru.Devices +namespace Aaru.Devices; + +/// Implements a device or media containing drive +[SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global"), + SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")] +public sealed partial class Device { - /// Implements a device or media containing drive - [SuppressMessage("ReSharper", "MemberCanBePrivate.Global"), SuppressMessage("ReSharper", "UnusedMember.Global"), - SuppressMessage("ReSharper", "UnusedMethodReturnValue.Global")] - public sealed partial class Device + /// Opens the device for sending direct commands + /// Device path + public Device(string devicePath) { - /// Opens the device for sending direct commands - /// Device path - public Device(string devicePath) + PlatformId = DetectOS.GetRealPlatformID(); + Timeout = 15; + Error = false; + IsRemovable = false; + _devicePath = devicePath; + + Uri aaruUri; + + try { - PlatformId = DetectOS.GetRealPlatformID(); - Timeout = 15; - Error = false; - IsRemovable = false; - _devicePath = devicePath; + aaruUri = new Uri(devicePath); + } + catch(Exception) + { + // Ignore, treat as local path below + aaruUri = null; + } - Uri aaruUri; + if(aaruUri?.Scheme == "dic" || + aaruUri?.Scheme == "aaru") + { + devicePath = aaruUri.AbsolutePath; - try + if(devicePath.StartsWith('/')) + devicePath = devicePath.Substring(1); + + if(devicePath.StartsWith("dev", StringComparison.Ordinal)) + devicePath = $"/{devicePath}"; + + _remote = new Remote.Remote(aaruUri); + + Error = !_remote.Open(devicePath, out int errno); + LastError = errno; + } + else + { + switch(PlatformId) { - aaruUri = new Uri(devicePath); - } - catch(Exception) - { - // Ignore, treat as local path below - aaruUri = null; - } - - if(aaruUri?.Scheme == "dic" || - aaruUri?.Scheme == "aaru") - { - devicePath = aaruUri.AbsolutePath; - - if(devicePath.StartsWith('/')) - devicePath = devicePath.Substring(1); - - if(devicePath.StartsWith("dev", StringComparison.Ordinal)) - devicePath = $"/{devicePath}"; - - _remote = new Remote.Remote(aaruUri); - - Error = !_remote.Open(devicePath, out int errno); - LastError = errno; - } - else - { - switch(PlatformId) + case PlatformID.Win32NT: { - case PlatformID.Win32NT: + FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, + FileShare.Read | FileShare.Write, IntPtr.Zero, + FileMode.OpenExisting, FileAttributes.Normal, IntPtr.Zero); + + if(((SafeFileHandle)FileHandle).IsInvalid) { - FileHandle = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, - FileShare.Read | FileShare.Write, IntPtr.Zero, - FileMode.OpenExisting, FileAttributes.Normal, IntPtr.Zero); - - if(((SafeFileHandle)FileHandle).IsInvalid) - { - Error = true; - LastError = Marshal.GetLastWin32Error(); - } - - break; + Error = true; + LastError = Marshal.GetLastWin32Error(); } - case PlatformID.Linux: - { - FileHandle = - Linux.Extern.open(devicePath, - FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); - if((int)FileHandle < 0) - { - LastError = Marshal.GetLastWin32Error(); - - if(LastError == 13 || - LastError == 30) // EACCES or EROFS - { - FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); - - if((int)FileHandle < 0) - { - Error = true; - LastError = Marshal.GetLastWin32Error(); - } - } - else - { - Error = true; - } - - LastError = Marshal.GetLastWin32Error(); - } - - break; - } - default: throw new DeviceException($"Platform {PlatformId} not supported."); + break; } - } + case PlatformID.Linux: + { + FileHandle = + Linux.Extern.open(devicePath, + FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); - if(Error) - throw new DeviceException(LastError); + if((int)FileHandle < 0) + { + LastError = Marshal.GetLastWin32Error(); - // Seems ioctl(2) does not allow the atomicity needed - if(_remote is null) - { - if(PlatformId == PlatformID.Linux) - _readMultipleBlockCannotSetBlockCount = true; + if(LastError == 13 || + LastError == 30) // EACCES or EROFS + { + FileHandle = Linux.Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); + + if((int)FileHandle < 0) + { + Error = true; + LastError = Marshal.GetLastWin32Error(); + } + } + else + { + Error = true; + } + + LastError = Marshal.GetLastWin32Error(); + } + + break; + } + default: throw new DeviceException($"Platform {PlatformId} not supported."); } - else if(_remote.ServerOperatingSystem == "Linux") + } + + if(Error) + throw new DeviceException(LastError); + + // Seems ioctl(2) does not allow the atomicity needed + if(_remote is null) + { + if(PlatformId == PlatformID.Linux) _readMultipleBlockCannotSetBlockCount = true; + } + else if(_remote.ServerOperatingSystem == "Linux") + _readMultipleBlockCannotSetBlockCount = true; - Type = DeviceType.Unknown; - ScsiType = PeripheralDeviceTypes.UnknownDevice; + Type = DeviceType.Unknown; + ScsiType = PeripheralDeviceTypes.UnknownDevice; - byte[] ataBuf; - byte[] inqBuf = null; + byte[] ataBuf; + byte[] inqBuf = null; - if(Error) - throw new DeviceException(LastError); + if(Error) + throw new DeviceException(LastError); - bool scsiSense = true; + bool scsiSense = true; - if(_remote is null) + if(_remote is null) + { + // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first + switch(PlatformId) { - // Windows is answering SCSI INQUIRY for all device types so it needs to be detected first - switch(PlatformId) - { - case PlatformID.Win32NT: - var query = new StoragePropertyQuery(); - query.PropertyId = StoragePropertyId.Device; - query.QueryType = StorageQueryType.Standard; - query.AdditionalParameters = new byte[1]; + case PlatformID.Win32NT: + var query = new StoragePropertyQuery(); + query.PropertyId = StoragePropertyId.Device; + query.QueryType = StorageQueryType.Standard; + query.AdditionalParameters = new byte[1]; - IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); - byte[] descriptorB = new byte[1000]; + IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); + byte[] descriptorB = new byte[1000]; - uint returned = 0; - int error = 0; + uint returned = 0; + int error = 0; - bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)FileHandle, - WindowsIoctl.IoctlStorageQueryProperty, - ref query, (uint)Marshal.SizeOf(query), - descriptorPtr, 1000, ref returned, - IntPtr.Zero); + bool hasError = !Extern.DeviceIoControlStorageQuery((SafeFileHandle)FileHandle, + WindowsIoctl.IoctlStorageQueryProperty, + ref query, (uint)Marshal.SizeOf(query), + descriptorPtr, 1000, ref returned, + IntPtr.Zero); - if(hasError) - error = Marshal.GetLastWin32Error(); + if(hasError) + error = Marshal.GetLastWin32Error(); - Marshal.Copy(descriptorPtr, descriptorB, 0, 1000); + Marshal.Copy(descriptorPtr, descriptorB, 0, 1000); - if(!hasError && - error == 0) + if(!hasError && + error == 0) + { + var descriptor = new StorageDeviceDescriptor { - var descriptor = new StorageDeviceDescriptor - { - Version = BitConverter.ToUInt32(descriptorB, 0), - Size = BitConverter.ToUInt32(descriptorB, 4), - DeviceType = descriptorB[8], - DeviceTypeModifier = descriptorB[9], - RemovableMedia = descriptorB[10] > 0, - CommandQueueing = descriptorB[11] > 0, - VendorIdOffset = BitConverter.ToInt32(descriptorB, 12), - ProductIdOffset = BitConverter.ToInt32(descriptorB, 16), - ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20), - SerialNumberOffset = BitConverter.ToInt32(descriptorB, 24), - BusType = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28), - RawPropertiesLength = BitConverter.ToUInt32(descriptorB, 32) - }; + Version = BitConverter.ToUInt32(descriptorB, 0), + Size = BitConverter.ToUInt32(descriptorB, 4), + DeviceType = descriptorB[8], + DeviceTypeModifier = descriptorB[9], + RemovableMedia = descriptorB[10] > 0, + CommandQueueing = descriptorB[11] > 0, + VendorIdOffset = BitConverter.ToInt32(descriptorB, 12), + ProductIdOffset = BitConverter.ToInt32(descriptorB, 16), + ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20), + SerialNumberOffset = BitConverter.ToInt32(descriptorB, 24), + BusType = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28), + RawPropertiesLength = BitConverter.ToUInt32(descriptorB, 32) + }; - descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength]; + descriptor.RawDeviceProperties = new byte[descriptor.RawPropertiesLength]; - Array.Copy(descriptorB, 36, descriptor.RawDeviceProperties, 0, - descriptor.RawPropertiesLength); + Array.Copy(descriptorB, 36, descriptor.RawDeviceProperties, 0, + descriptor.RawPropertiesLength); - switch(descriptor.BusType) - { - case StorageBusType.SCSI: - case StorageBusType.SSA: - case StorageBusType.Fibre: - case StorageBusType.iSCSI: - case StorageBusType.SAS: - Type = DeviceType.SCSI; - - break; - case StorageBusType.FireWire: - IsFireWire = true; - Type = DeviceType.SCSI; - - break; - case StorageBusType.USB: - IsUsb = true; - Type = DeviceType.SCSI; - - break; - case StorageBusType.ATAPI: - Type = DeviceType.ATAPI; - - break; - case StorageBusType.ATA: - case StorageBusType.SATA: - Type = DeviceType.ATA; - - break; - case StorageBusType.MultiMediaCard: - Type = DeviceType.MMC; - - break; - case StorageBusType.SecureDigital: - Type = DeviceType.SecureDigital; - - break; - case StorageBusType.NVMe: - Type = DeviceType.NVMe; - - break; - } - - switch(Type) - { - case DeviceType.SCSI: - case DeviceType.ATAPI: - scsiSense = ScsiInquiry(out inqBuf, out _); - - break; - case DeviceType.ATA: - bool atapiSense = AtapiIdentify(out ataBuf, out _); - - if(!atapiSense) - { - Type = DeviceType.ATAPI; - Identify.IdentifyDevice? ataid = Identify.Decode(ataBuf); - - if(ataid.HasValue) - scsiSense = ScsiInquiry(out inqBuf, out _); - } - else - { - Manufacturer = "ATA"; - } - - break; - } - } - - Marshal.FreeHGlobal(descriptorPtr); - - if(Windows.Command.IsSdhci((SafeFileHandle)FileHandle)) + switch(descriptor.BusType) { - byte[] sdBuffer = new byte[16]; + case StorageBusType.SCSI: + case StorageBusType.SSA: + case StorageBusType.Fibre: + case StorageBusType.iSCSI: + case StorageBusType.SAS: + Type = DeviceType.SCSI; - LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCsd, - false, false, - MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | - MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, - out _, out _, out bool sense); + break; + case StorageBusType.FireWire: + IsFireWire = true; + Type = DeviceType.SCSI; - if(!sense) - { - _cachedCsd = new byte[16]; - Array.Copy(sdBuffer, 0, _cachedCsd, 0, 16); - } + break; + case StorageBusType.USB: + IsUsb = true; + Type = DeviceType.SCSI; - sdBuffer = new byte[16]; + break; + case StorageBusType.ATAPI: + Type = DeviceType.ATAPI; - LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCid, - false, false, - MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | - MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, - out _, out _, out sense); + break; + case StorageBusType.ATA: + case StorageBusType.SATA: + Type = DeviceType.ATA; - if(!sense) - { - _cachedCid = new byte[16]; - Array.Copy(sdBuffer, 0, _cachedCid, 0, 16); - } + break; + case StorageBusType.MultiMediaCard: + Type = DeviceType.MMC; - sdBuffer = new byte[8]; + break; + case StorageBusType.SecureDigital: + Type = DeviceType.SecureDigital; - LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, - (MmcCommands)SecureDigitalCommands.SendScr, - false, true, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | - MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, - out _, out _, out sense); + break; + case StorageBusType.NVMe: + Type = DeviceType.NVMe; - if(!sense) - { - _cachedScr = new byte[8]; - Array.Copy(sdBuffer, 0, _cachedScr, 0, 8); - } - - sdBuffer = new byte[4]; - - LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, - _cachedScr != null - ? (MmcCommands)SecureDigitalCommands. - SendOperatingCondition - : MmcCommands.SendOpCond, false, true, - MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | - MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer, - out _, out _, out sense); - - if(!sense) - { - _cachedScr = new byte[4]; - Array.Copy(sdBuffer, 0, _cachedScr, 0, 4); - } - } - - break; - case PlatformID.Linux: - if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/st", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sg", StringComparison.Ordinal)) - { - scsiSense = ScsiInquiry(out inqBuf, out _); - } - - // MultiMediaCard and SecureDigital go here - else if(devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal)) - { - string devPath = devicePath.Substring(5); - - if(File.Exists("/sys/block/" + devPath + "/device/csd")) - { - int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/csd", - out _cachedCsd); - - if(len == 0) - _cachedCsd = null; - } - - if(File.Exists("/sys/block/" + devPath + "/device/cid")) - { - int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/cid", - out _cachedCid); - - if(len == 0) - _cachedCid = null; - } - - if(File.Exists("/sys/block/" + devPath + "/device/scr")) - { - int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/scr", - out _cachedScr); - - if(len == 0) - _cachedScr = null; - } - - if(File.Exists("/sys/block/" + devPath + "/device/ocr")) - { - int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/ocr", - out _cachedOcr); - - if(len == 0) - _cachedOcr = null; - } - } - - break; - default: - scsiSense = ScsiInquiry(out inqBuf, out _); - - break; - } - } - else - { - Type = _remote.GetDeviceType(); - - switch(Type) - { - case DeviceType.ATAPI: - case DeviceType.SCSI: - scsiSense = ScsiInquiry(out inqBuf, out _); - - break; - case DeviceType.SecureDigital: - case DeviceType.MMC: - if(!_remote.GetSdhciRegisters(out _cachedCsd, out _cachedCid, out _cachedOcr, out _cachedScr)) - { - Type = DeviceType.SCSI; - ScsiType = PeripheralDeviceTypes.DirectAccess; - } - - break; - } - } - - #region SecureDigital / MultiMediaCard - if(_cachedCid != null) - { - ScsiType = PeripheralDeviceTypes.DirectAccess; - IsRemovable = false; - - if(_cachedScr != null) - { - Type = DeviceType.SecureDigital; - CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(_cachedCid); - Manufacturer = VendorString.Prettify(decoded.Manufacturer); - Model = decoded.ProductName; - - FirmwareRevision = - $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; - - Serial = $"{decoded.ProductSerialNumber}"; - } - else - { - Type = DeviceType.MMC; - Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(_cachedCid); - Manufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); - Model = decoded.ProductName; - - FirmwareRevision = - $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; - - Serial = $"{decoded.ProductSerialNumber}"; - } - - return; - } - #endregion SecureDigital / MultiMediaCard - - #region USB - if(_remote is null) - { - switch(PlatformId) - { - case PlatformID.Linux: - if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) - { - string devPath = devicePath.Substring(5); - - if(Directory.Exists("/sys/block/" + devPath)) - { - string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); - - if(!string.IsNullOrEmpty(resolvedLink)) - { - resolvedLink = "/sys" + resolvedLink.Substring(2); - - while(resolvedLink.Contains("usb")) - { - resolvedLink = Path.GetDirectoryName(resolvedLink); - - if(!File.Exists(resolvedLink + "/descriptors") || - !File.Exists(resolvedLink + "/idProduct") || - !File.Exists(resolvedLink + "/idVendor")) - continue; - - var usbFs = new FileStream(resolvedLink + "/descriptors", - System.IO.FileMode.Open, System.IO.FileAccess.Read); - - byte[] usbBuf = new byte[65536]; - int usbCount = usbFs.Read(usbBuf, 0, 65536); - UsbDescriptors = new byte[usbCount]; - Array.Copy(usbBuf, 0, UsbDescriptors, 0, usbCount); - usbFs.Close(); - - var usbSr = new StreamReader(resolvedLink + "/idProduct"); - string usbTemp = usbSr.ReadToEnd(); - - ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out _usbProduct); - - usbSr.Close(); - - usbSr = new StreamReader(resolvedLink + "/idVendor"); - usbTemp = usbSr.ReadToEnd(); - - ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out _usbVendor); - - usbSr.Close(); - - if(File.Exists(resolvedLink + "/manufacturer")) - { - usbSr = new StreamReader(resolvedLink + "/manufacturer"); - UsbManufacturerString = usbSr.ReadToEnd().Trim(); - usbSr.Close(); - } - - if(File.Exists(resolvedLink + "/product")) - { - usbSr = new StreamReader(resolvedLink + "/product"); - UsbProductString = usbSr.ReadToEnd().Trim(); - usbSr.Close(); - } - - if(File.Exists(resolvedLink + "/serial")) - { - usbSr = new StreamReader(resolvedLink + "/serial"); - UsbSerialString = usbSr.ReadToEnd().Trim(); - usbSr.Close(); - } - - IsUsb = true; - - break; - } - } - } - } - - break; - case PlatformID.Win32NT: - Usb.UsbDevice usbDevice = null; - - // I have to search for USB disks, floppies and CD-ROMs as separate device types - foreach(string devGuid in new[] - { - Usb.GuidDevinterfaceFloppy, Usb.GuidDevinterfaceCdrom, Usb.GuidDevinterfaceDisk, - Usb.GuidDevinterfaceTape - }) - { - usbDevice = Usb.FindDrivePath(devicePath, devGuid); - - if(usbDevice != null) break; } - if(usbDevice != null) + switch(Type) { - UsbDescriptors = usbDevice.BinaryDescriptors; - _usbVendor = (ushort)usbDevice.DeviceDescriptor.idVendor; - _usbProduct = (ushort)usbDevice.DeviceDescriptor.idProduct; - UsbManufacturerString = usbDevice.Manufacturer; - UsbProductString = usbDevice.Product; + case DeviceType.SCSI: + case DeviceType.ATAPI: + scsiSense = ScsiInquiry(out inqBuf, out _); - UsbSerialString = - usbDevice. - SerialNumber; // This is incorrect filled by Windows with SCSI/ATA serial number + break; + case DeviceType.ATA: + bool atapiSense = AtapiIdentify(out ataBuf, out _); + + if(!atapiSense) + { + Type = DeviceType.ATAPI; + Identify.IdentifyDevice? ataid = Identify.Decode(ataBuf); + + if(ataid.HasValue) + scsiSense = ScsiInquiry(out inqBuf, out _); + } + else + { + Manufacturer = "ATA"; + } + + break; + } + } + + Marshal.FreeHGlobal(descriptorPtr); + + if(Windows.Command.IsSdhci((SafeFileHandle)FileHandle)) + { + byte[] sdBuffer = new byte[16]; + + LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCsd, + false, false, + MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | + MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, + out _, out _, out bool sense); + + if(!sense) + { + _cachedCsd = new byte[16]; + Array.Copy(sdBuffer, 0, _cachedCsd, 0, 16); } - break; - default: - IsUsb = false; + sdBuffer = new byte[16]; - break; - } - } - else - { - if(_remote.GetUsbData(out byte[] remoteUsbDescriptors, out ushort remoteUsbVendor, - out ushort remoteUsbProduct, out string remoteUsbManufacturer, - out string remoteUsbProductString, out string remoteUsbSerial)) - { - IsUsb = true; - UsbDescriptors = remoteUsbDescriptors; - _usbVendor = remoteUsbVendor; - _usbProduct = remoteUsbProduct; - UsbManufacturerString = remoteUsbManufacturer; - UsbProductString = remoteUsbProductString; - UsbSerialString = remoteUsbSerial; - } - } - #endregion USB + LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, MmcCommands.SendCid, + false, false, + MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | + MmcFlags.CommandAc, 0, 16, 1, ref sdBuffer, + out _, out _, out sense); - #region FireWire - if(!(_remote is null)) - { - if(_remote.GetFireWireData(out _firewireVendor, out _firewireModel, out _firewireGuid, - out string remoteFireWireVendorName, out string remoteFireWireModelName)) - { - IsFireWire = true; - FireWireVendorName = remoteFireWireVendorName; - FireWireModelName = remoteFireWireModelName; - } - } - else - { - if(PlatformId == PlatformID.Linux) - { + if(!sense) + { + _cachedCid = new byte[16]; + Array.Copy(sdBuffer, 0, _cachedCid, 0, 16); + } + + sdBuffer = new byte[8]; + + LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, + (MmcCommands)SecureDigitalCommands.SendScr, + false, true, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | + MmcFlags.CommandAdtc, 0, 8, 1, ref sdBuffer, + out _, out _, out sense); + + if(!sense) + { + _cachedScr = new byte[8]; + Array.Copy(sdBuffer, 0, _cachedScr, 0, 8); + } + + sdBuffer = new byte[4]; + + LastError = Windows.Command.SendMmcCommand((SafeFileHandle)FileHandle, + _cachedScr != null + ? (MmcCommands)SecureDigitalCommands. + SendOperatingCondition + : MmcCommands.SendOpCond, false, true, + MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | + MmcFlags.CommandBcr, 0, 4, 1, ref sdBuffer, + out _, out _, out sense); + + if(!sense) + { + _cachedScr = new byte[4]; + Array.Copy(sdBuffer, 0, _cachedScr, 0, 4); + } + } + + break; + case PlatformID.Linux: if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) + devicePath.StartsWith("/dev/st", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sg", StringComparison.Ordinal)) + { + scsiSense = ScsiInquiry(out inqBuf, out _); + } + + // MultiMediaCard and SecureDigital go here + else if(devicePath.StartsWith("/dev/mmcblk", StringComparison.Ordinal)) { string devPath = devicePath.Substring(5); - if(Directory.Exists("/sys/block/" + devPath)) + if(File.Exists("/sys/block/" + devPath + "/device/csd")) { - string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); - resolvedLink = "/sys" + resolvedLink.Substring(2); + int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/csd", + out _cachedCsd); - if(!string.IsNullOrEmpty(resolvedLink)) - while(resolvedLink.Contains("firewire")) - { - resolvedLink = Path.GetDirectoryName(resolvedLink); + if(len == 0) + _cachedCsd = null; + } - if(!File.Exists(resolvedLink + "/model") || - !File.Exists(resolvedLink + "/vendor") || - !File.Exists(resolvedLink + "/guid")) - continue; + if(File.Exists("/sys/block/" + devPath + "/device/cid")) + { + int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/cid", + out _cachedCid); - var fwSr = new StreamReader(resolvedLink + "/model"); - string fwTemp = fwSr.ReadToEnd(); + if(len == 0) + _cachedCid = null; + } - uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out _firewireModel); + if(File.Exists("/sys/block/" + devPath + "/device/scr")) + { + int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/scr", + out _cachedScr); - fwSr.Close(); + if(len == 0) + _cachedScr = null; + } - fwSr = new StreamReader(resolvedLink + "/vendor"); - fwTemp = fwSr.ReadToEnd(); + if(File.Exists("/sys/block/" + devPath + "/device/ocr")) + { + int len = ConvertFromFileHexAscii("/sys/block/" + devPath + "/device/ocr", + out _cachedOcr); - uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out _firewireVendor); - - fwSr.Close(); - - fwSr = new StreamReader(resolvedLink + "/guid"); - fwTemp = fwSr.ReadToEnd(); - - ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, - out _firewireGuid); - - fwSr.Close(); - - if(File.Exists(resolvedLink + "/model_name")) - { - fwSr = new StreamReader(resolvedLink + "/model_name"); - FireWireModelName = fwSr.ReadToEnd().Trim(); - fwSr.Close(); - } - - if(File.Exists(resolvedLink + "/vendor_name")) - { - fwSr = new StreamReader(resolvedLink + "/vendor_name"); - FireWireVendorName = fwSr.ReadToEnd().Trim(); - fwSr.Close(); - } - - IsFireWire = true; - - break; - } + if(len == 0) + _cachedOcr = null; } } - } - // TODO: Implement for other operating systems - else - { - IsFireWire = false; - } + break; + default: + scsiSense = ScsiInquiry(out inqBuf, out _); + + break; } - #endregion FireWire - - #region PCMCIA - if(_remote is null) - { - if(PlatformId == PlatformID.Linux) - { - if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || - devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) - { - string devPath = devicePath.Substring(5); - - if(Directory.Exists("/sys/block/" + devPath)) - { - string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); - resolvedLink = "/sys" + resolvedLink.Substring(2); - - if(!string.IsNullOrEmpty(resolvedLink)) - while(resolvedLink.Contains("/sys/devices")) - { - resolvedLink = Path.GetDirectoryName(resolvedLink); - - if(!Directory.Exists(resolvedLink + "/pcmcia_socket")) - continue; - - string[] subdirs = Directory.GetDirectories(resolvedLink + "/pcmcia_socket", - "pcmcia_socket*", - SearchOption.TopDirectoryOnly); - - if(subdirs.Length <= 0) - continue; - - string possibleDir = Path.Combine(resolvedLink, "pcmcia_socket", subdirs[0]); - - if(!File.Exists(possibleDir + "/card_type") || - !File.Exists(possibleDir + "/cis")) - continue; - - var cisFs = new FileStream(possibleDir + "/cis", System.IO.FileMode.Open, - System.IO.FileAccess.Read); - - byte[] cisBuf = new byte[65536]; - int cisCount = cisFs.Read(cisBuf, 0, 65536); - Cis = new byte[cisCount]; - Array.Copy(cisBuf, 0, Cis, 0, cisCount); - cisFs.Close(); - - IsPcmcia = true; - - break; - } - } - } - } - - // TODO: Implement for other operating systems - else - { - IsPcmcia = false; - } - } - else - { - if(_remote.GetPcmciaData(out byte[] cisBuf)) - { - IsPcmcia = true; - Cis = cisBuf; - } - } - #endregion PCMCIA - - if(!scsiSense) - { - Inquiry? inquiry = Inquiry.Decode(inqBuf); - - Type = DeviceType.SCSI; - bool serialSense = ScsiInquiry(out inqBuf, out _, 0x80); - - if(!serialSense) - Serial = EVPD.DecodePage80(inqBuf); - - if(inquiry.HasValue) - { - string tmp = StringHandlers.CToString(inquiry.Value.ProductRevisionLevel); - - if(tmp != null) - FirmwareRevision = tmp.Trim(); - - tmp = StringHandlers.CToString(inquiry.Value.ProductIdentification); - - if(tmp != null) - Model = tmp.Trim(); - - tmp = StringHandlers.CToString(inquiry.Value.VendorIdentification); - - if(tmp != null) - Manufacturer = tmp.Trim(); - - IsRemovable = inquiry.Value.RMB; - - ScsiType = (PeripheralDeviceTypes)inquiry.Value.PeripheralDeviceType; - } - - bool atapiSense = AtapiIdentify(out ataBuf, out _); - - if(!atapiSense) - { - Type = DeviceType.ATAPI; - Identify.IdentifyDevice? ataId = Identify.Decode(ataBuf); - - if(ataId.HasValue) - Serial = ataId.Value.SerialNumber; - } - - LastError = 0; - Error = false; - } - - if((scsiSense && !(IsUsb || IsFireWire)) || - Manufacturer == "ATA") - { - bool ataSense = AtaIdentify(out ataBuf, out _); - - if(!ataSense) - { - Type = DeviceType.ATA; - Identify.IdentifyDevice? ataid = Identify.Decode(ataBuf); - - if(ataid.HasValue) - { - string[] separated = ataid.Value.Model.Split(' '); - - if(separated.Length == 1) - { - Model = separated[0]; - } - else - { - Manufacturer = separated[0]; - Model = separated[^1]; - } - - FirmwareRevision = ataid.Value.FirmwareRevision; - Serial = ataid.Value.SerialNumber; - - ScsiType = PeripheralDeviceTypes.DirectAccess; - - if((ushort)ataid.Value.GeneralConfiguration != 0x848A) - IsRemovable |= - (ataid.Value.GeneralConfiguration & Identify.GeneralConfigurationBit.Removable) == - Identify.GeneralConfigurationBit.Removable; - else - IsCompactFlash = true; - } - } - } - - if(Type == DeviceType.Unknown) - { - Manufacturer = null; - Model = null; - FirmwareRevision = null; - Serial = null; - } - - if(IsUsb) - { - if(string.IsNullOrEmpty(Manufacturer)) - Manufacturer = UsbManufacturerString; - - if(string.IsNullOrEmpty(Model)) - Model = UsbProductString; - - if(string.IsNullOrEmpty(Serial)) - Serial = UsbSerialString; - else - foreach(char c in Serial.Where(c => !char.IsControl(c))) - Serial = $"{Serial}{(uint)c:X2}"; - } - - if(IsFireWire) - { - if(string.IsNullOrEmpty(Manufacturer)) - Manufacturer = FireWireVendorName; - - if(string.IsNullOrEmpty(Model)) - Model = FireWireModelName; - - if(string.IsNullOrEmpty(Serial)) - Serial = $"{_firewireGuid:X16}"; - else - foreach(char c in Serial.Where(c => !char.IsControl(c))) - Serial = $"{Serial}{(uint)c:X2}"; - } - - // Some optical drives are not getting the correct serial, and IDENTIFY PACKET DEVICE is blocked without - // administrator privileges - if(ScsiType != PeripheralDeviceTypes.MultiMediaDevice) - return; - - bool featureSense = GetConfiguration(out byte[] featureBuffer, out _, 0x0108, MmcGetConfigurationRt.Single, - Timeout, out _); - - if(featureSense) - return; - - Features.SeparatedFeatures features = Features.Separate(featureBuffer); - - if(features.Descriptors?.Length != 1 || - features.Descriptors[0].Code != 0x0108) - return; - - Feature_0108? serialFeature = Features.Decode_0108(features.Descriptors[0].Data); - - if(serialFeature is null) - return; - - Serial = serialFeature.Value.Serial; } - - static int ConvertFromFileHexAscii(string file, out byte[] outBuf) + else { - var sr = new StreamReader(file); - string ins = sr.ReadToEnd().Trim(); + Type = _remote.GetDeviceType(); - int count = Helpers.Marshal.ConvertFromHexAscii(ins, out outBuf); + switch(Type) + { + case DeviceType.ATAPI: + case DeviceType.SCSI: + scsiSense = ScsiInquiry(out inqBuf, out _); - sr.Close(); + break; + case DeviceType.SecureDigital: + case DeviceType.MMC: + if(!_remote.GetSdhciRegisters(out _cachedCsd, out _cachedCid, out _cachedOcr, out _cachedScr)) + { + Type = DeviceType.SCSI; + ScsiType = PeripheralDeviceTypes.DirectAccess; + } - return count; + break; + } } + + #region SecureDigital / MultiMediaCard + if(_cachedCid != null) + { + ScsiType = PeripheralDeviceTypes.DirectAccess; + IsRemovable = false; + + if(_cachedScr != null) + { + Type = DeviceType.SecureDigital; + CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(_cachedCid); + Manufacturer = VendorString.Prettify(decoded.Manufacturer); + Model = decoded.ProductName; + + FirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + Serial = $"{decoded.ProductSerialNumber}"; + } + else + { + Type = DeviceType.MMC; + Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(_cachedCid); + Manufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); + Model = decoded.ProductName; + + FirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + Serial = $"{decoded.ProductSerialNumber}"; + } + + return; + } + #endregion SecureDigital / MultiMediaCard + + #region USB + if(_remote is null) + { + switch(PlatformId) + { + case PlatformID.Linux: + if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) + { + string devPath = devicePath.Substring(5); + + if(Directory.Exists("/sys/block/" + devPath)) + { + string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); + + if(!string.IsNullOrEmpty(resolvedLink)) + { + resolvedLink = "/sys" + resolvedLink.Substring(2); + + while(resolvedLink.Contains("usb")) + { + resolvedLink = Path.GetDirectoryName(resolvedLink); + + if(!File.Exists(resolvedLink + "/descriptors") || + !File.Exists(resolvedLink + "/idProduct") || + !File.Exists(resolvedLink + "/idVendor")) + continue; + + var usbFs = new FileStream(resolvedLink + "/descriptors", + System.IO.FileMode.Open, System.IO.FileAccess.Read); + + byte[] usbBuf = new byte[65536]; + int usbCount = usbFs.Read(usbBuf, 0, 65536); + UsbDescriptors = new byte[usbCount]; + Array.Copy(usbBuf, 0, UsbDescriptors, 0, usbCount); + usbFs.Close(); + + var usbSr = new StreamReader(resolvedLink + "/idProduct"); + string usbTemp = usbSr.ReadToEnd(); + + ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out _usbProduct); + + usbSr.Close(); + + usbSr = new StreamReader(resolvedLink + "/idVendor"); + usbTemp = usbSr.ReadToEnd(); + + ushort.TryParse(usbTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out _usbVendor); + + usbSr.Close(); + + if(File.Exists(resolvedLink + "/manufacturer")) + { + usbSr = new StreamReader(resolvedLink + "/manufacturer"); + UsbManufacturerString = usbSr.ReadToEnd().Trim(); + usbSr.Close(); + } + + if(File.Exists(resolvedLink + "/product")) + { + usbSr = new StreamReader(resolvedLink + "/product"); + UsbProductString = usbSr.ReadToEnd().Trim(); + usbSr.Close(); + } + + if(File.Exists(resolvedLink + "/serial")) + { + usbSr = new StreamReader(resolvedLink + "/serial"); + UsbSerialString = usbSr.ReadToEnd().Trim(); + usbSr.Close(); + } + + IsUsb = true; + + break; + } + } + } + } + + break; + case PlatformID.Win32NT: + Usb.UsbDevice usbDevice = null; + + // I have to search for USB disks, floppies and CD-ROMs as separate device types + foreach(string devGuid in new[] + { + Usb.GuidDevinterfaceFloppy, Usb.GuidDevinterfaceCdrom, Usb.GuidDevinterfaceDisk, + Usb.GuidDevinterfaceTape + }) + { + usbDevice = Usb.FindDrivePath(devicePath, devGuid); + + if(usbDevice != null) + break; + } + + if(usbDevice != null) + { + UsbDescriptors = usbDevice.BinaryDescriptors; + _usbVendor = (ushort)usbDevice.DeviceDescriptor.idVendor; + _usbProduct = (ushort)usbDevice.DeviceDescriptor.idProduct; + UsbManufacturerString = usbDevice.Manufacturer; + UsbProductString = usbDevice.Product; + + UsbSerialString = + usbDevice. + SerialNumber; // This is incorrect filled by Windows with SCSI/ATA serial number + } + + break; + default: + IsUsb = false; + + break; + } + } + else + { + if(_remote.GetUsbData(out byte[] remoteUsbDescriptors, out ushort remoteUsbVendor, + out ushort remoteUsbProduct, out string remoteUsbManufacturer, + out string remoteUsbProductString, out string remoteUsbSerial)) + { + IsUsb = true; + UsbDescriptors = remoteUsbDescriptors; + _usbVendor = remoteUsbVendor; + _usbProduct = remoteUsbProduct; + UsbManufacturerString = remoteUsbManufacturer; + UsbProductString = remoteUsbProductString; + UsbSerialString = remoteUsbSerial; + } + } + #endregion USB + + #region FireWire + if(!(_remote is null)) + { + if(_remote.GetFireWireData(out _firewireVendor, out _firewireModel, out _firewireGuid, + out string remoteFireWireVendorName, out string remoteFireWireModelName)) + { + IsFireWire = true; + FireWireVendorName = remoteFireWireVendorName; + FireWireModelName = remoteFireWireModelName; + } + } + else + { + if(PlatformId == PlatformID.Linux) + { + if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) + { + string devPath = devicePath.Substring(5); + + if(Directory.Exists("/sys/block/" + devPath)) + { + string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); + resolvedLink = "/sys" + resolvedLink.Substring(2); + + if(!string.IsNullOrEmpty(resolvedLink)) + while(resolvedLink.Contains("firewire")) + { + resolvedLink = Path.GetDirectoryName(resolvedLink); + + if(!File.Exists(resolvedLink + "/model") || + !File.Exists(resolvedLink + "/vendor") || + !File.Exists(resolvedLink + "/guid")) + continue; + + var fwSr = new StreamReader(resolvedLink + "/model"); + string fwTemp = fwSr.ReadToEnd(); + + uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out _firewireModel); + + fwSr.Close(); + + fwSr = new StreamReader(resolvedLink + "/vendor"); + fwTemp = fwSr.ReadToEnd(); + + uint.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out _firewireVendor); + + fwSr.Close(); + + fwSr = new StreamReader(resolvedLink + "/guid"); + fwTemp = fwSr.ReadToEnd(); + + ulong.TryParse(fwTemp, NumberStyles.HexNumber, CultureInfo.InvariantCulture, + out _firewireGuid); + + fwSr.Close(); + + if(File.Exists(resolvedLink + "/model_name")) + { + fwSr = new StreamReader(resolvedLink + "/model_name"); + FireWireModelName = fwSr.ReadToEnd().Trim(); + fwSr.Close(); + } + + if(File.Exists(resolvedLink + "/vendor_name")) + { + fwSr = new StreamReader(resolvedLink + "/vendor_name"); + FireWireVendorName = fwSr.ReadToEnd().Trim(); + fwSr.Close(); + } + + IsFireWire = true; + + break; + } + } + } + } + + // TODO: Implement for other operating systems + else + { + IsFireWire = false; + } + } + #endregion FireWire + + #region PCMCIA + if(_remote is null) + { + if(PlatformId == PlatformID.Linux) + { + if(devicePath.StartsWith("/dev/sd", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/sr", StringComparison.Ordinal) || + devicePath.StartsWith("/dev/st", StringComparison.Ordinal)) + { + string devPath = devicePath.Substring(5); + + if(Directory.Exists("/sys/block/" + devPath)) + { + string resolvedLink = Linux.Command.ReadLink("/sys/block/" + devPath); + resolvedLink = "/sys" + resolvedLink.Substring(2); + + if(!string.IsNullOrEmpty(resolvedLink)) + while(resolvedLink.Contains("/sys/devices")) + { + resolvedLink = Path.GetDirectoryName(resolvedLink); + + if(!Directory.Exists(resolvedLink + "/pcmcia_socket")) + continue; + + string[] subdirs = Directory.GetDirectories(resolvedLink + "/pcmcia_socket", + "pcmcia_socket*", + SearchOption.TopDirectoryOnly); + + if(subdirs.Length <= 0) + continue; + + string possibleDir = Path.Combine(resolvedLink, "pcmcia_socket", subdirs[0]); + + if(!File.Exists(possibleDir + "/card_type") || + !File.Exists(possibleDir + "/cis")) + continue; + + var cisFs = new FileStream(possibleDir + "/cis", System.IO.FileMode.Open, + System.IO.FileAccess.Read); + + byte[] cisBuf = new byte[65536]; + int cisCount = cisFs.Read(cisBuf, 0, 65536); + Cis = new byte[cisCount]; + Array.Copy(cisBuf, 0, Cis, 0, cisCount); + cisFs.Close(); + + IsPcmcia = true; + + break; + } + } + } + } + + // TODO: Implement for other operating systems + else + { + IsPcmcia = false; + } + } + else + { + if(_remote.GetPcmciaData(out byte[] cisBuf)) + { + IsPcmcia = true; + Cis = cisBuf; + } + } + #endregion PCMCIA + + if(!scsiSense) + { + Inquiry? inquiry = Inquiry.Decode(inqBuf); + + Type = DeviceType.SCSI; + bool serialSense = ScsiInquiry(out inqBuf, out _, 0x80); + + if(!serialSense) + Serial = EVPD.DecodePage80(inqBuf); + + if(inquiry.HasValue) + { + string tmp = StringHandlers.CToString(inquiry.Value.ProductRevisionLevel); + + if(tmp != null) + FirmwareRevision = tmp.Trim(); + + tmp = StringHandlers.CToString(inquiry.Value.ProductIdentification); + + if(tmp != null) + Model = tmp.Trim(); + + tmp = StringHandlers.CToString(inquiry.Value.VendorIdentification); + + if(tmp != null) + Manufacturer = tmp.Trim(); + + IsRemovable = inquiry.Value.RMB; + + ScsiType = (PeripheralDeviceTypes)inquiry.Value.PeripheralDeviceType; + } + + bool atapiSense = AtapiIdentify(out ataBuf, out _); + + if(!atapiSense) + { + Type = DeviceType.ATAPI; + Identify.IdentifyDevice? ataId = Identify.Decode(ataBuf); + + if(ataId.HasValue) + Serial = ataId.Value.SerialNumber; + } + + LastError = 0; + Error = false; + } + + if((scsiSense && !(IsUsb || IsFireWire)) || + Manufacturer == "ATA") + { + bool ataSense = AtaIdentify(out ataBuf, out _); + + if(!ataSense) + { + Type = DeviceType.ATA; + Identify.IdentifyDevice? ataid = Identify.Decode(ataBuf); + + if(ataid.HasValue) + { + string[] separated = ataid.Value.Model.Split(' '); + + if(separated.Length == 1) + { + Model = separated[0]; + } + else + { + Manufacturer = separated[0]; + Model = separated[^1]; + } + + FirmwareRevision = ataid.Value.FirmwareRevision; + Serial = ataid.Value.SerialNumber; + + ScsiType = PeripheralDeviceTypes.DirectAccess; + + if((ushort)ataid.Value.GeneralConfiguration != 0x848A) + IsRemovable |= + (ataid.Value.GeneralConfiguration & Identify.GeneralConfigurationBit.Removable) == + Identify.GeneralConfigurationBit.Removable; + else + IsCompactFlash = true; + } + } + } + + if(Type == DeviceType.Unknown) + { + Manufacturer = null; + Model = null; + FirmwareRevision = null; + Serial = null; + } + + if(IsUsb) + { + if(string.IsNullOrEmpty(Manufacturer)) + Manufacturer = UsbManufacturerString; + + if(string.IsNullOrEmpty(Model)) + Model = UsbProductString; + + if(string.IsNullOrEmpty(Serial)) + Serial = UsbSerialString; + else + foreach(char c in Serial.Where(c => !char.IsControl(c))) + Serial = $"{Serial}{(uint)c:X2}"; + } + + if(IsFireWire) + { + if(string.IsNullOrEmpty(Manufacturer)) + Manufacturer = FireWireVendorName; + + if(string.IsNullOrEmpty(Model)) + Model = FireWireModelName; + + if(string.IsNullOrEmpty(Serial)) + Serial = $"{_firewireGuid:X16}"; + else + foreach(char c in Serial.Where(c => !char.IsControl(c))) + Serial = $"{Serial}{(uint)c:X2}"; + } + + // Some optical drives are not getting the correct serial, and IDENTIFY PACKET DEVICE is blocked without + // administrator privileges + if(ScsiType != PeripheralDeviceTypes.MultiMediaDevice) + return; + + bool featureSense = GetConfiguration(out byte[] featureBuffer, out _, 0x0108, MmcGetConfigurationRt.Single, + Timeout, out _); + + if(featureSense) + return; + + Features.SeparatedFeatures features = Features.Separate(featureBuffer); + + if(features.Descriptors?.Length != 1 || + features.Descriptors[0].Code != 0x0108) + return; + + Feature_0108? serialFeature = Features.Decode_0108(features.Descriptors[0].Data); + + if(serialFeature is null) + return; + + Serial = serialFeature.Value.Serial; + } + + static int ConvertFromFileHexAscii(string file, out byte[] outBuf) + { + var sr = new StreamReader(file); + string ins = sr.ReadToEnd().Trim(); + + int count = Helpers.Marshal.ConvertFromHexAscii(ins, out outBuf); + + sr.Close(); + + return count; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/Destructor.cs b/Aaru.Devices/Device/Destructor.cs index f717dc1ea..aa53c500f 100644 --- a/Aaru.Devices/Device/Destructor.cs +++ b/Aaru.Devices/Device/Destructor.cs @@ -34,43 +34,42 @@ using Aaru.CommonTypes.Interop; using Aaru.Devices.Linux; using Microsoft.Win32.SafeHandles; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// + /// Releases unmanaged resources and performs other cleanup operations before the is + /// reclaimed by garbage collection. + /// + ~Device() => Close(); + + /// Closes a device + public void Close() { - /// - /// Releases unmanaged resources and performs other cleanup operations before the is - /// reclaimed by garbage collection. - /// - ~Device() => Close(); - - /// Closes a device - public void Close() + if(_remote != null) { - if(_remote != null) - { - _remote.Close(); - _remote.Disconnect(); + _remote.Close(); + _remote.Disconnect(); - return; - } - - if(FileHandle == null) - return; - - switch(PlatformId) - { - case PlatformID.Win32NT: - (FileHandle as SafeFileHandle)?.Close(); - - break; - case PlatformID.Linux: - Extern.close((int)FileHandle); - - break; - } - - FileHandle = null; + return; } + + if(FileHandle == null) + return; + + switch(PlatformId) + { + case PlatformID.Win32NT: + (FileHandle as SafeFileHandle)?.Close(); + + break; + case PlatformID.Linux: + Extern.close((int)FileHandle); + + break; + } + + FileHandle = null; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/DeviceException.cs b/Aaru.Devices/Device/DeviceException.cs index 5fcadbbf4..5f496763c 100644 --- a/Aaru.Devices/Device/DeviceException.cs +++ b/Aaru.Devices/Device/DeviceException.cs @@ -32,17 +32,16 @@ using System; -namespace Aaru.Devices +namespace Aaru.Devices; + +/// +/// Exception to be returned by the device constructor +public sealed class DeviceException : Exception { - /// - /// Exception to be returned by the device constructor - public sealed class DeviceException : Exception - { - internal DeviceException(string message) : base(message) {} + internal DeviceException(string message) : base(message) {} - internal DeviceException(int lastError) => LastError = lastError; + internal DeviceException(int lastError) => LastError = lastError; - /// Last error sent by the operating systen - public int LastError { get; } - } + /// Last error sent by the operating systen + public int LastError { get; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/List.cs b/Aaru.Devices/Device/List.cs index 97222cb52..1c988601a 100644 --- a/Aaru.Devices/Device/List.cs +++ b/Aaru.Devices/Device/List.cs @@ -36,111 +36,110 @@ using Aaru.CommonTypes.Interop; using Aaru.Console; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; -namespace Aaru.Devices +namespace Aaru.Devices; + +/// Contains device information +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct DeviceInfo { - /// Contains device information - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct DeviceInfo + /// Device path + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] + public string Path; + + /// Device vendor or manufacturer + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string Vendor; + + /// Device model or product name + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string Model; + + /// Device serial number + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string Serial; + + /// Bus the device is attached to + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string Bus; + + /// + /// Set to true if Aaru can send commands to the device in the current machine or remote, false + /// otherwise + /// + [MarshalAs(UnmanagedType.U1)] + public bool Supported; + + /// Padding + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] Padding; +} + +public sealed partial class Device +{ + /// Lists devices attached to current machine + /// List of devices + public static DeviceInfo[] ListDevices() => ListDevices(out _, out _, out _, out _, out _, out _); + + /// Lists devices attached to current machine or specified remote + /// Is remote + /// Remote application + /// Remote application version + /// Remote operating system name + /// Remote operating system version + /// Remote architecture + /// Remote URI + /// List of devices + /// + public static DeviceInfo[] ListDevices(out bool isRemote, out string serverApplication, + out string serverVersion, out string serverOperatingSystem, + out string serverOperatingSystemVersion, out string serverArchitecture, + string aaruRemote = null) { - /// Device path - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] - public string Path; + isRemote = false; + serverApplication = null; + serverVersion = null; + serverOperatingSystem = null; + serverOperatingSystemVersion = null; + serverArchitecture = null; - /// Device vendor or manufacturer - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string Vendor; - - /// Device model or product name - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string Model; - - /// Device serial number - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string Serial; - - /// Bus the device is attached to - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string Bus; - - /// - /// Set to true if Aaru can send commands to the device in the current machine or remote, false - /// otherwise - /// - [MarshalAs(UnmanagedType.U1)] - public bool Supported; - - /// Padding - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] Padding; - } - - public sealed partial class Device - { - /// Lists devices attached to current machine - /// List of devices - public static DeviceInfo[] ListDevices() => ListDevices(out _, out _, out _, out _, out _, out _); - - /// Lists devices attached to current machine or specified remote - /// Is remote - /// Remote application - /// Remote application version - /// Remote operating system name - /// Remote operating system version - /// Remote architecture - /// Remote URI - /// List of devices - /// - public static DeviceInfo[] ListDevices(out bool isRemote, out string serverApplication, - out string serverVersion, out string serverOperatingSystem, - out string serverOperatingSystemVersion, out string serverArchitecture, - string aaruRemote = null) - { - isRemote = false; - serverApplication = null; - serverVersion = null; - serverOperatingSystem = null; - serverOperatingSystemVersion = null; - serverArchitecture = null; - - if(aaruRemote is null) - switch(DetectOS.GetRealPlatformID()) - { - case PlatformID.Win32NT: return Windows.ListDevices.GetList(); - case PlatformID.Linux: return Linux.ListDevices.GetList(); - default: - throw new - InvalidOperationException($"Platform {DetectOS.GetRealPlatformID()} not yet supported."); - } - - try + if(aaruRemote is null) + switch(DetectOS.GetRealPlatformID()) { - var aaruUri = new Uri(aaruRemote); - - if(aaruUri.Scheme != "aaru" && - aaruUri.Scheme != "dic") - { - AaruConsole.ErrorWriteLine("Invalid remote URI."); - - return Array.Empty(); - } - - using var remote = new Remote.Remote(aaruUri); - - isRemote = true; - serverApplication = remote.ServerApplication; - serverVersion = remote.ServerVersion; - serverOperatingSystem = remote.ServerOperatingSystem; - serverOperatingSystemVersion = remote.ServerOperatingSystemVersion; - serverArchitecture = remote.ServerArchitecture; - - return remote.ListDevices(); + case PlatformID.Win32NT: return Windows.ListDevices.GetList(); + case PlatformID.Linux: return Linux.ListDevices.GetList(); + default: + throw new + InvalidOperationException($"Platform {DetectOS.GetRealPlatformID()} not yet supported."); } - catch(Exception) + + try + { + var aaruUri = new Uri(aaruRemote); + + if(aaruUri.Scheme != "aaru" && + aaruUri.Scheme != "dic") { - AaruConsole.ErrorWriteLine("Error connecting to host."); + AaruConsole.ErrorWriteLine("Invalid remote URI."); return Array.Empty(); } + + using var remote = new Remote.Remote(aaruUri); + + isRemote = true; + serverApplication = remote.ServerApplication; + serverVersion = remote.ServerVersion; + serverOperatingSystem = remote.ServerOperatingSystem; + serverOperatingSystemVersion = remote.ServerOperatingSystemVersion; + serverArchitecture = remote.ServerArchitecture; + + return remote.ListDevices(); + } + catch(Exception) + { + AaruConsole.ErrorWriteLine("Error connecting to host."); + + return Array.Empty(); } } } \ No newline at end of file diff --git a/Aaru.Devices/Device/MmcCommands/MMC.cs b/Aaru.Devices/Device/MmcCommands/MMC.cs index 71e3194c1..7046dd014 100644 --- a/Aaru.Devices/Device/MmcCommands/MMC.cs +++ b/Aaru.Devices/Device/MmcCommands/MMC.cs @@ -33,358 +33,357 @@ using System; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Reads the CSD register from a SecureDigital or MultiMediaCard device + /// Data buffer + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadCsd(out byte[] buffer, out uint[] response, uint timeout, out double duration) { - /// Reads the CSD register from a SecureDigital or MultiMediaCard device - /// Data buffer - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadCsd(out byte[] buffer, out uint[] response, uint timeout, out double duration) + buffer = new byte[16]; + + LastError = SendMmcCommand(MmcCommands.SendCsd, false, false, + MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16, 1, + ref buffer, out response, out duration, out bool sense, timeout); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("MMC Device", "SEND_CSD took {0} ms.", duration); + + return sense; + } + + /// Reads the CID register from a SecureDigital or MultiMediaCard device + /// Data buffer + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadCid(out byte[] buffer, out uint[] response, uint timeout, out double duration) + { + buffer = new byte[16]; + + LastError = SendMmcCommand(MmcCommands.SendCid, false, false, + MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16, 1, + ref buffer, out response, out duration, out bool sense, timeout); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("MMC Device", "SEND_CID took {0} ms.", duration); + + return sense; + } + + /// Reads the OCR register from a MultiMediaCard device + /// Data buffer + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadOcr(out byte[] buffer, out uint[] response, uint timeout, out double duration) + { + buffer = new byte[4]; + + LastError = SendMmcCommand(MmcCommands.SendOpCond, false, true, + MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0, 4, 1, + ref buffer, out response, out duration, out bool sense, timeout); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SecureDigital Device", "SEND_OP_COND took {0} ms.", duration); + + return sense; + } + + /// Reads the extended CSD from a MultiMediaCard device + /// Data buffer + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadExtendedCsd(out byte[] buffer, out uint[] response, uint timeout, out double duration) + { + buffer = new byte[512]; + + LastError = SendMmcCommand(MmcCommands.SendExtCsd, false, false, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0, 512, 1, + ref buffer, out response, out duration, out bool sense, timeout); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("MMC Device", "SEND_EXT_CSD took {0} ms.", duration); + + return sense; + } + + /// Sets the block length for transfers from a SecureDigital or MultiMediaCard device + /// Block length in bytes + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool SetBlockLength(uint length, out uint[] response, uint timeout, out double duration) + { + byte[] buffer = Array.Empty(); + + LastError = SendMmcCommand(MmcCommands.SetBlocklen, false, false, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAc, length, 0, 0, + ref buffer, out response, out duration, out bool sense, timeout); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("MMC Device", "SET_BLOCKLEN took {0} ms.", duration); + + return sense; + } + + /// Reads blocks from a SecureDigital or MultiMediaCard device + /// Data buffer + /// Response + /// LBA to start reading from + /// Block size in bytes + /// Number of bytes to transfer + /// Card is byte-addressed + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool Read(out byte[] buffer, out uint[] response, uint lba, uint blockSize, ushort transferLength, + bool byteAddressed, uint timeout, out double duration) + { + bool sense = true; + buffer = null; + response = null; + duration = -1; + + if(transferLength <= 1) + return ReadSingleBlock(out buffer, out response, lba, blockSize, byteAddressed, timeout, out duration); + + if(!_readMultipleBlockCannotSetBlockCount) + sense = ReadMultipleBlock(out buffer, out response, lba, blockSize, transferLength, byteAddressed, + timeout, out duration); + + if(_readMultipleBlockCannotSetBlockCount) + return ReadMultipleUsingSingle(out buffer, out response, lba, blockSize, transferLength, byteAddressed, + timeout, out duration); + + return sense; + } + + /// Reads a single block from a SecureDigital or MultiMediaCard device + /// Data buffer + /// Response + /// LBA to start reading from + /// Block size in bytes + /// Card is byte-addressed + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadSingleBlock(out byte[] buffer, out uint[] response, uint lba, uint blockSize, + bool byteAddressed, uint timeout, out double duration) + { + uint address; + buffer = new byte[blockSize]; + response = null; + + if(byteAddressed) + address = lba * blockSize; + else + address = lba; + + LastError = SendMmcCommand(MmcCommands.ReadSingleBlock, false, false, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, address, + blockSize, 1, ref buffer, out response, out duration, out bool sense, timeout); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("MMC Device", "READ_SINGLE_BLOCK took {0} ms.", duration); + + return sense; + } + + static bool _readMultipleBlockCannotSetBlockCount; + + /// Reads multiple blocks from a SecureDigital or MultiMediaCard device + /// Data buffer + /// Response + /// LBA to start reading from + /// Block size in bytes + /// Number of bytes to transfer + /// Card is byte-addressed + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadMultipleBlock(out byte[] buffer, out uint[] response, uint lba, uint blockSize, + ushort transferLength, bool byteAddressed, uint timeout, out double duration) + { + buffer = new byte[transferLength * blockSize]; + double setDuration = 0; + uint address; + response = null; + + if(byteAddressed) + address = lba * blockSize; + else + address = lba; + + LastError = SendMmcCommand(MmcCommands.ReadMultipleBlock, false, false, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, address, + blockSize, transferLength, ref buffer, out response, out duration, + out bool sense, timeout); + + Error = LastError != 0; + + if(transferLength > 1) { - buffer = new byte[16]; - - LastError = SendMmcCommand(MmcCommands.SendCsd, false, false, - MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16, 1, - ref buffer, out response, out duration, out bool sense, timeout); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("MMC Device", "SEND_CSD took {0} ms.", duration); - - return sense; + duration += setDuration; + AaruConsole.DebugWriteLine("MMC Device", "READ_MULTIPLE_BLOCK took {0} ms.", duration); } + else + AaruConsole.DebugWriteLine("MMC Device", "READ_SINGLE_BLOCK took {0} ms.", duration); - /// Reads the CID register from a SecureDigital or MultiMediaCard device - /// Data buffer - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadCid(out byte[] buffer, out uint[] response, uint timeout, out double duration) - { - buffer = new byte[16]; + return sense; + } - LastError = SendMmcCommand(MmcCommands.SendCid, false, false, - MmcFlags.ResponseSpiR2 | MmcFlags.ResponseR2 | MmcFlags.CommandAc, 0, 16, 1, - ref buffer, out response, out duration, out bool sense, timeout); + /// Reads blocks using a single block read from a SecureDigital or MultiMediaCard device + /// Data buffer + /// Response + /// LBA to start reading from + /// Block size in bytes + /// Number of bytes to transfer + /// Card is byte-addressed + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadMultipleUsingSingle(out byte[] buffer, out uint[] response, uint lba, uint blockSize, + ushort transferLength, bool byteAddressed, uint timeout, + out double duration) + { + buffer = new byte[transferLength * blockSize]; + byte[] blockBuffer = new byte[blockSize]; + duration = 0; + bool sense = true; + response = null; - Error = LastError != 0; - - AaruConsole.DebugWriteLine("MMC Device", "SEND_CID took {0} ms.", duration); - - return sense; - } - - /// Reads the OCR register from a MultiMediaCard device - /// Data buffer - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadOcr(out byte[] buffer, out uint[] response, uint timeout, out double duration) - { - buffer = new byte[4]; - - LastError = SendMmcCommand(MmcCommands.SendOpCond, false, true, - MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0, 4, 1, - ref buffer, out response, out duration, out bool sense, timeout); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SecureDigital Device", "SEND_OP_COND took {0} ms.", duration); - - return sense; - } - - /// Reads the extended CSD from a MultiMediaCard device - /// Data buffer - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadExtendedCsd(out byte[] buffer, out uint[] response, uint timeout, out double duration) - { - buffer = new byte[512]; - - LastError = SendMmcCommand(MmcCommands.SendExtCsd, false, false, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0, 512, 1, - ref buffer, out response, out duration, out bool sense, timeout); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("MMC Device", "SEND_EXT_CSD took {0} ms.", duration); - - return sense; - } - - /// Sets the block length for transfers from a SecureDigital or MultiMediaCard device - /// Block length in bytes - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool SetBlockLength(uint length, out uint[] response, uint timeout, out double duration) - { - byte[] buffer = Array.Empty(); - - LastError = SendMmcCommand(MmcCommands.SetBlocklen, false, false, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAc, length, 0, 0, - ref buffer, out response, out duration, out bool sense, timeout); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("MMC Device", "SET_BLOCKLEN took {0} ms.", duration); - - return sense; - } - - /// Reads blocks from a SecureDigital or MultiMediaCard device - /// Data buffer - /// Response - /// LBA to start reading from - /// Block size in bytes - /// Number of bytes to transfer - /// Card is byte-addressed - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool Read(out byte[] buffer, out uint[] response, uint lba, uint blockSize, ushort transferLength, - bool byteAddressed, uint timeout, out double duration) - { - bool sense = true; - buffer = null; - response = null; - duration = -1; - - if(transferLength <= 1) - return ReadSingleBlock(out buffer, out response, lba, blockSize, byteAddressed, timeout, out duration); - - if(!_readMultipleBlockCannotSetBlockCount) - sense = ReadMultipleBlock(out buffer, out response, lba, blockSize, transferLength, byteAddressed, - timeout, out duration); - - if(_readMultipleBlockCannotSetBlockCount) - return ReadMultipleUsingSingle(out buffer, out response, lba, blockSize, transferLength, byteAddressed, - timeout, out duration); - - return sense; - } - - /// Reads a single block from a SecureDigital or MultiMediaCard device - /// Data buffer - /// Response - /// LBA to start reading from - /// Block size in bytes - /// Card is byte-addressed - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadSingleBlock(out byte[] buffer, out uint[] response, uint lba, uint blockSize, - bool byteAddressed, uint timeout, out double duration) + for(uint i = 0; i < transferLength; i++) { uint address; - buffer = new byte[blockSize]; - response = null; if(byteAddressed) - address = lba * blockSize; + address = (lba + i) * blockSize; else - address = lba; + address = lba + i; LastError = SendMmcCommand(MmcCommands.ReadSingleBlock, false, false, MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, address, - blockSize, 1, ref buffer, out response, out duration, out bool sense, timeout); + blockSize, 1, ref blockBuffer, out response, out double blockDuration, + out sense, timeout); Error = LastError != 0; - AaruConsole.DebugWriteLine("MMC Device", "READ_SINGLE_BLOCK took {0} ms.", duration); + duration += blockDuration; - return sense; + if(Error || sense) + break; + + Array.Copy(blockBuffer, 0, buffer, i * blockSize, blockSize); } - static bool _readMultipleBlockCannotSetBlockCount; + AaruConsole.DebugWriteLine("MMC Device", "Multiple READ_SINGLE_BLOCKs took {0} ms.", duration); - /// Reads multiple blocks from a SecureDigital or MultiMediaCard device - /// Data buffer - /// Response - /// LBA to start reading from - /// Block size in bytes - /// Number of bytes to transfer - /// Card is byte-addressed - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadMultipleBlock(out byte[] buffer, out uint[] response, uint lba, uint blockSize, - ushort transferLength, bool byteAddressed, uint timeout, out double duration) + return sense; + } + + /// Reads status register from a MultiMediaCard device + /// Data buffer + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadStatus(out byte[] buffer, out uint[] response, uint timeout, out double duration) + { + buffer = new byte[4]; + + LastError = SendMmcCommand(MmcCommands.SendStatus, false, true, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAc, 0, 4, 1, + ref buffer, out response, out duration, out bool sense, timeout); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SecureDigital Device", "SEND_STATUS took {0} ms.", duration); + + return sense; + } + + /// Reads blocks with block count from a SecureDigital or MultiMediaCard device + /// Data buffer + /// Response + /// LBA to start reading from + /// Block size in bytes + /// Number of bytes to transfer + /// Card is byte-addressed + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadWithBlockCount(out byte[] buffer, out uint[] response, uint lba, uint blockSize, + ushort transferLength, bool byteAddressed, uint timeout, out double duration) + { + uint address = byteAddressed ? lba * blockSize : lba; + MmcSingleCommand[] commands = new MmcSingleCommand[3]; + + // SET_BLOCK_COUNT + commands[0] = new MmcSingleCommand { - buffer = new byte[transferLength * blockSize]; - double setDuration = 0; - uint address; - response = null; + command = MmcCommands.SetBlockCount, + write = false, + isApplication = false, + flags = MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAc, + argument = transferLength, + blockSize = 0, + blocks = 0, + buffer = Array.Empty() + }; - if(byteAddressed) - address = lba * blockSize; - else - address = lba; - - LastError = SendMmcCommand(MmcCommands.ReadMultipleBlock, false, false, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, address, - blockSize, transferLength, ref buffer, out response, out duration, - out bool sense, timeout); - - Error = LastError != 0; - - if(transferLength > 1) - { - duration += setDuration; - AaruConsole.DebugWriteLine("MMC Device", "READ_MULTIPLE_BLOCK took {0} ms.", duration); - } - else - AaruConsole.DebugWriteLine("MMC Device", "READ_SINGLE_BLOCK took {0} ms.", duration); - - return sense; - } - - /// Reads blocks using a single block read from a SecureDigital or MultiMediaCard device - /// Data buffer - /// Response - /// LBA to start reading from - /// Block size in bytes - /// Number of bytes to transfer - /// Card is byte-addressed - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadMultipleUsingSingle(out byte[] buffer, out uint[] response, uint lba, uint blockSize, - ushort transferLength, bool byteAddressed, uint timeout, - out double duration) + // READ_MULTIPLE_BLOCK + commands[1] = new MmcSingleCommand { - buffer = new byte[transferLength * blockSize]; - byte[] blockBuffer = new byte[blockSize]; - duration = 0; - bool sense = true; - response = null; + command = MmcCommands.ReadMultipleBlock, + write = false, + isApplication = false, + flags = MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, + argument = address, + blockSize = blockSize, + blocks = transferLength, + buffer = new byte[transferLength * blockSize] + }; - for(uint i = 0; i < transferLength; i++) - { - uint address; - - if(byteAddressed) - address = (lba + i) * blockSize; - else - address = lba + i; - - LastError = SendMmcCommand(MmcCommands.ReadSingleBlock, false, false, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, address, - blockSize, 1, ref blockBuffer, out response, out double blockDuration, - out sense, timeout); - - Error = LastError != 0; - - duration += blockDuration; - - if(Error || sense) - break; - - Array.Copy(blockBuffer, 0, buffer, i * blockSize, blockSize); - } - - AaruConsole.DebugWriteLine("MMC Device", "Multiple READ_SINGLE_BLOCKs took {0} ms.", duration); - - return sense; - } - - /// Reads status register from a MultiMediaCard device - /// Data buffer - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadStatus(out byte[] buffer, out uint[] response, uint timeout, out double duration) + // STOP_TRANSMISSION + // Needed if the previous command fails + commands[2] = new MmcSingleCommand { - buffer = new byte[4]; + command = MmcCommands.StopTransmission, + write = false, + isApplication = false, + flags = MmcFlags.ResponseSpiR1B | MmcFlags.ResponseR1B | MmcFlags.CommandAc, + argument = 0, + blockSize = 0, + blocks = 0, + buffer = Array.Empty() + }; - LastError = SendMmcCommand(MmcCommands.SendStatus, false, true, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAc, 0, 4, 1, - ref buffer, out response, out duration, out bool sense, timeout); + LastError = SendMultipleMmcCommands(commands, out duration, out bool sense, timeout); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SecureDigital Device", "SEND_STATUS took {0} ms.", duration); + AaruConsole.DebugWriteLine("SecureDigital Device", "READ_MULTIPLE_BLOCK took {0} ms.", duration); - return sense; - } + buffer = commands[1].buffer; + response = commands[1].response; - /// Reads blocks with block count from a SecureDigital or MultiMediaCard device - /// Data buffer - /// Response - /// LBA to start reading from - /// Block size in bytes - /// Number of bytes to transfer - /// Card is byte-addressed - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadWithBlockCount(out byte[] buffer, out uint[] response, uint lba, uint blockSize, - ushort transferLength, bool byteAddressed, uint timeout, out double duration) - { - uint address = byteAddressed ? lba * blockSize : lba; - MmcSingleCommand[] commands = new MmcSingleCommand[3]; - - // SET_BLOCK_COUNT - commands[0] = new MmcSingleCommand - { - command = MmcCommands.SetBlockCount, - write = false, - isApplication = false, - flags = MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAc, - argument = transferLength, - blockSize = 0, - blocks = 0, - buffer = Array.Empty() - }; - - // READ_MULTIPLE_BLOCK - commands[1] = new MmcSingleCommand - { - command = MmcCommands.ReadMultipleBlock, - write = false, - isApplication = false, - flags = MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, - argument = address, - blockSize = blockSize, - blocks = transferLength, - buffer = new byte[transferLength * blockSize] - }; - - // STOP_TRANSMISSION - // Needed if the previous command fails - commands[2] = new MmcSingleCommand - { - command = MmcCommands.StopTransmission, - write = false, - isApplication = false, - flags = MmcFlags.ResponseSpiR1B | MmcFlags.ResponseR1B | MmcFlags.CommandAc, - argument = 0, - blockSize = 0, - blocks = 0, - buffer = Array.Empty() - }; - - LastError = SendMultipleMmcCommands(commands, out duration, out bool sense, timeout); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SecureDigital Device", "READ_MULTIPLE_BLOCK took {0} ms.", duration); - - buffer = commands[1].buffer; - response = commands[1].response; - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/MmcCommands/SecureDigital.cs b/Aaru.Devices/Device/MmcCommands/SecureDigital.cs index 0df3e3bd6..ca0f50a44 100644 --- a/Aaru.Devices/Device/MmcCommands/SecureDigital.cs +++ b/Aaru.Devices/Device/MmcCommands/SecureDigital.cs @@ -32,71 +32,70 @@ using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Reads the status register from a SecureDigital device + /// Data buffer + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadSdStatus(out byte[] buffer, out uint[] response, uint timeout, out double duration) { - /// Reads the status register from a SecureDigital device - /// Data buffer - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadSdStatus(out byte[] buffer, out uint[] response, uint timeout, out double duration) - { - buffer = new byte[64]; + buffer = new byte[64]; - LastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendStatus, false, true, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0, 64, 1, - ref buffer, out response, out duration, out bool sense, timeout); + LastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendStatus, false, true, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0, 64, 1, + ref buffer, out response, out duration, out bool sense, timeout); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SecureDigital Device", "SD_STATUS took {0} ms.", duration); + AaruConsole.DebugWriteLine("SecureDigital Device", "SD_STATUS took {0} ms.", duration); - return sense; - } + return sense; + } - /// Reads the OCR register from a SecureDigital device - /// Data buffer - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadSdocr(out byte[] buffer, out uint[] response, uint timeout, out double duration) - { - buffer = new byte[4]; + /// Reads the OCR register from a SecureDigital device + /// Data buffer + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadSdocr(out byte[] buffer, out uint[] response, uint timeout, out double duration) + { + buffer = new byte[4]; - LastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendOperatingCondition, false, true, - MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0, 4, 1, - ref buffer, out response, out duration, out bool sense, timeout); + LastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendOperatingCondition, false, true, + MmcFlags.ResponseSpiR3 | MmcFlags.ResponseR3 | MmcFlags.CommandBcr, 0, 4, 1, + ref buffer, out response, out duration, out bool sense, timeout); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SecureDigital Device", "SD_SEND_OP_COND took {0} ms.", duration); + AaruConsole.DebugWriteLine("SecureDigital Device", "SD_SEND_OP_COND took {0} ms.", duration); - return sense; - } + return sense; + } - /// Reads the SCR register from a SecureDigital device - /// Data buffer - /// Response - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool ReadScr(out byte[] buffer, out uint[] response, uint timeout, out double duration) - { - buffer = new byte[8]; + /// Reads the SCR register from a SecureDigital device + /// Data buffer + /// Response + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool ReadScr(out byte[] buffer, out uint[] response, uint timeout, out double duration) + { + buffer = new byte[8]; - LastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendScr, false, true, - MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0, 8, 1, - ref buffer, out response, out duration, out bool sense, timeout); + LastError = SendMmcCommand((MmcCommands)SecureDigitalCommands.SendScr, false, true, + MmcFlags.ResponseSpiR1 | MmcFlags.ResponseR1 | MmcFlags.CommandAdtc, 0, 8, 1, + ref buffer, out response, out duration, out bool sense, timeout); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SecureDigital Device", "SEND_SCR took {0} ms.", duration); + AaruConsole.DebugWriteLine("SecureDigital Device", "SEND_SCR took {0} ms.", duration); - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/Adaptec.cs b/Aaru.Devices/Device/ScsiCommands/Adaptec.cs index 777f3874b..c8ed7221b 100644 --- a/Aaru.Devices/Device/ScsiCommands/Adaptec.cs +++ b/Aaru.Devices/Device/ScsiCommands/Adaptec.cs @@ -34,179 +34,178 @@ using System; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. + /// Buffer. + /// Sense buffer. + /// SCSI Logical Block Address. + /// Timeout. + /// Duration. + public bool AdaptecTranslate(out byte[] buffer, out byte[] senseBuffer, uint lba, uint timeout, + out double duration) => + AdaptecTranslate(out buffer, out senseBuffer, false, lba, timeout, out duration); + + /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. + /// Buffer. + /// Sense buffer. + /// If set to true request the data from drive 1. + /// SCSI Logical Block Address. + /// Timeout. + /// Duration. + public bool AdaptecTranslate(out byte[] buffer, out byte[] senseBuffer, bool drive1, uint lba, uint timeout, + out double duration) { - /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. - /// Buffer. - /// Sense buffer. - /// SCSI Logical Block Address. - /// Timeout. - /// Duration. - public bool AdaptecTranslate(out byte[] buffer, out byte[] senseBuffer, uint lba, uint timeout, - out double duration) => - AdaptecTranslate(out buffer, out senseBuffer, false, lba, timeout, out duration); + buffer = new byte[8]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. - /// Buffer. - /// Sense buffer. - /// If set to true request the data from drive 1. - /// SCSI Logical Block Address. - /// Timeout. - /// Duration. - public bool AdaptecTranslate(out byte[] buffer, out byte[] senseBuffer, bool drive1, uint lba, uint timeout, - out double duration) - { - buffer = new byte[8]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; + cdb[0] = (byte)ScsiCommands.AdaptecTranslate; + cdb[1] = (byte)((lba & 0x1F0000) >> 16); + cdb[2] = (byte)((lba & 0xFF00) >> 8); + cdb[3] = (byte)(lba & 0xFF); - cdb[0] = (byte)ScsiCommands.AdaptecTranslate; - cdb[1] = (byte)((lba & 0x1F0000) >> 16); - cdb[2] = (byte)((lba & 0xFF00) >> 8); - cdb[3] = (byte)(lba & 0xFF); + if(drive1) + cdb[1] += 0x20; - if(drive1) - cdb[1] += 0x20; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC TRANSLATE took {0} ms.", duration); - AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC TRANSLATE took {0} ms.", duration); + return sense; + } - return sense; - } + /// Sets the error threshold + /// true, if set error threshold was adapteced, false otherwise. + /// Threshold. 0 to disable error reporting. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool + AdaptecSetErrorThreshold(byte threshold, out byte[] senseBuffer, uint timeout, out double duration) => + AdaptecSetErrorThreshold(threshold, out senseBuffer, false, timeout, out duration); - /// Sets the error threshold - /// true, if set error threshold was adapteced, false otherwise. - /// Threshold. 0 to disable error reporting. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool - AdaptecSetErrorThreshold(byte threshold, out byte[] senseBuffer, uint timeout, out double duration) => - AdaptecSetErrorThreshold(threshold, out senseBuffer, false, timeout, out duration); + /// Sets the error threshold + /// true, if set error threshold was adapteced, false otherwise. + /// Threshold. 0 to disable error reporting. + /// Sense buffer. + /// If set to true set the threshold from drive 1. + /// Timeout. + /// Duration. + public bool AdaptecSetErrorThreshold(byte threshold, out byte[] senseBuffer, bool drive1, uint timeout, + out double duration) + { + byte[] buffer = new byte[1]; + buffer[0] = threshold; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - /// Sets the error threshold - /// true, if set error threshold was adapteced, false otherwise. - /// Threshold. 0 to disable error reporting. - /// Sense buffer. - /// If set to true set the threshold from drive 1. - /// Timeout. - /// Duration. - public bool AdaptecSetErrorThreshold(byte threshold, out byte[] senseBuffer, bool drive1, uint timeout, - out double duration) - { - byte[] buffer = new byte[1]; - buffer[0] = threshold; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; + cdb[0] = (byte)ScsiCommands.AdaptecSetErrorThreshold; - cdb[0] = (byte)ScsiCommands.AdaptecSetErrorThreshold; + if(drive1) + cdb[1] += 0x20; - if(drive1) - cdb[1] += 0x20; + cdb[4] = 1; - cdb[4] = 1; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC SET ERROR THRESHOLD took {0} ms.", duration); - AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC SET ERROR THRESHOLD took {0} ms.", duration); + return sense; + } - return sense; - } + /// Requests the usage, seek and error counters, and resets them + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool AdaptecReadUsageCounter(out byte[] buffer, out byte[] senseBuffer, uint timeout, + out double duration) => + AdaptecReadUsageCounter(out buffer, out senseBuffer, false, timeout, out duration); - /// Requests the usage, seek and error counters, and resets them - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool AdaptecReadUsageCounter(out byte[] buffer, out byte[] senseBuffer, uint timeout, - out double duration) => - AdaptecReadUsageCounter(out buffer, out senseBuffer, false, timeout, out duration); + /// Requests the usage, seek and error counters, and resets them + /// Buffer. + /// Sense buffer. + /// If set to true get the counters from drive 1. + /// Timeout. + /// Duration. + public bool AdaptecReadUsageCounter(out byte[] buffer, out byte[] senseBuffer, bool drive1, uint timeout, + out double duration) + { + buffer = new byte[9]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - /// Requests the usage, seek and error counters, and resets them - /// Buffer. - /// Sense buffer. - /// If set to true get the counters from drive 1. - /// Timeout. - /// Duration. - public bool AdaptecReadUsageCounter(out byte[] buffer, out byte[] senseBuffer, bool drive1, uint timeout, - out double duration) - { - buffer = new byte[9]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; + cdb[0] = (byte)ScsiCommands.AdaptecTranslate; - cdb[0] = (byte)ScsiCommands.AdaptecTranslate; + if(drive1) + cdb[1] += 0x20; - if(drive1) - cdb[1] += 0x20; + cdb[4] = (byte)buffer.Length; - cdb[4] = (byte)buffer.Length; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC READ/RESET USAGE COUNTER took {0} ms.", duration); - AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC READ/RESET USAGE COUNTER took {0} ms.", duration); + return sense; + } - return sense; - } + /// Fills the Adaptec controller RAM with 1K bytes of data + /// Data to fill the buffer with. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool AdaptecWriteBuffer(byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + byte[] oneKBuffer = new byte[1024]; + Array.Copy(buffer, 0, oneKBuffer, 0, buffer.Length < 1024 ? buffer.Length : 1024); - /// Fills the Adaptec controller RAM with 1K bytes of data - /// Data to fill the buffer with. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool AdaptecWriteBuffer(byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - byte[] oneKBuffer = new byte[1024]; - Array.Copy(buffer, 0, oneKBuffer, 0, buffer.Length < 1024 ? buffer.Length : 1024); + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; + cdb[0] = (byte)ScsiCommands.AdaptecWriteBuffer; - cdb[0] = (byte)ScsiCommands.AdaptecWriteBuffer; + LastError = SendScsiCommand(cdb, ref oneKBuffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref oneKBuffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC WRITE DATA BUFFER took {0} ms.", duration); - AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC WRITE DATA BUFFER took {0} ms.", duration); + return sense; + } - return sense; - } + /// Reads 1K bytes of data from the Adaptec controller RAM + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool AdaptecReadBuffer(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[1024]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - /// Reads 1K bytes of data from the Adaptec controller RAM - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool AdaptecReadBuffer(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[1024]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; + cdb[0] = (byte)ScsiCommands.AdaptecReadBuffer; - cdb[0] = (byte)ScsiCommands.AdaptecReadBuffer; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC READ DATA BUFFER took {0} ms.", duration); - AaruConsole.DebugWriteLine("SCSI Device", "ADAPTEC READ DATA BUFFER took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/ArchiveCorp.cs b/Aaru.Devices/Device/ScsiCommands/ArchiveCorp.cs index 6b99b17cd..5071ccd05 100644 --- a/Aaru.Devices/Device/ScsiCommands/ArchiveCorp.cs +++ b/Aaru.Devices/Device/ScsiCommands/ArchiveCorp.cs @@ -33,76 +33,75 @@ using System; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. + /// Buffer. + /// Sense buffer. + /// Address. + /// Timeout. + /// Duration. + public bool ArchiveCorpRequestBlockAddress(out byte[] buffer, out byte[] senseBuffer, uint lba, uint timeout, + out double duration) { - /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. - /// Buffer. - /// Sense buffer. - /// Address. - /// Timeout. - /// Duration. - public bool ArchiveCorpRequestBlockAddress(out byte[] buffer, out byte[] senseBuffer, uint lba, uint timeout, - out double duration) - { - buffer = new byte[3]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; + buffer = new byte[3]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - cdb[0] = (byte)ScsiCommands.ArchiveRequestBlockAddress; - cdb[1] = (byte)((lba & 0x1F0000) >> 16); - cdb[2] = (byte)((lba & 0xFF00) >> 8); - cdb[3] = (byte)(lba & 0xFF); - cdb[4] = 3; + cdb[0] = (byte)ScsiCommands.ArchiveRequestBlockAddress; + cdb[1] = (byte)((lba & 0x1F0000) >> 16); + cdb[2] = (byte)((lba & 0xFF00) >> 8); + cdb[3] = (byte)(lba & 0xFF); + cdb[4] = 3; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "ARCHIVE CORP. REQUEST BLOCK ADDRESS took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "ARCHIVE CORP. REQUEST BLOCK ADDRESS took {0} ms.", duration); - return sense; - } + return sense; + } - /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. - /// Sense buffer. - /// Logical Block Address, starting from 1. - /// Timeout. - /// Duration. - public bool ArchiveCorpSeekBlock(out byte[] senseBuffer, uint lba, uint timeout, out double duration) => - ArchiveCorpSeekBlock(out senseBuffer, false, lba, timeout, out duration); + /// Gets the underlying drive cylinder, head and index bytes for the specified SCSI LBA. + /// Sense buffer. + /// Logical Block Address, starting from 1. + /// Timeout. + /// Duration. + public bool ArchiveCorpSeekBlock(out byte[] senseBuffer, uint lba, uint timeout, out double duration) => + ArchiveCorpSeekBlock(out senseBuffer, false, lba, timeout, out duration); - /// Positions the tape at the specified block address - /// Sense buffer. - /// If set to true, return from the command immediately. - /// Logical Block Address, starting from 1. - /// Timeout. - /// Duration. - public bool ArchiveCorpSeekBlock(out byte[] senseBuffer, bool immediate, uint lba, uint timeout, - out double duration) - { - byte[] buffer = Array.Empty(); - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; + /// Positions the tape at the specified block address + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Logical Block Address, starting from 1. + /// Timeout. + /// Duration. + public bool ArchiveCorpSeekBlock(out byte[] senseBuffer, bool immediate, uint lba, uint timeout, + out double duration) + { + byte[] buffer = Array.Empty(); + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - cdb[0] = (byte)ScsiCommands.ArchiveSeekBlock; - cdb[1] = (byte)((lba & 0x1F0000) >> 16); - cdb[2] = (byte)((lba & 0xFF00) >> 8); - cdb[3] = (byte)(lba & 0xFF); + cdb[0] = (byte)ScsiCommands.ArchiveSeekBlock; + cdb[1] = (byte)((lba & 0x1F0000) >> 16); + cdb[2] = (byte)((lba & 0xFF00) >> 8); + cdb[3] = (byte)(lba & 0xFF); - if(immediate) - cdb[1] += 0x01; + if(immediate) + cdb[1] += 0x01; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "ARCHIVE CORP. SEEK BLOCK took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "ARCHIVE CORP. SEEK BLOCK took {0} ms.", duration); - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/Certance.cs b/Aaru.Devices/Device/ScsiCommands/Certance.cs index d52a3945a..1a6953d44 100644 --- a/Aaru.Devices/Device/ScsiCommands/Certance.cs +++ b/Aaru.Devices/Device/ScsiCommands/Certance.cs @@ -34,49 +34,48 @@ using System; using System.Diagnostics.CodeAnalysis; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +public sealed partial class Device { - [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - public sealed partial class Device + /// Parks the load arm in preparation for transport + /// Sense buffer. + /// Timeout. + /// Duration. + public bool CertancePark(out byte[] senseBuffer, uint timeout, out double duration) => + CertanceParkUnpark(out senseBuffer, true, timeout, out duration); + + /// Unparks the load arm prior to operation + /// Sense buffer. + /// Timeout. + /// Duration. + public bool CertanceUnpark(out byte[] senseBuffer, uint timeout, out double duration) => + CertanceParkUnpark(out senseBuffer, false, timeout, out duration); + + /// Parks the load arm in preparation for transport or unparks it prior to operation + /// Sense buffer. + /// If set to true, parks the load arm + /// Timeout. + /// Duration. + public bool CertanceParkUnpark(out byte[] senseBuffer, bool park, uint timeout, out double duration) { - /// Parks the load arm in preparation for transport - /// Sense buffer. - /// Timeout. - /// Duration. - public bool CertancePark(out byte[] senseBuffer, uint timeout, out double duration) => - CertanceParkUnpark(out senseBuffer, true, timeout, out duration); + byte[] buffer = Array.Empty(); + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - /// Unparks the load arm prior to operation - /// Sense buffer. - /// Timeout. - /// Duration. - public bool CertanceUnpark(out byte[] senseBuffer, uint timeout, out double duration) => - CertanceParkUnpark(out senseBuffer, false, timeout, out duration); + cdb[0] = (byte)ScsiCommands.CertanceParkUnpark; - /// Parks the load arm in preparation for transport or unparks it prior to operation - /// Sense buffer. - /// If set to true, parks the load arm - /// Timeout. - /// Duration. - public bool CertanceParkUnpark(out byte[] senseBuffer, bool park, uint timeout, out double duration) - { - byte[] buffer = Array.Empty(); - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; + if(park) + cdb[4] = 1; - cdb[0] = (byte)ScsiCommands.CertanceParkUnpark; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); - if(park) - cdb[4] = 1; + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + AaruConsole.DebugWriteLine("SCSI Device", "CERTANCE PARK UNPARK took {0} ms.", duration); - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "CERTANCE PARK UNPARK took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/Fujitsu.cs b/Aaru.Devices/Device/ScsiCommands/Fujitsu.cs index 29cfe1e70..1d748cd23 100644 --- a/Aaru.Devices/Device/ScsiCommands/Fujitsu.cs +++ b/Aaru.Devices/Device/ScsiCommands/Fujitsu.cs @@ -35,77 +35,76 @@ using System.Text; using Aaru.Console; using Aaru.Helpers; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sets the data for the integrated display + /// Returned SENSE buffer + /// If the display should start flashing + /// Display mode + /// String to be shown in the first line of the display + /// String to be shown in the second line of the display + /// Timeout to wait for command execution + /// Time the device took to execute the command in milliseconds + /// true if the device set an error condition, false otherwise + public bool FujitsuDisplay(out byte[] senseBuffer, bool flash, FujitsuDisplayModes mode, string firstHalf, + string secondHalf, uint timeout, out double duration) { - /// Sets the data for the integrated display - /// Returned SENSE buffer - /// If the display should start flashing - /// Display mode - /// String to be shown in the first line of the display - /// String to be shown in the second line of the display - /// Timeout to wait for command execution - /// Time the device took to execute the command in milliseconds - /// true if the device set an error condition, false otherwise - public bool FujitsuDisplay(out byte[] senseBuffer, bool flash, FujitsuDisplayModes mode, string firstHalf, - string secondHalf, uint timeout, out double duration) + byte[] tmp; + byte[] firstHalfBytes = new byte[8]; + byte[] secondHalfBytes = new byte[8]; + byte[] buffer = new byte[17]; + bool displayLen = false; + bool halfMsg = false; + byte[] cdb = new byte[10]; + + if(!string.IsNullOrWhiteSpace(firstHalf)) { - byte[] tmp; - byte[] firstHalfBytes = new byte[8]; - byte[] secondHalfBytes = new byte[8]; - byte[] buffer = new byte[17]; - bool displayLen = false; - bool halfMsg = false; - byte[] cdb = new byte[10]; - - if(!string.IsNullOrWhiteSpace(firstHalf)) - { - tmp = Encoding.ASCII.GetBytes(firstHalf); - Array.Copy(tmp, 0, firstHalfBytes, 0, 8); - } - - if(!string.IsNullOrWhiteSpace(secondHalf)) - { - tmp = Encoding.ASCII.GetBytes(secondHalf); - Array.Copy(tmp, 0, secondHalfBytes, 0, 8); - } - - if(mode != FujitsuDisplayModes.Half) - if(!ArrayHelpers.ArrayIsNullOrWhiteSpace(firstHalfBytes) && - !ArrayHelpers.ArrayIsNullOrWhiteSpace(secondHalfBytes)) - displayLen = true; - else if(!ArrayHelpers.ArrayIsNullOrWhiteSpace(firstHalfBytes) && - ArrayHelpers.ArrayIsNullOrWhiteSpace(secondHalfBytes)) - halfMsg = true; - - buffer[0] = (byte)((byte)mode << 5); - - if(displayLen) - buffer[0] += 0x10; - - if(flash) - buffer[0] += 0x08; - - if(halfMsg) - buffer[0] += 0x04; - - buffer[0] += 0x01; // Always ASCII - - Array.Copy(firstHalfBytes, 0, buffer, 1, 8); - Array.Copy(secondHalfBytes, 0, buffer, 9, 8); - - cdb[0] = (byte)ScsiCommands.FujitsuDisplay; - cdb[6] = (byte)buffer.Length; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "FUJITSU DISPLAY took {0} ms.", duration); - - return sense; + tmp = Encoding.ASCII.GetBytes(firstHalf); + Array.Copy(tmp, 0, firstHalfBytes, 0, 8); } + + if(!string.IsNullOrWhiteSpace(secondHalf)) + { + tmp = Encoding.ASCII.GetBytes(secondHalf); + Array.Copy(tmp, 0, secondHalfBytes, 0, 8); + } + + if(mode != FujitsuDisplayModes.Half) + if(!ArrayHelpers.ArrayIsNullOrWhiteSpace(firstHalfBytes) && + !ArrayHelpers.ArrayIsNullOrWhiteSpace(secondHalfBytes)) + displayLen = true; + else if(!ArrayHelpers.ArrayIsNullOrWhiteSpace(firstHalfBytes) && + ArrayHelpers.ArrayIsNullOrWhiteSpace(secondHalfBytes)) + halfMsg = true; + + buffer[0] = (byte)((byte)mode << 5); + + if(displayLen) + buffer[0] += 0x10; + + if(flash) + buffer[0] += 0x08; + + if(halfMsg) + buffer[0] += 0x04; + + buffer[0] += 0x01; // Always ASCII + + Array.Copy(firstHalfBytes, 0, buffer, 1, 8); + Array.Copy(secondHalfBytes, 0, buffer, 9, 8); + + cdb[0] = (byte)ScsiCommands.FujitsuDisplay; + cdb[6] = (byte)buffer.Length; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "FUJITSU DISPLAY took {0} ms.", duration); + + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/HL-DT-ST.cs b/Aaru.Devices/Device/ScsiCommands/HL-DT-ST.cs index c2a54fcbf..e7f67c7dd 100644 --- a/Aaru.Devices/Device/ScsiCommands/HL-DT-ST.cs +++ b/Aaru.Devices/Device/ScsiCommands/HL-DT-ST.cs @@ -32,45 +32,44 @@ using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Reads a "raw" sector from DVD on HL-DT-ST drives. + /// true if the command failed and contains the sense buffer. + /// Buffer where the HL-DT-ST READ DVD (RAW) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Start block address. + /// How many blocks to read. + public bool HlDtStReadRawDvd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, + uint timeout, out double duration) { - /// Reads a "raw" sector from DVD on HL-DT-ST drives. - /// true if the command failed and contains the sense buffer. - /// Buffer where the HL-DT-ST READ DVD (RAW) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Start block address. - /// How many blocks to read. - public bool HlDtStReadRawDvd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, - uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - buffer = new byte[2064 * transferLength]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + buffer = new byte[2064 * transferLength]; - cdb[0] = (byte)ScsiCommands.HlDtStVendor; - cdb[1] = 0x48; - cdb[2] = 0x49; - cdb[3] = 0x54; - cdb[4] = 0x01; - cdb[6] = (byte)((lba & 0xFF000000) >> 24); - cdb[7] = (byte)((lba & 0xFF0000) >> 16); - cdb[8] = (byte)((lba & 0xFF00) >> 8); - cdb[9] = (byte)(lba & 0xFF); - cdb[10] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[11] = (byte)(buffer.Length & 0xFF); + cdb[0] = (byte)ScsiCommands.HlDtStVendor; + cdb[1] = 0x48; + cdb[2] = 0x49; + cdb[3] = 0x54; + cdb[4] = 0x01; + cdb[6] = (byte)((lba & 0xFF000000) >> 24); + cdb[7] = (byte)((lba & 0xFF0000) >> 16); + cdb[8] = (byte)((lba & 0xFF00) >> 8); + cdb[9] = (byte)(lba & 0xFF); + cdb[10] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[11] = (byte)(buffer.Length & 0xFF); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "HL-DT-ST READ DVD (RAW) took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "HL-DT-ST READ DVD (RAW) took {0} ms.", duration); - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/HP.cs b/Aaru.Devices/Device/ScsiCommands/HP.cs index 73630b513..e32e09f73 100644 --- a/Aaru.Devices/Device/ScsiCommands/HP.cs +++ b/Aaru.Devices/Device/ScsiCommands/HP.cs @@ -32,74 +32,73 @@ using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the HP READ LONG vendor command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ LONG response will be stored + /// Sense buffer. + /// If set to true address contain two's complement offset from last read address. + /// PBA/LBA to read. + /// How many bytes per block. + /// If set to true address contain physical block address. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool HpReadLong(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, ushort blockBytes, + bool pba, uint timeout, out double duration) => + HpReadLong(out buffer, out senseBuffer, relAddr, address, 0, blockBytes, pba, false, timeout, out duration); + + /// Sends the HP READ LONG vendor command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ LONG response will be stored + /// Sense buffer. + /// If set to true address contain two's complement offset from last read address. + /// PBA/LBA to read. + /// How many blocks/bytes to read. + /// How many bytes per block. + /// If set to true address contain physical block address. + /// + /// If set to true is a count of secors to read. Otherwise + /// it will be ignored + /// + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool HpReadLong(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, + ushort transferLen, ushort blockBytes, bool pba, bool sectorCount, uint timeout, + out double duration) { - /// Sends the HP READ LONG vendor command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ LONG response will be stored - /// Sense buffer. - /// If set to true address contain two's complement offset from last read address. - /// PBA/LBA to read. - /// How many bytes per block. - /// If set to true address contain physical block address. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool HpReadLong(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, ushort blockBytes, - bool pba, uint timeout, out double duration) => - HpReadLong(out buffer, out senseBuffer, relAddr, address, 0, blockBytes, pba, false, timeout, out duration); + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - /// Sends the HP READ LONG vendor command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ LONG response will be stored - /// Sense buffer. - /// If set to true address contain two's complement offset from last read address. - /// PBA/LBA to read. - /// How many blocks/bytes to read. - /// How many bytes per block. - /// If set to true address contain physical block address. - /// - /// If set to true is a count of secors to read. Otherwise - /// it will be ignored - /// - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool HpReadLong(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, - ushort transferLen, ushort blockBytes, bool pba, bool sectorCount, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + cdb[0] = (byte)ScsiCommands.ReadLong; - cdb[0] = (byte)ScsiCommands.ReadLong; + if(relAddr) + cdb[1] += 0x01; - if(relAddr) - cdb[1] += 0x01; + cdb[2] = (byte)((address & 0xFF000000) >> 24); + cdb[3] = (byte)((address & 0xFF0000) >> 16); + cdb[4] = (byte)((address & 0xFF00) >> 8); + cdb[5] = (byte)(address & 0xFF); + cdb[7] = (byte)((transferLen & 0xFF00) >> 8); + cdb[8] = (byte)(transferLen & 0xFF); - cdb[2] = (byte)((address & 0xFF000000) >> 24); - cdb[3] = (byte)((address & 0xFF0000) >> 16); - cdb[4] = (byte)((address & 0xFF00) >> 8); - cdb[5] = (byte)(address & 0xFF); - cdb[7] = (byte)((transferLen & 0xFF00) >> 8); - cdb[8] = (byte)(transferLen & 0xFF); + if(pba) + cdb[9] += 0x80; - if(pba) - cdb[9] += 0x80; + if(sectorCount) + cdb[9] += 0x40; - if(sectorCount) - cdb[9] += 0x40; + buffer = sectorCount ? new byte[blockBytes * transferLen] : new byte[transferLen]; - buffer = sectorCount ? new byte[blockBytes * transferLen] : new byte[transferLen]; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("SCSI Device", "HP READ LONG took {0} ms.", duration); - AaruConsole.DebugWriteLine("SCSI Device", "HP READ LONG took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/Kreon.cs b/Aaru.Devices/Device/ScsiCommands/Kreon.cs index 61bd3b426..43c4d7d4a 100644 --- a/Aaru.Devices/Device/ScsiCommands/Kreon.cs +++ b/Aaru.Devices/Device/ScsiCommands/Kreon.cs @@ -33,213 +33,212 @@ using System; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sets the drive to the xtreme unlocked state + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool KreonDeprecatedUnlock(out byte[] senseBuffer, uint timeout, out double duration) { - /// Sets the drive to the xtreme unlocked state - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool KreonDeprecatedUnlock(out byte[] senseBuffer, uint timeout, out double duration) + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.KreonCommand; + cdb[1] = 0x08; + cdb[2] = 0x01; + cdb[3] = 0x01; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "KREON DEPRECATED UNLOCK took {0} ms.", duration); + + return sense; + } + + /// Sets the drive to the locked state. + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool KreonLock(out byte[] senseBuffer, uint timeout, out double duration) => + KreonSetLockState(out senseBuffer, KreonLockStates.Locked, timeout, out duration); + + /// Sets the drive to the xtreme unlocked state + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool KreonUnlockXtreme(out byte[] senseBuffer, uint timeout, out double duration) => + KreonSetLockState(out senseBuffer, KreonLockStates.Xtreme, timeout, out duration); + + /// Sets the drive to the wxripper unlocked state + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool KreonUnlockWxripper(out byte[] senseBuffer, uint timeout, out double duration) => + KreonSetLockState(out senseBuffer, KreonLockStates.Wxripper, timeout, out duration); + + /// Sets the drive to the specified lock state + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + /// Lock state + public bool KreonSetLockState(out byte[] senseBuffer, KreonLockStates state, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.KreonCommand; + cdb[1] = 0x08; + cdb[2] = 0x01; + cdb[3] = 0x11; + cdb[4] = (byte)state; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "KREON SET LOCK STATE took {0} ms.", duration); + + return sense || Error; + } + + /// Gets a list of supported features + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + /// Features supported by drive. + public bool KreonGetFeatureList(out byte[] senseBuffer, out KreonFeatures features, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = new byte[26]; + features = 0; + + cdb[0] = (byte)ScsiCommands.KreonCommand; + cdb[1] = 0x08; + cdb[2] = 0x01; + cdb[3] = 0x10; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "KREON GET FEATURE LIST took {0} ms.", duration); + + if(sense) + return true; + + if(buffer[0] != 0xA5 || + buffer[1] != 0x5A || + buffer[2] != 0x5A || + buffer[3] != 0xA5) + return true; + + for(int i = 4; i < 26; i += 2) { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); + ushort feature = BitConverter.ToUInt16(buffer, i); - cdb[0] = (byte)ScsiCommands.KreonCommand; - cdb[1] = 0x08; - cdb[2] = 0x01; - cdb[3] = 0x01; + if(feature == 0x0000) + break; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "KREON DEPRECATED UNLOCK took {0} ms.", duration); - - return sense; - } - - /// Sets the drive to the locked state. - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool KreonLock(out byte[] senseBuffer, uint timeout, out double duration) => - KreonSetLockState(out senseBuffer, KreonLockStates.Locked, timeout, out duration); - - /// Sets the drive to the xtreme unlocked state - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool KreonUnlockXtreme(out byte[] senseBuffer, uint timeout, out double duration) => - KreonSetLockState(out senseBuffer, KreonLockStates.Xtreme, timeout, out duration); - - /// Sets the drive to the wxripper unlocked state - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool KreonUnlockWxripper(out byte[] senseBuffer, uint timeout, out double duration) => - KreonSetLockState(out senseBuffer, KreonLockStates.Wxripper, timeout, out duration); - - /// Sets the drive to the specified lock state - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - /// Lock state - public bool KreonSetLockState(out byte[] senseBuffer, KreonLockStates state, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); - - cdb[0] = (byte)ScsiCommands.KreonCommand; - cdb[1] = 0x08; - cdb[2] = 0x01; - cdb[3] = 0x11; - cdb[4] = (byte)state; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "KREON SET LOCK STATE took {0} ms.", duration); - - return sense || Error; - } - - /// Gets a list of supported features - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - /// Features supported by drive. - public bool KreonGetFeatureList(out byte[] senseBuffer, out KreonFeatures features, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = new byte[26]; - features = 0; - - cdb[0] = (byte)ScsiCommands.KreonCommand; - cdb[1] = 0x08; - cdb[2] = 0x01; - cdb[3] = 0x10; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "KREON GET FEATURE LIST took {0} ms.", duration); - - if(sense) - return true; - - if(buffer[0] != 0xA5 || - buffer[1] != 0x5A || - buffer[2] != 0x5A || - buffer[3] != 0xA5) - return true; - - for(int i = 4; i < 26; i += 2) + switch(feature) { - ushort feature = BitConverter.ToUInt16(buffer, i); + case 0x0001: + features |= KreonFeatures.XtremeUnlock360; - if(feature == 0x0000) break; + case 0x0101: + features |= KreonFeatures.WxripperUnlock360; - switch(feature) - { - case 0x0001: - features |= KreonFeatures.XtremeUnlock360; + break; + case 0x2001: + features |= KreonFeatures.DecryptSs360; - break; - case 0x0101: - features |= KreonFeatures.WxripperUnlock360; + break; + case 0x2101: + features |= KreonFeatures.ChallengeResponse360; - break; - case 0x2001: - features |= KreonFeatures.DecryptSs360; + break; + case 0x0002: + features |= KreonFeatures.XtremeUnlock; - break; - case 0x2101: - features |= KreonFeatures.ChallengeResponse360; + break; + case 0x0102: + features |= KreonFeatures.WxripperUnlock; - break; - case 0x0002: - features |= KreonFeatures.XtremeUnlock; + break; + case 0x2002: + features |= KreonFeatures.DecryptSs; - break; - case 0x0102: - features |= KreonFeatures.WxripperUnlock; + break; + case 0x2102: + features |= KreonFeatures.ChallengeResponse; - break; - case 0x2002: - features |= KreonFeatures.DecryptSs; + break; + case 0x00F0: + features |= KreonFeatures.Lock; - break; - case 0x2102: - features |= KreonFeatures.ChallengeResponse; + break; + case 0x01F0: + features |= KreonFeatures.ErrorSkipping; - break; - case 0x00F0: - features |= KreonFeatures.Lock; - - break; - case 0x01F0: - features |= KreonFeatures.ErrorSkipping; - - break; - } + break; } - - return false; } - /// Gets the SS sector. - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - /// The SS sector. - /// Request number. - public bool KreonExtractSs(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration, - byte requestNumber = 0x00) - { - buffer = new byte[2048]; - byte[] cdb = new byte[12]; - senseBuffer = new byte[64]; + return false; + } - cdb[0] = (byte)ScsiCommands.KreonSsCommand; - cdb[1] = 0x00; - cdb[2] = 0xFF; - cdb[3] = 0x02; - cdb[4] = 0xFD; - cdb[5] = 0xFF; - cdb[6] = 0xFE; - cdb[7] = 0x00; - cdb[8] = 0x08; - cdb[9] = 0x00; - cdb[10] = requestNumber; - cdb[11] = 0xC0; + /// Gets the SS sector. + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + /// The SS sector. + /// Request number. + public bool KreonExtractSs(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration, + byte requestNumber = 0x00) + { + buffer = new byte[2048]; + byte[] cdb = new byte[12]; + senseBuffer = new byte[64]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + cdb[0] = (byte)ScsiCommands.KreonSsCommand; + cdb[1] = 0x00; + cdb[2] = 0xFF; + cdb[3] = 0x02; + cdb[4] = 0xFD; + cdb[5] = 0xFF; + cdb[6] = 0xFE; + cdb[7] = 0x00; + cdb[8] = 0x08; + cdb[9] = 0x00; + cdb[10] = requestNumber; + cdb[11] = 0xC0; - Error = LastError != 0; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - AaruConsole.DebugWriteLine("SCSI Device", "KREON EXTRACT SS took {0} ms.", duration); + Error = LastError != 0; - return sense || Error; - } + AaruConsole.DebugWriteLine("SCSI Device", "KREON EXTRACT SS took {0} ms.", duration); + + return sense || Error; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/MMC.cs b/Aaru.Devices/Device/ScsiCommands/MMC.cs index 59cd7b08d..96bf0972c 100644 --- a/Aaru.Devices/Device/ScsiCommands/MMC.cs +++ b/Aaru.Devices/Device/ScsiCommands/MMC.cs @@ -35,303 +35,287 @@ using System.Diagnostics.CodeAnalysis; using System.Text; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the MMC GET CONFIGURATION command for all Features + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI GET CONFIGURATION response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + GetConfiguration(out buffer, out senseBuffer, 0x0000, MmcGetConfigurationRt.All, timeout, out duration); + + /// Sends the MMC GET CONFIGURATION command for all Features starting with specified one + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI GET CONFIGURATION response will be stored + /// Sense buffer. + /// Feature number where the feature list should start from + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, ushort startingFeatureNumber, + uint timeout, out double duration) => + GetConfiguration(out buffer, out senseBuffer, startingFeatureNumber, MmcGetConfigurationRt.All, timeout, + out duration); + + /// Sends the MMC GET CONFIGURATION command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI GET CONFIGURATION response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting Feature number. + /// Return type, . + public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, ushort startingFeatureNumber, + MmcGetConfigurationRt rt, uint timeout, out double duration) { - /// Sends the MMC GET CONFIGURATION command for all Features - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI GET CONFIGURATION response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - GetConfiguration(out buffer, out senseBuffer, 0x0000, MmcGetConfigurationRt.All, timeout, out duration); + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + buffer = new byte[8]; - /// Sends the MMC GET CONFIGURATION command for all Features starting with specified one - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI GET CONFIGURATION response will be stored - /// Sense buffer. - /// Feature number where the feature list should start from - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, ushort startingFeatureNumber, - uint timeout, out double duration) => - GetConfiguration(out buffer, out senseBuffer, startingFeatureNumber, MmcGetConfigurationRt.All, timeout, - out duration); + cdb[0] = (byte)ScsiCommands.GetConfiguration; + cdb[1] = (byte)((byte)rt & 0x03); + cdb[2] = (byte)((startingFeatureNumber & 0xFF00) >> 8); + cdb[3] = (byte)(startingFeatureNumber & 0xFF); + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + cdb[9] = 0; - /// Sends the MMC GET CONFIGURATION command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI GET CONFIGURATION response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting Feature number. - /// Return type, . - public bool GetConfiguration(out byte[] buffer, out byte[] senseBuffer, ushort startingFeatureNumber, - MmcGetConfigurationRt rt, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - buffer = new byte[8]; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - cdb[0] = (byte)ScsiCommands.GetConfiguration; - cdb[1] = (byte)((byte)rt & 0x03); - cdb[2] = (byte)((startingFeatureNumber & 0xFF00) >> 8); - cdb[3] = (byte)(startingFeatureNumber & 0xFF); - cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(buffer.Length & 0xFF); - cdb[9] = 0; + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + if(sense) + return true; - Error = LastError != 0; + ushort confLength = (ushort)((buffer[2] << 8) + buffer[3] + 4); + buffer = new byte[confLength]; + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + senseBuffer = new byte[64]; - if(sense) - return true; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); - ushort confLength = (ushort)((buffer[2] << 8) + buffer[3] + 4); - buffer = new byte[confLength]; - cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(buffer.Length & 0xFF); - senseBuffer = new byte[64]; + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); + AaruConsole.DebugWriteLine("SCSI Device", + "GET CONFIGURATION (Starting Feature Number: {1}, Return Type: {2}, Sense: {3}, Last Error: {4}) took {0} ms.", + duration, startingFeatureNumber, rt, sense, LastError); - Error = LastError != 0; + return sense; + } - AaruConsole.DebugWriteLine("SCSI Device", - "GET CONFIGURATION (Starting Feature Number: {1}, Return Type: {2}, Sense: {3}, Last Error: {4}) took {0} ms.", - duration, startingFeatureNumber, rt, sense, LastError); + /// Sends the MMC READ DISC STRUCTURE command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ DISC STRUCTURE response will be stored + /// Sense buffer. + /// Medium type for requested disc structure + /// Medium address for requested disc structure + /// Medium layer for requested disc structure + /// Timeout in seconds. + /// Which disc structure are we requesting + /// AGID used in medium copy protection + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadDiscStructure(out byte[] buffer, out byte[] senseBuffer, MmcDiscStructureMediaType mediaType, + uint address, byte layerNumber, MmcDiscStructureFormat format, byte agid, + uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + buffer = new byte[8]; - return sense; - } + cdb[0] = (byte)ScsiCommands.ReadDiscStructure; + cdb[1] = (byte)((byte)mediaType & 0x0F); + cdb[2] = (byte)((address & 0xFF000000) >> 24); + cdb[3] = (byte)((address & 0xFF0000) >> 16); + cdb[4] = (byte)((address & 0xFF00) >> 8); + cdb[5] = (byte)(address & 0xFF); + cdb[6] = layerNumber; + cdb[7] = (byte)format; + cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[9] = (byte)(buffer.Length & 0xFF); + cdb[10] = (byte)((agid & 0x03) << 6); - /// Sends the MMC READ DISC STRUCTURE command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ DISC STRUCTURE response will be stored - /// Sense buffer. - /// Medium type for requested disc structure - /// Medium address for requested disc structure - /// Medium layer for requested disc structure - /// Timeout in seconds. - /// Which disc structure are we requesting - /// AGID used in medium copy protection - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadDiscStructure(out byte[] buffer, out byte[] senseBuffer, MmcDiscStructureMediaType mediaType, - uint address, byte layerNumber, MmcDiscStructureFormat format, byte agid, - uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - buffer = new byte[8]; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - cdb[0] = (byte)ScsiCommands.ReadDiscStructure; - cdb[1] = (byte)((byte)mediaType & 0x0F); - cdb[2] = (byte)((address & 0xFF000000) >> 24); - cdb[3] = (byte)((address & 0xFF0000) >> 16); - cdb[4] = (byte)((address & 0xFF00) >> 8); - cdb[5] = (byte)(address & 0xFF); - cdb[6] = layerNumber; - cdb[7] = (byte)format; - cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[9] = (byte)(buffer.Length & 0xFF); - cdb[10] = (byte)((agid & 0x03) << 6); + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + if(sense) + return true; - Error = LastError != 0; + ushort strctLength = (ushort)((buffer[0] << 8) + buffer[1] + 2); - if(sense) - return true; + // WORKAROUND: Some drives return incorrect length information. As these structures are fixed length just apply known length. + if(mediaType == MmcDiscStructureMediaType.Bd) + switch(format) + { + case MmcDiscStructureFormat.DiscInformation: + buffer = new byte[4100]; - ushort strctLength = (ushort)((buffer[0] << 8) + buffer[1] + 2); + break; + case MmcDiscStructureFormat.BdBurstCuttingArea: + buffer = new byte[68]; - // WORKAROUND: Some drives return incorrect length information. As these structures are fixed length just apply known length. - if(mediaType == MmcDiscStructureMediaType.Bd) - switch(format) - { - case MmcDiscStructureFormat.DiscInformation: - buffer = new byte[4100]; + break; + case MmcDiscStructureFormat.BdDds: + buffer = new byte[strctLength < 100 ? 100 : strctLength]; - break; - case MmcDiscStructureFormat.BdBurstCuttingArea: - buffer = new byte[68]; + break; + case MmcDiscStructureFormat.CartridgeStatus: + buffer = new byte[8]; - break; - case MmcDiscStructureFormat.BdDds: - buffer = new byte[strctLength < 100 ? 100 : strctLength]; + break; + case MmcDiscStructureFormat.BdSpareAreaInformation: + buffer = new byte[16]; - break; - case MmcDiscStructureFormat.CartridgeStatus: - buffer = new byte[8]; + break; + default: + buffer = new byte[strctLength]; - break; - case MmcDiscStructureFormat.BdSpareAreaInformation: - buffer = new byte[16]; - - break; - default: - buffer = new byte[strctLength]; - - break; - } - else - buffer = new byte[strctLength]; - - cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[9] = (byte)(buffer.Length & 0xFF); - senseBuffer = new byte[64]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", - "READ DISC STRUCTURE (Media Type: {1}, Address: {2}, Layer Number: {3}, Format: {4}, AGID: {5}, Sense: {6}, Last Error: {7}) took {0} ms.", - duration, mediaType, address, layerNumber, format, agid, sense, LastError); - - return sense; - } - - /// Sends the MMC READ TOC/PMA/ATIP command to get formatted TOC from disc, in MM:SS:FF format - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// Start TOC from this track - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadToc(out byte[] buffer, out byte[] senseBuffer, byte track, uint timeout, out double duration) => - ReadTocPmaAtip(out buffer, out senseBuffer, true, 0, track, timeout, out duration); - - /// Sends the MMC READ TOC/PMA/ATIP command to get formatted TOC from disc - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// If true, request data in MM:SS:FF units, otherwise, in blocks - /// Start TOC from this track - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadToc(out byte[] buffer, out byte[] senseBuffer, bool msf, byte track, uint timeout, - out double duration) => - ReadTocPmaAtip(out buffer, out senseBuffer, msf, 0, track, timeout, out duration); - - /// Sends the MMC READ TOC/PMA/ATIP command to get multi-session information, in MM:SS:FF format - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadSessionInfo(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadTocPmaAtip(out buffer, out senseBuffer, true, 1, 0, timeout, out duration); - - /// Sends the MMC READ TOC/PMA/ATIP command to get multi-session information - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// If true, request data in MM:SS:FF units, otherwise, in blocks - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadSessionInfo(out byte[] buffer, out byte[] senseBuffer, bool msf, uint timeout, - out double duration) => - ReadTocPmaAtip(out buffer, out senseBuffer, msf, 1, 0, timeout, out duration); - - /// Sends the MMC READ TOC/PMA/ATIP command to get raw TOC subchannels - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// Session which TOC to get - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadRawToc(out byte[] buffer, out byte[] senseBuffer, byte sessionNumber, uint timeout, - out double duration) => - ReadTocPmaAtip(out buffer, out senseBuffer, true, 2, sessionNumber, timeout, out duration); - - /// Sends the MMC READ TOC/PMA/ATIP command to get PMA - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadPma(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadTocPmaAtip(out buffer, out senseBuffer, true, 3, 0, timeout, out duration); - - /// Sends the MMC READ TOC/PMA/ATIP command to get ATIP - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadAtip(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadTocPmaAtip(out buffer, out senseBuffer, true, 4, 0, timeout, out duration); - - /// Sends the MMC READ TOC/PMA/ATIP command to get Lead-In CD-TEXT - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadCdText(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadTocPmaAtip(out buffer, out senseBuffer, true, 5, 0, timeout, out duration); - - /// Sends the MMC READ TOC/PMA/ATIP command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored - /// Sense buffer. - /// If true, request data in MM:SS:FF units, otherwise, in blocks - /// What structure is requested - /// Track/Session number - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadTocPmaAtip(out byte[] buffer, out byte[] senseBuffer, bool msf, byte format, - byte trackSessionNumber, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - - byte[] tmpBuffer = (format & 0x0F) == 5 ? new byte[32768] : new byte[1536]; - - cdb[0] = (byte)ScsiCommands.ReadTocPmaAtip; - - if(msf) - cdb[1] = 0x02; - - cdb[2] = (byte)(format & 0x0F); - cdb[6] = trackSessionNumber; - cdb[7] = (byte)((tmpBuffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(tmpBuffer.Length & 0xFF); - - LastError = SendScsiCommand(cdb, ref tmpBuffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - uint strctLength = (uint)((tmpBuffer[0] << 8) + tmpBuffer[1] + 2); + break; + } + else buffer = new byte[strctLength]; - if(buffer.Length <= tmpBuffer.Length) - { - Array.Copy(tmpBuffer, 0, buffer, 0, buffer.Length); + cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[9] = (byte)(buffer.Length & 0xFF); + senseBuffer = new byte[64]; - AaruConsole.DebugWriteLine("SCSI Device", - "READ TOC/PMA/ATIP took (MSF: {1}, Format: {2}, Track/Session Number: {3}, Sense: {4}, LastError: {5}) {0} ms.", - duration, msf, format, trackSessionNumber, sense, LastError); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); - return sense; - } + Error = LastError != 0; - double tmpDuration = duration; + AaruConsole.DebugWriteLine("SCSI Device", + "READ DISC STRUCTURE (Media Type: {1}, Address: {2}, Layer Number: {3}, Format: {4}, AGID: {5}, Sense: {6}, Last Error: {7}) took {0} ms.", + duration, mediaType, address, layerNumber, format, agid, sense, LastError); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); + return sense; + } - Error = LastError != 0; + /// Sends the MMC READ TOC/PMA/ATIP command to get formatted TOC from disc, in MM:SS:FF format + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// Start TOC from this track + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadToc(out byte[] buffer, out byte[] senseBuffer, byte track, uint timeout, out double duration) => + ReadTocPmaAtip(out buffer, out senseBuffer, true, 0, track, timeout, out duration); - duration += tmpDuration; + /// Sends the MMC READ TOC/PMA/ATIP command to get formatted TOC from disc + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// If true, request data in MM:SS:FF units, otherwise, in blocks + /// Start TOC from this track + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadToc(out byte[] buffer, out byte[] senseBuffer, bool msf, byte track, uint timeout, + out double duration) => + ReadTocPmaAtip(out buffer, out senseBuffer, msf, 0, track, timeout, out duration); + + /// Sends the MMC READ TOC/PMA/ATIP command to get multi-session information, in MM:SS:FF format + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadSessionInfo(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadTocPmaAtip(out buffer, out senseBuffer, true, 1, 0, timeout, out duration); + + /// Sends the MMC READ TOC/PMA/ATIP command to get multi-session information + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// If true, request data in MM:SS:FF units, otherwise, in blocks + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadSessionInfo(out byte[] buffer, out byte[] senseBuffer, bool msf, uint timeout, + out double duration) => + ReadTocPmaAtip(out buffer, out senseBuffer, msf, 1, 0, timeout, out duration); + + /// Sends the MMC READ TOC/PMA/ATIP command to get raw TOC subchannels + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// Session which TOC to get + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadRawToc(out byte[] buffer, out byte[] senseBuffer, byte sessionNumber, uint timeout, + out double duration) => + ReadTocPmaAtip(out buffer, out senseBuffer, true, 2, sessionNumber, timeout, out duration); + + /// Sends the MMC READ TOC/PMA/ATIP command to get PMA + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadPma(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadTocPmaAtip(out buffer, out senseBuffer, true, 3, 0, timeout, out duration); + + /// Sends the MMC READ TOC/PMA/ATIP command to get ATIP + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadAtip(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadTocPmaAtip(out buffer, out senseBuffer, true, 4, 0, timeout, out duration); + + /// Sends the MMC READ TOC/PMA/ATIP command to get Lead-In CD-TEXT + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadCdText(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadTocPmaAtip(out buffer, out senseBuffer, true, 5, 0, timeout, out duration); + + /// Sends the MMC READ TOC/PMA/ATIP command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ TOC/PMA/ATIP response will be stored + /// Sense buffer. + /// If true, request data in MM:SS:FF units, otherwise, in blocks + /// What structure is requested + /// Track/Session number + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadTocPmaAtip(out byte[] buffer, out byte[] senseBuffer, bool msf, byte format, + byte trackSessionNumber, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + + byte[] tmpBuffer = (format & 0x0F) == 5 ? new byte[32768] : new byte[1536]; + + cdb[0] = (byte)ScsiCommands.ReadTocPmaAtip; + + if(msf) + cdb[1] = 0x02; + + cdb[2] = (byte)(format & 0x0F); + cdb[6] = trackSessionNumber; + cdb[7] = (byte)((tmpBuffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(tmpBuffer.Length & 0xFF); + + LastError = SendScsiCommand(cdb, ref tmpBuffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + uint strctLength = (uint)((tmpBuffer[0] << 8) + tmpBuffer[1] + 2); + buffer = new byte[strctLength]; + + if(buffer.Length <= tmpBuffer.Length) + { + Array.Copy(tmpBuffer, 0, buffer, 0, buffer.Length); AaruConsole.DebugWriteLine("SCSI Device", "READ TOC/PMA/ATIP took (MSF: {1}, Format: {2}, Track/Session Number: {3}, Sense: {4}, LastError: {5}) {0} ms.", @@ -340,482 +324,497 @@ namespace Aaru.Devices return sense; } - /// Sends the MMC READ DISC INFORMATION command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ DISC INFORMATION response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadDiscInformation(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadDiscInformation(out buffer, out senseBuffer, MmcDiscInformationDataTypes.DiscInformation, timeout, - out duration); + double tmpDuration = duration; - /// Sends the MMC READ DISC INFORMATION command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ DISC INFORMATION response will be stored - /// Sense buffer. - /// Which disc information to read - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadDiscInformation(out byte[] buffer, out byte[] senseBuffer, MmcDiscInformationDataTypes dataType, - uint timeout, out double duration) + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); + + Error = LastError != 0; + + duration += tmpDuration; + + AaruConsole.DebugWriteLine("SCSI Device", + "READ TOC/PMA/ATIP took (MSF: {1}, Format: {2}, Track/Session Number: {3}, Sense: {4}, LastError: {5}) {0} ms.", + duration, msf, format, trackSessionNumber, sense, LastError); + + return sense; + } + + /// Sends the MMC READ DISC INFORMATION command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ DISC INFORMATION response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadDiscInformation(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadDiscInformation(out buffer, out senseBuffer, MmcDiscInformationDataTypes.DiscInformation, timeout, + out duration); + + /// Sends the MMC READ DISC INFORMATION command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ DISC INFORMATION response will be stored + /// Sense buffer. + /// Which disc information to read + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadDiscInformation(out byte[] buffer, out byte[] senseBuffer, MmcDiscInformationDataTypes dataType, + uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + byte[] tmpBuffer = new byte[804]; + + cdb[0] = (byte)ScsiCommands.ReadDiscInformation; + cdb[1] = (byte)dataType; + cdb[7] = (byte)((tmpBuffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(tmpBuffer.Length & 0xFF); + + LastError = SendScsiCommand(cdb, ref tmpBuffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + uint strctLength = (uint)((tmpBuffer[0] << 8) + tmpBuffer[1] + 2); + + if(strctLength > tmpBuffer.Length) + strctLength = (uint)tmpBuffer.Length; + + buffer = new byte[strctLength]; + Array.Copy(tmpBuffer, 0, buffer, 0, buffer.Length); + + AaruConsole.DebugWriteLine("SCSI Device", + "READ DISC INFORMATION (Data Type: {1}, Sense: {2}, Last Error: {3}) took {0} ms.", + duration, dataType, sense, LastError); + + return sense; + } + + /// Sends the MMC READ CD command + /// true if the command failed and contains the sense buffer. + /// Buffer where the MMC READ CD response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Start block address. + /// How many blocks to read. + /// Block size. + /// Expected sector type. + /// If set to true CD-DA should be modified by mute and interpolation + /// If set to true address is relative to current position. + /// If set to true we request the sync bytes for data sectors. + /// Header codes. + /// If set to true we request the user data. + /// If set to true we request the EDC/ECC fields for data sectors. + /// C2 error options. + /// Subchannel selection. + public bool ReadCd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint transferLength, + MmcSectorTypes expectedSectorType, bool dap, bool relAddr, bool sync, + MmcHeaderCodes headerCodes, bool userData, bool edcEcc, MmcErrorField c2Error, + MmcSubchannel subchannel, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.ReadCd; + cdb[1] = (byte)((byte)expectedSectorType << 2); + + if(dap) + cdb[1] += 0x02; + + if(relAddr) + cdb[1] += 0x01; + + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[6] = (byte)((transferLength & 0xFF0000) >> 16); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); + cdb[9] = (byte)((byte)c2Error << 1); + cdb[9] += (byte)((byte)headerCodes << 5); + + if(sync) + cdb[9] += 0x80; + + if(userData) + cdb[9] += 0x10; + + if(edcEcc) + cdb[9] += 0x08; + + cdb[10] = (byte)subchannel; + + buffer = new byte[blockSize * transferLength]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", + "READ CD (LBA: {1}, Block Size: {2}, Transfer Length: {3}, Expected Sector Type: {4}, DAP: {5}, Relative Address: {6}, Sync: {7}, Headers: {8}, User Data: {9}, ECC/EDC: {10}, C2: {11}, Subchannel: {12}, Sense: {13}, Last Error: {14}) took {0} ms.", + duration, lba, blockSize, transferLength, expectedSectorType, dap, relAddr, sync, + headerCodes, userData, edcEcc, c2Error, subchannel, sense, LastError); + + return sense; + } + + /// Sends the MMC READ CD MSF command + /// true if the command failed and contains the sense buffer. + /// Buffer where the MMC READ CD MSF response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Start MM:SS:FF of read encoded as 0x00MMSSFF. + /// End MM:SS:FF of read encoded as 0x00MMSSFF. + /// Block size. + /// Expected sector type. + /// If set to true CD-DA should be modified by mute and interpolation + /// If set to true we request the sync bytes for data sectors. + /// Header codes. + /// If set to true we request the user data. + /// If set to true we request the EDC/ECC fields for data sectors. + /// C2 error options. + /// Subchannel selection. + public bool ReadCdMsf(out byte[] buffer, out byte[] senseBuffer, uint startMsf, uint endMsf, uint blockSize, + MmcSectorTypes expectedSectorType, bool dap, bool sync, MmcHeaderCodes headerCodes, + bool userData, bool edcEcc, MmcErrorField c2Error, MmcSubchannel subchannel, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.ReadCdMsf; + cdb[1] = (byte)((byte)expectedSectorType << 2); + + if(dap) + cdb[1] += 0x02; + + cdb[3] = (byte)((startMsf & 0xFF0000) >> 16); + cdb[4] = (byte)((startMsf & 0xFF00) >> 8); + cdb[5] = (byte)(startMsf & 0xFF); + cdb[6] = (byte)((endMsf & 0xFF0000) >> 16); + cdb[7] = (byte)((endMsf & 0xFF00) >> 8); + cdb[8] = (byte)(endMsf & 0xFF); + cdb[9] = (byte)((byte)c2Error << 1); + cdb[9] += (byte)((byte)headerCodes << 5); + + if(sync) + cdb[9] += 0x80; + + if(userData) + cdb[9] += 0x10; + + if(edcEcc) + cdb[9] += 0x08; + + cdb[10] = (byte)subchannel; + + uint transferLength = (uint)(((cdb[6] - cdb[3]) * 60 * 75) + ((cdb[7] - cdb[4]) * 75) + (cdb[8] - cdb[5])); + + buffer = new byte[blockSize * transferLength]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", + "READ CD MSF (Start MSF: {1}, End MSF: {2}, Block Size: {3}, Expected Sector Type: {4}, DAP: {5}, Sync: {6}, Headers: {7}, User Data: {8}, ECC/EDC: {9}, C2: {10}, Subchannel: {11}, Sense: {12}, LastError: {13}) took {0} ms.", + duration, startMsf, endMsf, blockSize, expectedSectorType, dap, sync, + headerCodes, userData, edcEcc, c2Error, subchannel, sense, LastError); + + return sense; + } + + /// Prevents ejection of the media inserted in the drive + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool PreventMediumRemoval(out byte[] senseBuffer, uint timeout, out double duration) => + PreventAllowMediumRemoval(out senseBuffer, false, true, timeout, out duration); + + /// Allows ejection of the media inserted in the drive + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool AllowMediumRemoval(out byte[] senseBuffer, uint timeout, out double duration) => + PreventAllowMediumRemoval(out senseBuffer, false, false, timeout, out duration); + + /// Prevents or allows ejection of the media inserted in the drive + /// Sense buffer. + /// Persistent. + /// Prevent. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool PreventAllowMediumRemoval(out byte[] senseBuffer, bool persistent, bool prevent, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.PreventAllowMediumRemoval; + + if(prevent) + cdb[4] += 0x01; + + if(persistent) + cdb[4] += 0x02; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", + "PREVENT ALLOW MEDIUM REMOVAL (Persistent: {1}, Prevent: {2}, Sense: {3}, LastError: {4}) took {0} ms.", + duration, persistent, prevent, sense, LastError); + + return sense; + } + + /// Loads the media tray + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool LoadTray(out byte[] senseBuffer, uint timeout, out double duration) => + StartStopUnit(out senseBuffer, false, 0, 0, false, true, true, timeout, out duration); + + /// Ejects the media or its tray + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool EjectTray(out byte[] senseBuffer, uint timeout, out double duration) => + StartStopUnit(out senseBuffer, false, 0, 0, false, true, false, timeout, out duration); + + /// Starts the drive's reading mechanism + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool StartUnit(out byte[] senseBuffer, uint timeout, out double duration) => + StartStopUnit(out senseBuffer, false, 0, 0, false, false, true, timeout, out duration); + + /// Stops the drive's reading mechanism + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool StopUnit(out byte[] senseBuffer, uint timeout, out double duration) => + StartStopUnit(out senseBuffer, false, 0, 0, false, false, false, timeout, out duration); + + /// Starts or stops the drive's reading mechanism or tray + /// Sense buffer. + /// Return from execution immediately + /// Choose density layer for hybrid discs + /// Power condition + /// Change format layer + /// Loads or ejects the media + /// Starts the mechanism + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool StartStopUnit(out byte[] senseBuffer, bool immediate, byte formatLayer, byte powerConditions, + bool changeFormatLayer, bool loadEject, bool start, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.StartStopUnit; + + if(immediate) + cdb[1] += 0x01; + + if(changeFormatLayer) { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - byte[] tmpBuffer = new byte[804]; - - cdb[0] = (byte)ScsiCommands.ReadDiscInformation; - cdb[1] = (byte)dataType; - cdb[7] = (byte)((tmpBuffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(tmpBuffer.Length & 0xFF); - - LastError = SendScsiCommand(cdb, ref tmpBuffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - uint strctLength = (uint)((tmpBuffer[0] << 8) + tmpBuffer[1] + 2); - - if(strctLength > tmpBuffer.Length) - strctLength = (uint)tmpBuffer.Length; - - buffer = new byte[strctLength]; - Array.Copy(tmpBuffer, 0, buffer, 0, buffer.Length); - - AaruConsole.DebugWriteLine("SCSI Device", - "READ DISC INFORMATION (Data Type: {1}, Sense: {2}, Last Error: {3}) took {0} ms.", - duration, dataType, sense, LastError); - - return sense; + cdb[3] = (byte)(formatLayer & 0x03); + cdb[4] += 0x04; } - - /// Sends the MMC READ CD command - /// true if the command failed and contains the sense buffer. - /// Buffer where the MMC READ CD response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Start block address. - /// How many blocks to read. - /// Block size. - /// Expected sector type. - /// If set to true CD-DA should be modified by mute and interpolation - /// If set to true address is relative to current position. - /// If set to true we request the sync bytes for data sectors. - /// Header codes. - /// If set to true we request the user data. - /// If set to true we request the EDC/ECC fields for data sectors. - /// C2 error options. - /// Subchannel selection. - public bool ReadCd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint transferLength, - MmcSectorTypes expectedSectorType, bool dap, bool relAddr, bool sync, - MmcHeaderCodes headerCodes, bool userData, bool edcEcc, MmcErrorField c2Error, - MmcSubchannel subchannel, uint timeout, out double duration) + else { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.ReadCd; - cdb[1] = (byte)((byte)expectedSectorType << 2); - - if(dap) - cdb[1] += 0x02; - - if(relAddr) - cdb[1] += 0x01; - - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[6] = (byte)((transferLength & 0xFF0000) >> 16); - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); - cdb[9] = (byte)((byte)c2Error << 1); - cdb[9] += (byte)((byte)headerCodes << 5); - - if(sync) - cdb[9] += 0x80; - - if(userData) - cdb[9] += 0x10; - - if(edcEcc) - cdb[9] += 0x08; - - cdb[10] = (byte)subchannel; - - buffer = new byte[blockSize * transferLength]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", - "READ CD (LBA: {1}, Block Size: {2}, Transfer Length: {3}, Expected Sector Type: {4}, DAP: {5}, Relative Address: {6}, Sync: {7}, Headers: {8}, User Data: {9}, ECC/EDC: {10}, C2: {11}, Subchannel: {12}, Sense: {13}, Last Error: {14}) took {0} ms.", - duration, lba, blockSize, transferLength, expectedSectorType, dap, relAddr, sync, - headerCodes, userData, edcEcc, c2Error, subchannel, sense, LastError); - - return sense; - } - - /// Sends the MMC READ CD MSF command - /// true if the command failed and contains the sense buffer. - /// Buffer where the MMC READ CD MSF response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Start MM:SS:FF of read encoded as 0x00MMSSFF. - /// End MM:SS:FF of read encoded as 0x00MMSSFF. - /// Block size. - /// Expected sector type. - /// If set to true CD-DA should be modified by mute and interpolation - /// If set to true we request the sync bytes for data sectors. - /// Header codes. - /// If set to true we request the user data. - /// If set to true we request the EDC/ECC fields for data sectors. - /// C2 error options. - /// Subchannel selection. - public bool ReadCdMsf(out byte[] buffer, out byte[] senseBuffer, uint startMsf, uint endMsf, uint blockSize, - MmcSectorTypes expectedSectorType, bool dap, bool sync, MmcHeaderCodes headerCodes, - bool userData, bool edcEcc, MmcErrorField c2Error, MmcSubchannel subchannel, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.ReadCdMsf; - cdb[1] = (byte)((byte)expectedSectorType << 2); - - if(dap) - cdb[1] += 0x02; - - cdb[3] = (byte)((startMsf & 0xFF0000) >> 16); - cdb[4] = (byte)((startMsf & 0xFF00) >> 8); - cdb[5] = (byte)(startMsf & 0xFF); - cdb[6] = (byte)((endMsf & 0xFF0000) >> 16); - cdb[7] = (byte)((endMsf & 0xFF00) >> 8); - cdb[8] = (byte)(endMsf & 0xFF); - cdb[9] = (byte)((byte)c2Error << 1); - cdb[9] += (byte)((byte)headerCodes << 5); - - if(sync) - cdb[9] += 0x80; - - if(userData) - cdb[9] += 0x10; - - if(edcEcc) - cdb[9] += 0x08; - - cdb[10] = (byte)subchannel; - - uint transferLength = (uint)(((cdb[6] - cdb[3]) * 60 * 75) + ((cdb[7] - cdb[4]) * 75) + (cdb[8] - cdb[5])); - - buffer = new byte[blockSize * transferLength]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", - "READ CD MSF (Start MSF: {1}, End MSF: {2}, Block Size: {3}, Expected Sector Type: {4}, DAP: {5}, Sync: {6}, Headers: {7}, User Data: {8}, ECC/EDC: {9}, C2: {10}, Subchannel: {11}, Sense: {12}, LastError: {13}) took {0} ms.", - duration, startMsf, endMsf, blockSize, expectedSectorType, dap, sync, - headerCodes, userData, edcEcc, c2Error, subchannel, sense, LastError); - - return sense; - } - - /// Prevents ejection of the media inserted in the drive - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool PreventMediumRemoval(out byte[] senseBuffer, uint timeout, out double duration) => - PreventAllowMediumRemoval(out senseBuffer, false, true, timeout, out duration); - - /// Allows ejection of the media inserted in the drive - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool AllowMediumRemoval(out byte[] senseBuffer, uint timeout, out double duration) => - PreventAllowMediumRemoval(out senseBuffer, false, false, timeout, out duration); - - /// Prevents or allows ejection of the media inserted in the drive - /// Sense buffer. - /// Persistent. - /// Prevent. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool PreventAllowMediumRemoval(out byte[] senseBuffer, bool persistent, bool prevent, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); - - cdb[0] = (byte)ScsiCommands.PreventAllowMediumRemoval; - - if(prevent) - cdb[4] += 0x01; - - if(persistent) + if(loadEject) cdb[4] += 0x02; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", - "PREVENT ALLOW MEDIUM REMOVAL (Persistent: {1}, Prevent: {2}, Sense: {3}, LastError: {4}) took {0} ms.", - duration, persistent, prevent, sense, LastError); - - return sense; + if(start) + cdb[4] += 0x01; } - /// Loads the media tray - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool LoadTray(out byte[] senseBuffer, uint timeout, out double duration) => - StartStopUnit(out senseBuffer, false, 0, 0, false, true, true, timeout, out duration); + cdb[4] += (byte)((powerConditions & 0x0F) << 4); - /// Ejects the media or its tray - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool EjectTray(out byte[] senseBuffer, uint timeout, out double duration) => - StartStopUnit(out senseBuffer, false, 0, 0, false, true, false, timeout, out duration); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); - /// Starts the drive's reading mechanism - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool StartUnit(out byte[] senseBuffer, uint timeout, out double duration) => - StartStopUnit(out senseBuffer, false, 0, 0, false, false, true, timeout, out duration); + Error = LastError != 0; - /// Stops the drive's reading mechanism - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool StopUnit(out byte[] senseBuffer, uint timeout, out double duration) => - StartStopUnit(out senseBuffer, false, 0, 0, false, false, false, timeout, out duration); + AaruConsole.DebugWriteLine("SCSI Device", + "START STOP UNIT (Immediate: {1}, FormatLayer: {2}, Power Conditions: {3}, Change Format Layer: {4}, Load/Eject: {5}, Start: {6}, Sense: {7}, Last Error: {8}) took {0} ms.", + duration, immediate, formatLayer, powerConditions, changeFormatLayer, loadEject, + start, sense, LastError); - /// Starts or stops the drive's reading mechanism or tray - /// Sense buffer. - /// Return from execution immediately - /// Choose density layer for hybrid discs - /// Power condition - /// Change format layer - /// Loads or ejects the media - /// Starts the mechanism - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool StartStopUnit(out byte[] senseBuffer, bool immediate, byte formatLayer, byte powerConditions, - bool changeFormatLayer, bool loadEject, bool start, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); + return sense; + } - cdb[0] = (byte)ScsiCommands.StartStopUnit; + /// Reads the MCN from a disc + /// Decoded MCN. + /// Buffer containing raw drive response. + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + [SuppressMessage("ReSharper", "ShiftExpressionZeroLeftOperand")] + public bool ReadMcn(out string mcn, out byte[] buffer, out byte[] senseBuffer, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + mcn = null; - if(immediate) - cdb[1] += 0x01; + cdb[0] = (byte)ScsiCommands.ReadSubChannel; + cdb[1] = 0; + cdb[2] = 0x40; + cdb[3] = 0x02; + cdb[7] = (23 & 0xFF00) >> 8; + cdb[8] = 23 & 0xFF; - if(changeFormatLayer) - { - cdb[3] = (byte)(formatLayer & 0x03); - cdb[4] += 0x04; - } - else - { - if(loadEject) - cdb[4] += 0x02; + buffer = new byte[23]; - if(start) - cdb[4] += 0x01; - } + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - cdb[4] += (byte)((powerConditions & 0x0F) << 4); + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + AaruConsole.DebugWriteLine("SCSI Device", + "READ READ SUB-CHANNEL (MCN, Sense {1}, Last Error {2}) took {0} ms.", duration, + sense, LastError); - Error = LastError != 0; + if(!sense && + (buffer[8] & 0x80) == 0x80) + mcn = Encoding.ASCII.GetString(buffer, 9, 13); - AaruConsole.DebugWriteLine("SCSI Device", - "START STOP UNIT (Immediate: {1}, FormatLayer: {2}, Power Conditions: {3}, Change Format Layer: {4}, Load/Eject: {5}, Start: {6}, Sense: {7}, Last Error: {8}) took {0} ms.", - duration, immediate, formatLayer, powerConditions, changeFormatLayer, loadEject, - start, sense, LastError); + return sense; + } - return sense; - } + /// Reads the ISRC from a track + /// Track number. + /// Decoded ISRC. + /// Buffer containing raw drive response. + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + [SuppressMessage("ReSharper", "ShiftExpressionZeroLeftOperand")] + public bool ReadIsrc(byte trackNumber, out string isrc, out byte[] buffer, out byte[] senseBuffer, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + isrc = null; - /// Reads the MCN from a disc - /// Decoded MCN. - /// Buffer containing raw drive response. - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - [SuppressMessage("ReSharper", "ShiftExpressionZeroLeftOperand")] - public bool ReadMcn(out string mcn, out byte[] buffer, out byte[] senseBuffer, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - mcn = null; + cdb[0] = (byte)ScsiCommands.ReadSubChannel; + cdb[1] = 0; + cdb[2] = 0x40; + cdb[3] = 0x03; + cdb[6] = trackNumber; + cdb[7] = (23 & 0xFF00) >> 8; + cdb[8] = 23 & 0xFF; - cdb[0] = (byte)ScsiCommands.ReadSubChannel; - cdb[1] = 0; - cdb[2] = 0x40; - cdb[3] = 0x02; - cdb[7] = (23 & 0xFF00) >> 8; - cdb[8] = 23 & 0xFF; + buffer = new byte[23]; - buffer = new byte[23]; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("SCSI Device", + "READ READ SUB-CHANNEL (ISRC, Track Number: {1}, Sense: {2}, Last Error: {3}) took {0} ms.", + duration, trackNumber, sense, LastError); - AaruConsole.DebugWriteLine("SCSI Device", - "READ READ SUB-CHANNEL (MCN, Sense {1}, Last Error {2}) took {0} ms.", duration, - sense, LastError); + if(!sense && + (buffer[8] & 0x80) == 0x80) + isrc = Encoding.ASCII.GetString(buffer, 9, 12); - if(!sense && - (buffer[8] & 0x80) == 0x80) - mcn = Encoding.ASCII.GetString(buffer, 9, 13); + return sense; + } - return sense; - } + /// Sets the reading speed + /// Sense buffer. + /// Rotational control. + /// Read speed. + /// Write speed. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool SetCdSpeed(out byte[] senseBuffer, RotationalControl rotationalControl, ushort readSpeed, + ushort writeSpeed, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + byte[] buffer = Array.Empty(); - /// Reads the ISRC from a track - /// Track number. - /// Decoded ISRC. - /// Buffer containing raw drive response. - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - [SuppressMessage("ReSharper", "ShiftExpressionZeroLeftOperand")] - public bool ReadIsrc(byte trackNumber, out string isrc, out byte[] buffer, out byte[] senseBuffer, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - isrc = null; + cdb[0] = (byte)ScsiCommands.SetCdRomSpeed; + cdb[1] = (byte)((byte)rotationalControl & 0x03); + cdb[2] = (byte)((readSpeed & 0xFF00) >> 8); + cdb[3] = (byte)(readSpeed & 0xFF); + cdb[4] = (byte)((writeSpeed & 0xFF00) >> 8); + cdb[5] = (byte)(writeSpeed & 0xFF); - cdb[0] = (byte)ScsiCommands.ReadSubChannel; - cdb[1] = 0; - cdb[2] = 0x40; - cdb[3] = 0x03; - cdb[6] = trackNumber; - cdb[7] = (23 & 0xFF00) >> 8; - cdb[8] = 23 & 0xFF; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); - buffer = new byte[23]; + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + AaruConsole.DebugWriteLine("SCSI Device", + "SET CD SPEED (Rotational Control: {1}, Read Speed: {2}, Write Speed: {3}, Sense: {4}, Last Error: {5}) took {0} ms.", + duration, rotationalControl, readSpeed, writeSpeed, sense, LastError); - Error = LastError != 0; + return sense; + } - AaruConsole.DebugWriteLine("SCSI Device", - "READ READ SUB-CHANNEL (ISRC, Track Number: {1}, Sense: {2}, Last Error: {3}) took {0} ms.", - duration, trackNumber, sense, LastError); + /// Sends the MMC READ TRACK INFORMATION command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ DISC INFORMATION response will be stored + /// Sense buffer. + /// Track/session/sector address + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Report information of non-closed tracks + /// Type of information to retrieve + public bool ReadTrackInformation(out byte[] buffer, out byte[] senseBuffer, bool open, + TrackInformationType type, uint address, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + buffer = new byte[48]; - if(!sense && - (buffer[8] & 0x80) == 0x80) - isrc = Encoding.ASCII.GetString(buffer, 9, 12); + cdb[0] = (byte)ScsiCommands.ReadTrackInformation; + cdb[1] = (byte)((byte)type & 0x3); + cdb[2] = (byte)((address & 0xFF000000) >> 24); + cdb[3] = (byte)((address & 0xFF0000) >> 16); + cdb[4] = (byte)((address & 0xFF00) >> 8); + cdb[5] = (byte)(address & 0xFF); + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); - return sense; - } + if(open) + cdb[1] += 4; - /// Sets the reading speed - /// Sense buffer. - /// Rotational control. - /// Read speed. - /// Write speed. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool SetCdSpeed(out byte[] senseBuffer, RotationalControl rotationalControl, ushort readSpeed, - ushort writeSpeed, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - byte[] buffer = Array.Empty(); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - cdb[0] = (byte)ScsiCommands.SetCdRomSpeed; - cdb[1] = (byte)((byte)rotationalControl & 0x03); - cdb[2] = (byte)((readSpeed & 0xFF00) >> 8); - cdb[3] = (byte)(readSpeed & 0xFF); - cdb[4] = (byte)((writeSpeed & 0xFF00) >> 8); - cdb[5] = (byte)(writeSpeed & 0xFF); + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + AaruConsole.DebugWriteLine("SCSI Device", + "READ TRACK INFORMATION (Data Type: {1}, Sense: {2}, Last Error: {3}) took {0} ms.", + duration, type, sense, LastError); - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", - "SET CD SPEED (Rotational Control: {1}, Read Speed: {2}, Write Speed: {3}, Sense: {4}, Last Error: {5}) took {0} ms.", - duration, rotationalControl, readSpeed, writeSpeed, sense, LastError); - - return sense; - } - - /// Sends the MMC READ TRACK INFORMATION command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ DISC INFORMATION response will be stored - /// Sense buffer. - /// Track/session/sector address - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Report information of non-closed tracks - /// Type of information to retrieve - public bool ReadTrackInformation(out byte[] buffer, out byte[] senseBuffer, bool open, - TrackInformationType type, uint address, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - buffer = new byte[48]; - - cdb[0] = (byte)ScsiCommands.ReadTrackInformation; - cdb[1] = (byte)((byte)type & 0x3); - cdb[2] = (byte)((address & 0xFF000000) >> 24); - cdb[3] = (byte)((address & 0xFF0000) >> 16); - cdb[4] = (byte)((address & 0xFF00) >> 8); - cdb[5] = (byte)(address & 0xFF); - cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(buffer.Length & 0xFF); - - if(open) - cdb[1] += 4; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", - "READ TRACK INFORMATION (Data Type: {1}, Sense: {2}, Last Error: {3}) took {0} ms.", - duration, type, sense, LastError); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/MediaTek.cs b/Aaru.Devices/Device/ScsiCommands/MediaTek.cs index 5a88e781f..7aeaf58ab 100644 --- a/Aaru.Devices/Device/ScsiCommands/MediaTek.cs +++ b/Aaru.Devices/Device/ScsiCommands/MediaTek.cs @@ -32,44 +32,43 @@ using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Reads from the drive's DRAM. + /// true if the command failed and contains the sense buffer. + /// Buffer where the DRAM contents will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting offset in DRAM memory. + /// How much data to retrieve from DRAM. + public bool MediaTekReadDram(out byte[] buffer, out byte[] senseBuffer, uint offset, uint length, uint timeout, + out double duration) { - /// Reads from the drive's DRAM. - /// true if the command failed and contains the sense buffer. - /// Buffer where the DRAM contents will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting offset in DRAM memory. - /// How much data to retrieve from DRAM. - public bool MediaTekReadDram(out byte[] buffer, out byte[] senseBuffer, uint offset, uint length, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - buffer = new byte[length]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + buffer = new byte[length]; - cdb[0] = (byte)ScsiCommands.MediaTekVendorCommand; - cdb[1] = 0x06; - cdb[2] = (byte)((offset & 0xFF000000) >> 24); - cdb[3] = (byte)((offset & 0xFF0000) >> 16); - cdb[4] = (byte)((offset & 0xFF00) >> 8); - cdb[5] = (byte)(offset & 0xFF); - cdb[6] = (byte)((length & 0xFF000000) >> 24); - cdb[7] = (byte)((length & 0xFF0000) >> 16); - cdb[8] = (byte)((length & 0xFF00) >> 8); - cdb[9] = (byte)(length & 0xFF); + cdb[0] = (byte)ScsiCommands.MediaTekVendorCommand; + cdb[1] = 0x06; + cdb[2] = (byte)((offset & 0xFF000000) >> 24); + cdb[3] = (byte)((offset & 0xFF0000) >> 16); + cdb[4] = (byte)((offset & 0xFF00) >> 8); + cdb[5] = (byte)(offset & 0xFF); + cdb[6] = (byte)((length & 0xFF000000) >> 24); + cdb[7] = (byte)((length & 0xFF0000) >> 16); + cdb[8] = (byte)((length & 0xFF00) >> 8); + cdb[9] = (byte)(length & 0xFF); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "MediaTek READ DRAM took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "MediaTek READ DRAM took {0} ms.", duration); - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/MiniDisc.cs b/Aaru.Devices/Device/ScsiCommands/MiniDisc.cs index 2b45cc0b9..d513fc5c1 100644 --- a/Aaru.Devices/Device/ScsiCommands/MiniDisc.cs +++ b/Aaru.Devices/Device/ScsiCommands/MiniDisc.cs @@ -35,184 +35,183 @@ using Aaru.Console; // ReSharper disable InconsistentNaming -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Reads the data TOC from an MD-DATA + /// Buffer where the response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool MiniDiscReadDataTOC(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) { - /// Reads the data TOC from an MD-DATA - /// Buffer where the response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool MiniDiscReadDataTOC(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - ushort transferLength = 2336; - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + ushort transferLength = 2336; + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - cdb[0] = (byte)ScsiCommands.MiniDiscReadDTOC; + cdb[0] = (byte)ScsiCommands.MiniDiscReadDTOC; - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); - buffer = new byte[transferLength]; + buffer = new byte[transferLength]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC READ DTOC took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC READ DTOC took {0} ms.", duration); - return sense; - } + return sense; + } - /// Reads the user TOC from an MD-DATA - /// Buffer where the response will be stored - /// Sense buffer. - /// TOC sector to read - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool MiniDiscReadUserTOC(out byte[] buffer, out byte[] senseBuffer, uint sector, uint timeout, - out double duration) - { - ushort transferLength = 2336; - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + /// Reads the user TOC from an MD-DATA + /// Buffer where the response will be stored + /// Sense buffer. + /// TOC sector to read + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool MiniDiscReadUserTOC(out byte[] buffer, out byte[] senseBuffer, uint sector, uint timeout, + out double duration) + { + ushort transferLength = 2336; + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - cdb[0] = (byte)ScsiCommands.MiniDiscReadUTOC; + cdb[0] = (byte)ScsiCommands.MiniDiscReadUTOC; - cdb[2] = (byte)((sector & 0xFF000000) >> 24); - cdb[3] = (byte)((sector & 0xFF0000) >> 16); - cdb[4] = (byte)((sector & 0xFF00) >> 8); - cdb[5] = (byte)(sector & 0xFF); - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); + cdb[2] = (byte)((sector & 0xFF000000) >> 24); + cdb[3] = (byte)((sector & 0xFF0000) >> 16); + cdb[4] = (byte)((sector & 0xFF00) >> 8); + cdb[5] = (byte)(sector & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); - buffer = new byte[transferLength]; + buffer = new byte[transferLength]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC READ UTOC took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC READ UTOC took {0} ms.", duration); - return sense; - } + return sense; + } - /// Sends a D5h command to a MD-DATA drive (harmless) - /// Buffer where the response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool MiniDiscD5(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - ushort transferLength = 4; - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + /// Sends a D5h command to a MD-DATA drive (harmless) + /// Buffer where the response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool MiniDiscD5(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + ushort transferLength = 4; + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - cdb[0] = (byte)ScsiCommands.MiniDiscD5; + cdb[0] = (byte)ScsiCommands.MiniDiscD5; - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); - buffer = new byte[transferLength]; + buffer = new byte[transferLength]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC command D5h took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC command D5h took {0} ms.", duration); - return sense; - } + return sense; + } - /// Stops playing MiniDisc audio from an MD-DATA drive - /// Buffer where the response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool MiniDiscStopPlaying(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + /// Stops playing MiniDisc audio from an MD-DATA drive + /// Buffer where the response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool MiniDiscStopPlaying(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - cdb[0] = (byte)ScsiCommands.MiniDiscStopPlay; + cdb[0] = (byte)ScsiCommands.MiniDiscStopPlay; - buffer = Array.Empty(); + buffer = Array.Empty(); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC STOP PLAY took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC STOP PLAY took {0} ms.", duration); - return sense; - } + return sense; + } - /// Gets current position while playing MiniDisc audio from an MD-DATA drive - /// Buffer where the response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool MiniDiscReadPosition(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - ushort transferLength = 4; - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + /// Gets current position while playing MiniDisc audio from an MD-DATA drive + /// Buffer where the response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool MiniDiscReadPosition(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + ushort transferLength = 4; + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - cdb[0] = (byte)ScsiCommands.MiniDiscReadPosition; + cdb[0] = (byte)ScsiCommands.MiniDiscReadPosition; - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); - buffer = new byte[transferLength]; + buffer = new byte[transferLength]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC READ POSITION took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC READ POSITION took {0} ms.", duration); - return sense; - } + return sense; + } - /// Gets MiniDisc type from an MD-DATA drive - /// Buffer where the response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed and contains the sense buffer. - public bool MiniDiscGetType(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - ushort transferLength = 8; - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + /// Gets MiniDisc type from an MD-DATA drive + /// Buffer where the response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed and contains the sense buffer. + public bool MiniDiscGetType(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + ushort transferLength = 8; + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - cdb[0] = (byte)ScsiCommands.MiniDiscGetType; + cdb[0] = (byte)ScsiCommands.MiniDiscGetType; - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); - buffer = new byte[transferLength]; + buffer = new byte[transferLength]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC GET TYPE took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "MINIDISC GET TYPE took {0} ms.", duration); - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/NEC.cs b/Aaru.Devices/Device/ScsiCommands/NEC.cs index f675dd2fc..6e6f02559 100644 --- a/Aaru.Devices/Device/ScsiCommands/NEC.cs +++ b/Aaru.Devices/Device/ScsiCommands/NEC.cs @@ -32,42 +32,41 @@ using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the NEC READ CD-DA command + /// true if the command failed and contains the sense buffer. + /// Buffer where the NEC READ CD-DA response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Start block address. + /// How many blocks to read. + public bool NecReadCdDa(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, uint timeout, + out double duration) { - /// Sends the NEC READ CD-DA command - /// true if the command failed and contains the sense buffer. - /// Buffer where the NEC READ CD-DA response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Start block address. - /// How many blocks to read. - public bool NecReadCdDa(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - cdb[0] = (byte)ScsiCommands.NecReadCdDa; - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); + cdb[0] = (byte)ScsiCommands.NecReadCdDa; + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); - buffer = new byte[2352 * transferLength]; + buffer = new byte[2352 * transferLength]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "READ CD-DA took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "READ CD-DA took {0} ms.", duration); - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/Optical.cs b/Aaru.Devices/Device/ScsiCommands/Optical.cs index 812856c3b..7a737b0e4 100644 --- a/Aaru.Devices/Device/ScsiCommands/Optical.cs +++ b/Aaru.Devices/Device/ScsiCommands/Optical.cs @@ -34,100 +34,99 @@ using System; using Aaru.Console; using Aaru.Decoders.SCSI; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Scan the medium for a contiguous set of written or blank logical blocks + /// Sense buffer. + /// Set to true if is relative + /// Logical block address where to start the search. + /// Number of blocks to scan + /// How many blocks were found + /// Timeout. + /// Duration. + /// + /// If set to true drive will search for written blocks, otherwise it will search for blank + /// blocks + /// + /// If set to true drive will consider the search area has contiguous blocks + /// If set to true drive will search in reverse + /// + /// If set to true return even if the total number of blocks requested is not found but the + /// other parameters are met + /// + /// Number of contiguous blocks to find + /// First LBA found + public bool MediumScan(out byte[] senseBuffer, bool written, bool advancedScan, bool reverse, bool partial, + bool relAddr, uint lba, uint requested, uint scanLength, out uint foundLba, + out uint foundBlocks, uint timeout, out double duration) { - /// Scan the medium for a contiguous set of written or blank logical blocks - /// Sense buffer. - /// Set to true if is relative - /// Logical block address where to start the search. - /// Number of blocks to scan - /// How many blocks were found - /// Timeout. - /// Duration. - /// - /// If set to true drive will search for written blocks, otherwise it will search for blank - /// blocks - /// - /// If set to true drive will consider the search area has contiguous blocks - /// If set to true drive will search in reverse - /// - /// If set to true return even if the total number of blocks requested is not found but the - /// other parameters are met - /// - /// Number of contiguous blocks to find - /// First LBA found - public bool MediumScan(out byte[] senseBuffer, bool written, bool advancedScan, bool reverse, bool partial, - bool relAddr, uint lba, uint requested, uint scanLength, out uint foundLba, - out uint foundBlocks, uint timeout, out double duration) + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + byte[] buffer = Array.Empty(); + foundLba = 0; + foundBlocks = 0; + + cdb[0] = (byte)ScsiCommands.MediumScan; + + if(written) + cdb[1] += 0x10; + + if(advancedScan) + cdb[1] += 0x08; + + if(reverse) + cdb[1] += 0x04; + + if(partial) + cdb[1] += 0x02; + + if(relAddr) + cdb[1] += 0x01; + + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + + if(requested > 0 || + scanLength > 1) { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - byte[] buffer = Array.Empty(); - foundLba = 0; - foundBlocks = 0; + buffer = new byte[8]; + buffer[0] = (byte)((requested & 0xFF000000) >> 24); + buffer[1] = (byte)((requested & 0xFF0000) >> 16); + buffer[2] = (byte)((requested & 0xFF00) >> 8); + buffer[3] = (byte)(requested & 0xFF); + buffer[4] = (byte)((scanLength & 0xFF000000) >> 24); + buffer[5] = (byte)((scanLength & 0xFF0000) >> 16); + buffer[6] = (byte)((scanLength & 0xFF00) >> 8); + buffer[7] = (byte)(scanLength & 0xFF); + cdb[8] = 8; + } - cdb[0] = (byte)ScsiCommands.MediumScan; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, + buffer.Length == 0 ? ScsiDirection.None : ScsiDirection.Out, out duration, + out bool sense); - if(written) - cdb[1] += 0x10; + Error = LastError != 0; - if(advancedScan) - cdb[1] += 0x08; + AaruConsole.DebugWriteLine("SCSI Device", "MEDIUM SCAN took {0} ms.", duration); - if(reverse) - cdb[1] += 0x04; + if(Error) + return sense; - if(partial) - cdb[1] += 0x02; + DecodedSense? decodedSense = Sense.Decode(senseBuffer); - if(relAddr) - cdb[1] += 0x01; + switch(decodedSense?.SenseKey) + { + case SenseKeys.NoSense: return false; + case SenseKeys.Equal when decodedSense.Value.Fixed?.InformationValid == true: + foundBlocks = decodedSense.Value.Fixed.Value.CommandSpecific; + foundLba = decodedSense.Value.Fixed.Value.Information; - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - - if(requested > 0 || - scanLength > 1) - { - buffer = new byte[8]; - buffer[0] = (byte)((requested & 0xFF000000) >> 24); - buffer[1] = (byte)((requested & 0xFF0000) >> 16); - buffer[2] = (byte)((requested & 0xFF00) >> 8); - buffer[3] = (byte)(requested & 0xFF); - buffer[4] = (byte)((scanLength & 0xFF000000) >> 24); - buffer[5] = (byte)((scanLength & 0xFF0000) >> 16); - buffer[6] = (byte)((scanLength & 0xFF00) >> 8); - buffer[7] = (byte)(scanLength & 0xFF); - cdb[8] = 8; - } - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, - buffer.Length == 0 ? ScsiDirection.None : ScsiDirection.Out, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "MEDIUM SCAN took {0} ms.", duration); - - if(Error) - return sense; - - DecodedSense? decodedSense = Sense.Decode(senseBuffer); - - switch(decodedSense?.SenseKey) - { - case SenseKeys.NoSense: return false; - case SenseKeys.Equal when decodedSense.Value.Fixed?.InformationValid == true: - foundBlocks = decodedSense.Value.Fixed.Value.CommandSpecific; - foundLba = decodedSense.Value.Fixed.Value.Information; - - return false; - default: return sense; - } + return false; + default: return sense; } } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/Pioneer.cs b/Aaru.Devices/Device/ScsiCommands/Pioneer.cs index 26de1638d..880f9d9e3 100644 --- a/Aaru.Devices/Device/ScsiCommands/Pioneer.cs +++ b/Aaru.Devices/Device/ScsiCommands/Pioneer.cs @@ -32,139 +32,138 @@ using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the Pioneer READ CD-DA command + /// true if the command failed and contains the sense buffer. + /// Buffer where the Pioneer READ CD-DA response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Start block address. + /// How many blocks to read. + /// Block size. + /// Subchannel selection. + public bool PioneerReadCdDa(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, + uint transferLength, PioneerSubchannel subchannel, uint timeout, + out double duration) { - /// Sends the Pioneer READ CD-DA command - /// true if the command failed and contains the sense buffer. - /// Buffer where the Pioneer READ CD-DA response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Start block address. - /// How many blocks to read. - /// Block size. - /// Subchannel selection. - public bool PioneerReadCdDa(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, - uint transferLength, PioneerSubchannel subchannel, uint timeout, - out double duration) + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.ReadCdDa; + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF0000) >> 16); + cdb[8] = (byte)((transferLength & 0xFF00) >> 8); + cdb[9] = (byte)(transferLength & 0xFF); + cdb[10] = (byte)subchannel; + + buffer = new byte[blockSize * transferLength]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PIONEER READ CD-DA took {0} ms.", duration); + + return sense; + } + + /// Sends the Pioneer READ CD-DA MSF command + /// true if the command failed and contains the sense buffer. + /// Buffer where the Pioneer READ CD-DA MSF response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Start MM:SS:FF of read encoded as 0x00MMSSFF. + /// End MM:SS:FF of read encoded as 0x00MMSSFF. + /// Block size. + /// Subchannel selection. + public bool PioneerReadCdDaMsf(out byte[] buffer, out byte[] senseBuffer, uint startMsf, uint endMsf, + uint blockSize, PioneerSubchannel subchannel, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.ReadCdDaMsf; + cdb[3] = (byte)((startMsf & 0xFF0000) >> 16); + cdb[4] = (byte)((startMsf & 0xFF00) >> 8); + cdb[5] = (byte)(startMsf & 0xFF); + cdb[7] = (byte)((endMsf & 0xFF0000) >> 16); + cdb[8] = (byte)((endMsf & 0xFF00) >> 8); + cdb[9] = (byte)(endMsf & 0xFF); + cdb[10] = (byte)subchannel; + + uint transferLength = (uint)(((cdb[7] - cdb[3]) * 60 * 75) + ((cdb[8] - cdb[4]) * 75) + (cdb[9] - cdb[5])); + buffer = new byte[blockSize * transferLength]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PIONEER READ CD-DA MSF took {0} ms.", duration); + + return sense; + } + + /// Sends the Pioneer READ CD-XA command + /// true if the command failed and contains the sense buffer. + /// Buffer where the Pioneer READ CD-XA response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// + /// If set to true, returns all sector data with 294 bytes of error flags. Superseedes + /// + /// + /// If set to true, returns all 2352 bytes of sector data. + /// Start block address. + /// How many blocks to read. + public bool PioneerReadCdXa(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, + bool errorFlags, bool wholeSector, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.ReadCdXa; + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF0000) >> 16); + cdb[8] = (byte)((transferLength & 0xFF00) >> 8); + cdb[9] = (byte)(transferLength & 0xFF); + + if(errorFlags) { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.ReadCdDa; - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[7] = (byte)((transferLength & 0xFF0000) >> 16); - cdb[8] = (byte)((transferLength & 0xFF00) >> 8); - cdb[9] = (byte)(transferLength & 0xFF); - cdb[10] = (byte)subchannel; - - buffer = new byte[blockSize * transferLength]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PIONEER READ CD-DA took {0} ms.", duration); - - return sense; + buffer = new byte[2646 * transferLength]; + cdb[6] = 0x1F; + } + else if(wholeSector) + { + buffer = new byte[2352 * transferLength]; + cdb[6] = 0x0F; + } + else + { + buffer = new byte[2048 * transferLength]; + cdb[6] = 0x00; } - /// Sends the Pioneer READ CD-DA MSF command - /// true if the command failed and contains the sense buffer. - /// Buffer where the Pioneer READ CD-DA MSF response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Start MM:SS:FF of read encoded as 0x00MMSSFF. - /// End MM:SS:FF of read encoded as 0x00MMSSFF. - /// Block size. - /// Subchannel selection. - public bool PioneerReadCdDaMsf(out byte[] buffer, out byte[] senseBuffer, uint startMsf, uint endMsf, - uint blockSize, PioneerSubchannel subchannel, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - cdb[0] = (byte)ScsiCommands.ReadCdDaMsf; - cdb[3] = (byte)((startMsf & 0xFF0000) >> 16); - cdb[4] = (byte)((startMsf & 0xFF00) >> 8); - cdb[5] = (byte)(startMsf & 0xFF); - cdb[7] = (byte)((endMsf & 0xFF0000) >> 16); - cdb[8] = (byte)((endMsf & 0xFF00) >> 8); - cdb[9] = (byte)(endMsf & 0xFF); - cdb[10] = (byte)subchannel; + Error = LastError != 0; - uint transferLength = (uint)(((cdb[7] - cdb[3]) * 60 * 75) + ((cdb[8] - cdb[4]) * 75) + (cdb[9] - cdb[5])); - buffer = new byte[blockSize * transferLength]; + AaruConsole.DebugWriteLine("SCSI Device", "PIONEER READ CD-XA took {0} ms.", duration); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PIONEER READ CD-DA MSF took {0} ms.", duration); - - return sense; - } - - /// Sends the Pioneer READ CD-XA command - /// true if the command failed and contains the sense buffer. - /// Buffer where the Pioneer READ CD-XA response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// - /// If set to true, returns all sector data with 294 bytes of error flags. Superseedes - /// - /// - /// If set to true, returns all 2352 bytes of sector data. - /// Start block address. - /// How many blocks to read. - public bool PioneerReadCdXa(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, - bool errorFlags, bool wholeSector, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.ReadCdXa; - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[7] = (byte)((transferLength & 0xFF0000) >> 16); - cdb[8] = (byte)((transferLength & 0xFF00) >> 8); - cdb[9] = (byte)(transferLength & 0xFF); - - if(errorFlags) - { - buffer = new byte[2646 * transferLength]; - cdb[6] = 0x1F; - } - else if(wholeSector) - { - buffer = new byte[2352 * transferLength]; - cdb[6] = 0x0F; - } - else - { - buffer = new byte[2048 * transferLength]; - cdb[6] = 0x00; - } - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PIONEER READ CD-XA took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/Plasmon.cs b/Aaru.Devices/Device/ScsiCommands/Plasmon.cs index 40b3c3c5e..9eda6bad5 100644 --- a/Aaru.Devices/Device/ScsiCommands/Plasmon.cs +++ b/Aaru.Devices/Device/ScsiCommands/Plasmon.cs @@ -32,78 +32,77 @@ using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the Plasmon READ LONG vendor command + /// true if the command failed and contains the sense buffer. + /// Buffer where the Plasmon READ LONG response will be stored + /// Sense buffer. + /// If set to true address contain two's complement offset from last read address. + /// PBA/LBA to read. + /// How many bytes per block. + /// If set to true address contain physical block address. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool PlasmonReadLong(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, + ushort blockBytes, bool pba, uint timeout, out double duration) => + HpReadLong(out buffer, out senseBuffer, relAddr, address, 0, blockBytes, pba, false, timeout, out duration); + + /// Sends the Plasmon READ LONG vendor command + /// true if the command failed and contains the sense buffer. + /// Buffer where the Plasmon READ LONG response will be stored + /// Sense buffer. + /// If set to true address contain two's complement offset from last read address. + /// PBA/LBA to read. + /// How many blocks/bytes to read. + /// How many bytes per block. + /// If set to true address contain physical block address. + /// + /// If set to true is a count of secors to read. Otherwise + /// it will be ignored + /// + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool PlasmonReadLong(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, + ushort transferLen, ushort blockBytes, bool pba, bool sectorCount, uint timeout, + out double duration) => + HpReadLong(out buffer, out senseBuffer, relAddr, address, transferLen, blockBytes, pba, sectorCount, + timeout, out duration); + + /// Retrieves the logical or physical block address for the specified + /// true if the command failed and contains the sense buffer. + /// Buffer where the block address will be stored + /// Sense buffer. + /// PBA/LBA to read. + /// If set to true address contain a physical block address. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool PlasmonReadSectorLocation(out byte[] buffer, out byte[] senseBuffer, uint address, bool pba, + uint timeout, out double duration) { - /// Sends the Plasmon READ LONG vendor command - /// true if the command failed and contains the sense buffer. - /// Buffer where the Plasmon READ LONG response will be stored - /// Sense buffer. - /// If set to true address contain two's complement offset from last read address. - /// PBA/LBA to read. - /// How many bytes per block. - /// If set to true address contain physical block address. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool PlasmonReadLong(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, - ushort blockBytes, bool pba, uint timeout, out double duration) => - HpReadLong(out buffer, out senseBuffer, relAddr, address, 0, blockBytes, pba, false, timeout, out duration); + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - /// Sends the Plasmon READ LONG vendor command - /// true if the command failed and contains the sense buffer. - /// Buffer where the Plasmon READ LONG response will be stored - /// Sense buffer. - /// If set to true address contain two's complement offset from last read address. - /// PBA/LBA to read. - /// How many blocks/bytes to read. - /// How many bytes per block. - /// If set to true address contain physical block address. - /// - /// If set to true is a count of secors to read. Otherwise - /// it will be ignored - /// - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool PlasmonReadLong(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, - ushort transferLen, ushort blockBytes, bool pba, bool sectorCount, uint timeout, - out double duration) => - HpReadLong(out buffer, out senseBuffer, relAddr, address, transferLen, blockBytes, pba, sectorCount, - timeout, out duration); + cdb[0] = (byte)ScsiCommands.PlasmonReadSectorLocation; + cdb[2] = (byte)((address & 0xFF000000) >> 24); + cdb[3] = (byte)((address & 0xFF0000) >> 16); + cdb[4] = (byte)((address & 0xFF00) >> 8); + cdb[5] = (byte)(address & 0xFF); - /// Retrieves the logical or physical block address for the specified - /// true if the command failed and contains the sense buffer. - /// Buffer where the block address will be stored - /// Sense buffer. - /// PBA/LBA to read. - /// If set to true address contain a physical block address. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool PlasmonReadSectorLocation(out byte[] buffer, out byte[] senseBuffer, uint address, bool pba, - uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; + if(pba) + cdb[9] += 0x80; - cdb[0] = (byte)ScsiCommands.PlasmonReadSectorLocation; - cdb[2] = (byte)((address & 0xFF000000) >> 24); - cdb[3] = (byte)((address & 0xFF0000) >> 16); - cdb[4] = (byte)((address & 0xFF00) >> 8); - cdb[5] = (byte)(address & 0xFF); + buffer = new byte[8]; - if(pba) - cdb[9] += 0x80; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - buffer = new byte[8]; + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + AaruConsole.DebugWriteLine("SCSI Device", "PLASMON READ SECTOR LOCATION took {0} ms.", duration); - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLASMON READ SECTOR LOCATION took {0} ms.", duration); - - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/Plextor.cs b/Aaru.Devices/Device/ScsiCommands/Plextor.cs index d6496ef15..c805146e2 100644 --- a/Aaru.Devices/Device/ScsiCommands/Plextor.cs +++ b/Aaru.Devices/Device/ScsiCommands/Plextor.cs @@ -33,472 +33,471 @@ using Aaru.Console; using Aaru.Helpers; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the Plextor READ CD-DA command + /// true if the command failed and contains the sense buffer. + /// Buffer where the Plextor READ CD-DA response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Start block address. + /// How many blocks to read. + /// Block size. + /// Subchannel selection. + public bool PlextorReadCdDa(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, + uint transferLength, PlextorSubchannel subchannel, uint timeout, + out double duration) { - /// Sends the Plextor READ CD-DA command - /// true if the command failed and contains the sense buffer. - /// Buffer where the Plextor READ CD-DA response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Start block address. - /// How many blocks to read. - /// Block size. - /// Subchannel selection. - public bool PlextorReadCdDa(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, - uint transferLength, PlextorSubchannel subchannel, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; - cdb[0] = (byte)ScsiCommands.ReadCdDa; - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[6] = (byte)((transferLength & 0xFF000000) >> 24); - cdb[7] = (byte)((transferLength & 0xFF0000) >> 16); - cdb[8] = (byte)((transferLength & 0xFF00) >> 8); - cdb[9] = (byte)(transferLength & 0xFF); - cdb[10] = (byte)subchannel; + cdb[0] = (byte)ScsiCommands.ReadCdDa; + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[6] = (byte)((transferLength & 0xFF000000) >> 24); + cdb[7] = (byte)((transferLength & 0xFF0000) >> 16); + cdb[8] = (byte)((transferLength & 0xFF00) >> 8); + cdb[9] = (byte)(transferLength & 0xFF); + cdb[10] = (byte)subchannel; - buffer = new byte[blockSize * transferLength]; + buffer = new byte[blockSize * transferLength]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", - "Plextor READ CD-DA (LBA: {1}, Block Size: {2}, Transfer Length: {3}, Subchannel: {4}, Sense: {5}, Last Error: {6}) took {0} ms.", - duration, lba, blockSize, transferLength, subchannel, sense, LastError); + AaruConsole.DebugWriteLine("SCSI Device", + "Plextor READ CD-DA (LBA: {1}, Block Size: {2}, Transfer Length: {3}, Subchannel: {4}, Sense: {5}, Last Error: {6}) took {0} ms.", + duration, lba, blockSize, transferLength, subchannel, sense, LastError); + return sense; + } + + /// Reads a "raw" sector from DVD on Plextor drives. Does it reading drive's cache. + /// true if the command failed and contains the sense buffer. + /// Buffer where the Plextor READ DVD (RAW) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Start block address. + /// How many blocks to read. + public bool PlextorReadRawDvd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, + uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + buffer = new byte[2064 * transferLength]; + + cdb[0] = (byte)ScsiCommands.ReadBuffer; + cdb[1] = 0x02; + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[3] = (byte)((buffer.Length & 0xFF0000) >> 16); + cdb[4] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[5] = (byte)(buffer.Length & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "Plextor READ DVD (RAW) took {0} ms.", duration); + + return sense; + } + + /// Reads the statistics EEPROM from Plextor CD recorders + /// true, if EEPROM is correctly read, false otherwise. + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool PlextorReadEepromCdr(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[256]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorReadEeprom; + cdb[8] = 1; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR READ EEPROM took {0} ms.", duration); + + return sense; + } + + /// Reads the statistics EEPROM from Plextor PX-708 and PX-712 recorders + /// true, if EEPROM is correctly read, false otherwise. + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool PlextorReadEeprom(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[512]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorReadEeprom; + cdb[8] = 2; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR READ EEPROM took {0} ms.", duration); + + return sense; + } + + /// Reads a block from the statistics EEPROM from Plextor DVD recorders + /// true, if EEPROM is correctly read, false otherwise. + /// Buffer. + /// Sense buffer. + /// EEPROM block to read + /// How many bytes are in the EEPROM block + /// Timeout. + /// Duration. + public bool PlextorReadEepromBlock(out byte[] buffer, out byte[] senseBuffer, byte block, ushort blockSize, + uint timeout, out double duration) + { + buffer = new byte[blockSize]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorReadEeprom; + cdb[1] = 1; + cdb[7] = block; + cdb[8] = (byte)((blockSize & 0xFF00) >> 8); + cdb[9] = (byte)(blockSize & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR READ EEPROM took {0} ms.", duration); + + return sense; + } + + /// Gets speeds set by Plextor PoweRec + /// true, if speeds were got correctly, false otherwise. + /// Sense buffer. + /// Selected write speed. + /// Max speed for currently inserted media. + /// Last actual speed. + /// Timeout. + /// Duration. + public bool PlextorGetSpeeds(out byte[] senseBuffer, out ushort selected, out ushort max, out ushort last, + uint timeout, out double duration) + { + byte[] buf = new byte[10]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + selected = 0; + max = 0; + last = 0; + + cdb[0] = (byte)ScsiCommands.PlextorPoweRec; + cdb[9] = (byte)buf.Length; + + LastError = SendScsiCommand(cdb, ref buf, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR POWEREC GET SPEEDS took {0} ms.", duration); + + if(sense || Error) return sense; - } - /// Reads a "raw" sector from DVD on Plextor drives. Does it reading drive's cache. - /// true if the command failed and contains the sense buffer. - /// Buffer where the Plextor READ DVD (RAW) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Start block address. - /// How many blocks to read. - public bool PlextorReadRawDvd(out byte[] buffer, out byte[] senseBuffer, uint lba, uint transferLength, - uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - buffer = new byte[2064 * transferLength]; + selected = BigEndianBitConverter.ToUInt16(buf, 4); + max = BigEndianBitConverter.ToUInt16(buf, 6); + last = BigEndianBitConverter.ToUInt16(buf, 8); - cdb[0] = (byte)ScsiCommands.ReadBuffer; - cdb[1] = 0x02; - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[3] = (byte)((buffer.Length & 0xFF0000) >> 16); - cdb[4] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[5] = (byte)(buffer.Length & 0xFF); + return false; + } - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + /// Gets the Plextor PoweRec status + /// true, if PoweRec is supported, false otherwise. + /// Sense buffer. + /// PoweRec is enabled. + /// PoweRec recommended speed. + /// Timeout. + /// Duration. + public bool PlextorGetPoweRec(out byte[] senseBuffer, out bool enabled, out ushort speed, uint timeout, + out double duration) + { + byte[] buf = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; - Error = LastError != 0; + enabled = false; + speed = 0; - AaruConsole.DebugWriteLine("SCSI Device", "Plextor READ DVD (RAW) took {0} ms.", duration); + cdb[0] = (byte)ScsiCommands.PlextorExtend2; + cdb[1] = (byte)PlextorSubCommands.GetMode; + cdb[9] = (byte)buf.Length; + LastError = SendScsiCommand(cdb, ref buf, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR POWEREC GET SPEEDS took {0} ms.", duration); + + if(sense || Error) return sense; - } - - /// Reads the statistics EEPROM from Plextor CD recorders - /// true, if EEPROM is correctly read, false otherwise. - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool PlextorReadEepromCdr(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[256]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorReadEeprom; - cdb[8] = 1; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR READ EEPROM took {0} ms.", duration); - - return sense; - } - - /// Reads the statistics EEPROM from Plextor PX-708 and PX-712 recorders - /// true, if EEPROM is correctly read, false otherwise. - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool PlextorReadEeprom(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[512]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorReadEeprom; - cdb[8] = 2; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR READ EEPROM took {0} ms.", duration); - - return sense; - } - - /// Reads a block from the statistics EEPROM from Plextor DVD recorders - /// true, if EEPROM is correctly read, false otherwise. - /// Buffer. - /// Sense buffer. - /// EEPROM block to read - /// How many bytes are in the EEPROM block - /// Timeout. - /// Duration. - public bool PlextorReadEepromBlock(out byte[] buffer, out byte[] senseBuffer, byte block, ushort blockSize, - uint timeout, out double duration) - { - buffer = new byte[blockSize]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorReadEeprom; - cdb[1] = 1; - cdb[7] = block; - cdb[8] = (byte)((blockSize & 0xFF00) >> 8); - cdb[9] = (byte)(blockSize & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR READ EEPROM took {0} ms.", duration); - - return sense; - } - - /// Gets speeds set by Plextor PoweRec - /// true, if speeds were got correctly, false otherwise. - /// Sense buffer. - /// Selected write speed. - /// Max speed for currently inserted media. - /// Last actual speed. - /// Timeout. - /// Duration. - public bool PlextorGetSpeeds(out byte[] senseBuffer, out ushort selected, out ushort max, out ushort last, - uint timeout, out double duration) - { - byte[] buf = new byte[10]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - selected = 0; - max = 0; - last = 0; - - cdb[0] = (byte)ScsiCommands.PlextorPoweRec; - cdb[9] = (byte)buf.Length; - - LastError = SendScsiCommand(cdb, ref buf, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR POWEREC GET SPEEDS took {0} ms.", duration); - - if(sense || Error) - return sense; - - selected = BigEndianBitConverter.ToUInt16(buf, 4); - max = BigEndianBitConverter.ToUInt16(buf, 6); - last = BigEndianBitConverter.ToUInt16(buf, 8); - - return false; - } - - /// Gets the Plextor PoweRec status - /// true, if PoweRec is supported, false otherwise. - /// Sense buffer. - /// PoweRec is enabled. - /// PoweRec recommended speed. - /// Timeout. - /// Duration. - public bool PlextorGetPoweRec(out byte[] senseBuffer, out bool enabled, out ushort speed, uint timeout, - out double duration) - { - byte[] buf = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - enabled = false; - speed = 0; - - cdb[0] = (byte)ScsiCommands.PlextorExtend2; - cdb[1] = (byte)PlextorSubCommands.GetMode; - cdb[9] = (byte)buf.Length; - - LastError = SendScsiCommand(cdb, ref buf, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR POWEREC GET SPEEDS took {0} ms.", duration); - - if(sense || Error) - return sense; - - enabled = buf[2] != 0; - speed = BigEndianBitConverter.ToUInt16(buf, 4); - - return false; - } - - /// Gets the Plextor SilentMode status - /// true, if SilentMode is supported, false otherwise. - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool PlextorGetSilentMode(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorExtend; - cdb[1] = (byte)PlextorSubCommands.GetMode; - cdb[2] = (byte)PlextorSubCommands.Silent; - cdb[3] = 4; - cdb[10] = (byte)buffer.Length; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET SILENT MODE took {0} ms.", duration); - - return sense; - } - - /// Gets the Plextor GigaRec status - /// true, if GigaRec is supported, false otherwise. - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool PlextorGetGigaRec(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorExtend; - cdb[1] = (byte)PlextorSubCommands.GetMode; - cdb[2] = (byte)PlextorSubCommands.GigaRec; - cdb[10] = (byte)buffer.Length; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET GIGAREC took {0} ms.", duration); - - return sense; - } - - /// Gets the Plextor VariRec status - /// true, if VariRec is supported, false otherwise. - /// Buffer. - /// Sense buffer. - /// Set if request is for DVD. - /// Timeout. - /// Duration. - public bool PlextorGetVariRec(out byte[] buffer, out byte[] senseBuffer, bool dvd, uint timeout, - out double duration) - { - buffer = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorExtend; - cdb[1] = (byte)PlextorSubCommands.GetMode; - cdb[2] = (byte)PlextorSubCommands.VariRec; - cdb[10] = (byte)buffer.Length; - - if(dvd) - cdb[3] = 0x12; - else - cdb[3] = 0x02; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET VARIREC took {0} ms.", duration); - - return sense; - } - - /// Gets the Plextor SecuRec status - /// true, if SecuRec is supported, false otherwise. - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool PlextorGetSecuRec(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorExtend; - cdb[2] = (byte)PlextorSubCommands.SecuRec; - cdb[10] = (byte)buffer.Length; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET SECUREC took {0} ms.", duration); - - return sense; - } - - /// Gets the Plextor SpeedRead status - /// true, if SpeedRead is supported, false otherwise. - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool PlextorGetSpeedRead(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorExtend; - cdb[1] = (byte)PlextorSubCommands.GetMode; - cdb[2] = (byte)PlextorSubCommands.SpeedRead; - cdb[10] = (byte)buffer.Length; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET SPEEDREAD took {0} ms.", duration); - - return sense; - } - - /// Gets the Plextor CD-R and multi-session hiding status - /// true, if CD-R and multi-session hiding is supported, false otherwise. - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool PlextorGetHiding(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorExtend; - cdb[1] = (byte)PlextorSubCommands.GetMode; - cdb[2] = (byte)PlextorSubCommands.SessionHide; - cdb[9] = (byte)buffer.Length; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET SINGLE-SESSION / HIDE CD-R took {0} ms.", duration); - - return sense; - } - - /// Gets the Plextor DVD+ book bitsetting status - /// true, if DVD+ book bitsetting is supported, false otherwise. - /// Buffer. - /// Sense buffer. - /// Set if bitset is for dual layer discs. - /// Timeout. - /// Duration. - public bool PlextorGetBitsetting(out byte[] buffer, out byte[] senseBuffer, bool dualLayer, uint timeout, - out double duration) - { - buffer = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorExtend; - cdb[1] = (byte)PlextorSubCommands.GetMode; - cdb[2] = (byte)PlextorSubCommands.BitSet; - cdb[9] = (byte)buffer.Length; - - if(dualLayer) - cdb[3] = (byte)PlextorSubCommands.BitSetRdl; - else - cdb[3] = (byte)PlextorSubCommands.BitSetR; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET BOOK BITSETTING took {0} ms.", duration); - - return sense; - } - - /// Gets the Plextor DVD+ test writing status - /// true, if DVD+ test writing is supported, false otherwise. - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool PlextorGetTestWriteDvdPlus(out byte[] buffer, out byte[] senseBuffer, uint timeout, - out double duration) - { - buffer = new byte[8]; - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.PlextorExtend; - cdb[1] = (byte)PlextorSubCommands.GetMode; - cdb[2] = (byte)PlextorSubCommands.TestWriteDvdPlus; - cdb[10] = (byte)buffer.Length; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET TEST WRITE DVD+ took {0} ms.", duration); - - return sense; - } + + enabled = buf[2] != 0; + speed = BigEndianBitConverter.ToUInt16(buf, 4); + + return false; + } + + /// Gets the Plextor SilentMode status + /// true, if SilentMode is supported, false otherwise. + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool PlextorGetSilentMode(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorExtend; + cdb[1] = (byte)PlextorSubCommands.GetMode; + cdb[2] = (byte)PlextorSubCommands.Silent; + cdb[3] = 4; + cdb[10] = (byte)buffer.Length; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET SILENT MODE took {0} ms.", duration); + + return sense; + } + + /// Gets the Plextor GigaRec status + /// true, if GigaRec is supported, false otherwise. + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool PlextorGetGigaRec(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorExtend; + cdb[1] = (byte)PlextorSubCommands.GetMode; + cdb[2] = (byte)PlextorSubCommands.GigaRec; + cdb[10] = (byte)buffer.Length; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET GIGAREC took {0} ms.", duration); + + return sense; + } + + /// Gets the Plextor VariRec status + /// true, if VariRec is supported, false otherwise. + /// Buffer. + /// Sense buffer. + /// Set if request is for DVD. + /// Timeout. + /// Duration. + public bool PlextorGetVariRec(out byte[] buffer, out byte[] senseBuffer, bool dvd, uint timeout, + out double duration) + { + buffer = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorExtend; + cdb[1] = (byte)PlextorSubCommands.GetMode; + cdb[2] = (byte)PlextorSubCommands.VariRec; + cdb[10] = (byte)buffer.Length; + + if(dvd) + cdb[3] = 0x12; + else + cdb[3] = 0x02; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET VARIREC took {0} ms.", duration); + + return sense; + } + + /// Gets the Plextor SecuRec status + /// true, if SecuRec is supported, false otherwise. + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool PlextorGetSecuRec(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorExtend; + cdb[2] = (byte)PlextorSubCommands.SecuRec; + cdb[10] = (byte)buffer.Length; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET SECUREC took {0} ms.", duration); + + return sense; + } + + /// Gets the Plextor SpeedRead status + /// true, if SpeedRead is supported, false otherwise. + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool PlextorGetSpeedRead(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorExtend; + cdb[1] = (byte)PlextorSubCommands.GetMode; + cdb[2] = (byte)PlextorSubCommands.SpeedRead; + cdb[10] = (byte)buffer.Length; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET SPEEDREAD took {0} ms.", duration); + + return sense; + } + + /// Gets the Plextor CD-R and multi-session hiding status + /// true, if CD-R and multi-session hiding is supported, false otherwise. + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool PlextorGetHiding(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorExtend; + cdb[1] = (byte)PlextorSubCommands.GetMode; + cdb[2] = (byte)PlextorSubCommands.SessionHide; + cdb[9] = (byte)buffer.Length; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET SINGLE-SESSION / HIDE CD-R took {0} ms.", duration); + + return sense; + } + + /// Gets the Plextor DVD+ book bitsetting status + /// true, if DVD+ book bitsetting is supported, false otherwise. + /// Buffer. + /// Sense buffer. + /// Set if bitset is for dual layer discs. + /// Timeout. + /// Duration. + public bool PlextorGetBitsetting(out byte[] buffer, out byte[] senseBuffer, bool dualLayer, uint timeout, + out double duration) + { + buffer = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorExtend; + cdb[1] = (byte)PlextorSubCommands.GetMode; + cdb[2] = (byte)PlextorSubCommands.BitSet; + cdb[9] = (byte)buffer.Length; + + if(dualLayer) + cdb[3] = (byte)PlextorSubCommands.BitSetRdl; + else + cdb[3] = (byte)PlextorSubCommands.BitSetR; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET BOOK BITSETTING took {0} ms.", duration); + + return sense; + } + + /// Gets the Plextor DVD+ test writing status + /// true, if DVD+ test writing is supported, false otherwise. + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool PlextorGetTestWriteDvdPlus(out byte[] buffer, out byte[] senseBuffer, uint timeout, + out double duration) + { + buffer = new byte[8]; + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.PlextorExtend; + cdb[1] = (byte)PlextorSubCommands.GetMode; + cdb[2] = (byte)PlextorSubCommands.TestWriteDvdPlus; + cdb[10] = (byte)buffer.Length; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PLEXTOR GET TEST WRITE DVD+ took {0} ms.", duration); + + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/SBC.cs b/Aaru.Devices/Device/ScsiCommands/SBC.cs index 49ae076ea..840e8a671 100644 --- a/Aaru.Devices/Device/ScsiCommands/SBC.cs +++ b/Aaru.Devices/Device/ScsiCommands/SBC.cs @@ -33,404 +33,403 @@ using System; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Sends the SBC READ (6) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting block. + /// Block size in bytes. + public bool Read6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint timeout, + out double duration) => + Read6(out buffer, out senseBuffer, lba, blockSize, 1, timeout, out duration); + + /// Sends the SBC READ (6) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting block. + /// Block size in bytes. + /// How many blocks to read. + public bool Read6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, byte transferLength, + uint timeout, out double duration) { - /// Sends the SBC READ (6) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting block. - /// Block size in bytes. - public bool Read6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint timeout, - out double duration) => - Read6(out buffer, out senseBuffer, lba, blockSize, 1, timeout, out duration); + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; - /// Sends the SBC READ (6) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting block. - /// Block size in bytes. - /// How many blocks to read. - public bool Read6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, byte transferLength, - uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; + cdb[0] = (byte)ScsiCommands.Read6; + cdb[1] = (byte)((lba & 0x1F0000) >> 16); + cdb[2] = (byte)((lba & 0xFF00) >> 8); + cdb[3] = (byte)(lba & 0xFF); + cdb[4] = transferLength; - cdb[0] = (byte)ScsiCommands.Read6; - cdb[1] = (byte)((lba & 0x1F0000) >> 16); - cdb[2] = (byte)((lba & 0xFF00) >> 8); - cdb[3] = (byte)(lba & 0xFF); - cdb[4] = transferLength; + if(transferLength == 0) + buffer = new byte[256 * blockSize]; + else + buffer = new byte[transferLength * blockSize]; - if(transferLength == 0) - buffer = new byte[256 * blockSize]; - else - buffer = new byte[transferLength * blockSize]; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + AaruConsole.DebugWriteLine("SCSI Device", "READ (6) took {0} ms.", duration); - AaruConsole.DebugWriteLine("SCSI Device", "READ (6) took {0} ms.", duration); + return sense; + } - return sense; - } + /// Sends the SBC READ (10) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Instructs the drive how to check for protection information on the medium. + /// + /// If set to true requested blocks shall be assigned the lowest retention priority on cache + /// fetch/retain. + /// + /// If set to true requested blocks MUST bu read from medium and not the cache. + /// + /// If set to true requested blocks will be returned from non-volatile cache. If they're not + /// present they shall be stored there. + /// + /// Starting block. + /// Block size in bytes. + /// Group number where attributes associated with this command should be collected. + /// How many blocks to read. + /// If set to true address is relative to current position. + public bool Read10(out byte[] buffer, out byte[] senseBuffer, byte rdprotect, bool dpo, bool fua, bool fuaNv, + bool relAddr, uint lba, uint blockSize, byte groupNumber, ushort transferLength, + uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; - /// Sends the SBC READ (10) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Instructs the drive how to check for protection information on the medium. - /// - /// If set to true requested blocks shall be assigned the lowest retention priority on cache - /// fetch/retain. - /// - /// If set to true requested blocks MUST bu read from medium and not the cache. - /// - /// If set to true requested blocks will be returned from non-volatile cache. If they're not - /// present they shall be stored there. - /// - /// Starting block. - /// Block size in bytes. - /// Group number where attributes associated with this command should be collected. - /// How many blocks to read. - /// If set to true address is relative to current position. - public bool Read10(out byte[] buffer, out byte[] senseBuffer, byte rdprotect, bool dpo, bool fua, bool fuaNv, - bool relAddr, uint lba, uint blockSize, byte groupNumber, ushort transferLength, + cdb[0] = (byte)ScsiCommands.Read10; + cdb[1] = (byte)((rdprotect & 0x07) << 5); + + if(dpo) + cdb[1] += 0x10; + + if(fua) + cdb[1] += 0x08; + + if(fuaNv) + cdb[1] += 0x02; + + if(relAddr) + cdb[1] += 0x01; + + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[6] = (byte)(groupNumber & 0x1F); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); + + buffer = new byte[transferLength * blockSize]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ (10) took {0} ms.", duration); + + return sense; + } + + /// Sends the SBC READ (12) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Instructs the drive how to check for protection information on the medium. + /// + /// If set to true requested blocks shall be assigned the lowest retention priority on cache + /// fetch/retain. + /// + /// If set to true requested blocks MUST bu read from medium and not the cache. + /// + /// If set to true requested blocks will be returned from non-volatile cache. If they're not + /// present they shall be stored there. + /// + /// Starting block. + /// Block size in bytes. + /// Group number where attributes associated with this command should be collected. + /// How many blocks to read. + /// If set to true the stream playback operation should be used (MMC only). + /// If set to true address is relative to current position. + public bool Read12(out byte[] buffer, out byte[] senseBuffer, byte rdprotect, bool dpo, bool fua, bool fuaNv, + bool relAddr, uint lba, uint blockSize, byte groupNumber, uint transferLength, + bool streaming, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + + cdb[0] = (byte)ScsiCommands.Read12; + cdb[1] = (byte)((rdprotect & 0x07) << 5); + + if(dpo) + cdb[1] += 0x10; + + if(fua) + cdb[1] += 0x08; + + if(fuaNv) + cdb[1] += 0x02; + + if(relAddr) + cdb[1] += 0x01; + + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[6] = (byte)((transferLength & 0xFF000000) >> 24); + cdb[7] = (byte)((transferLength & 0xFF0000) >> 16); + cdb[8] = (byte)((transferLength & 0xFF00) >> 8); + cdb[9] = (byte)(transferLength & 0xFF); + cdb[10] = (byte)(groupNumber & 0x1F); + + if(streaming) + cdb[10] += 0x80; + + buffer = new byte[transferLength * blockSize]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ (12) took {0} ms.", duration); + + return sense; + } + + /// Sends the SBC READ (16) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Instructs the drive how to check for protection information on the medium. + /// + /// If set to true requested blocks shall be assigned the lowest retention priority on cache + /// fetch/retain. + /// + /// If set to true requested blocks MUST bu read from medium and not the cache. + /// + /// If set to true requested blocks will be returned from non-volatile cache. If they're not + /// present they shall be stored there. + /// + /// Starting block. + /// Block size in bytes. + /// Group number where attributes associated with this command should be collected. + /// How many blocks to read. + /// If set to true the stream playback operation should be used (MMC only). + public bool Read16(out byte[] buffer, out byte[] senseBuffer, byte rdprotect, bool dpo, bool fua, bool fuaNv, + ulong lba, uint blockSize, byte groupNumber, uint transferLength, bool streaming, + uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[16]; + byte[] lbaBytes = BitConverter.GetBytes(lba); + + cdb[0] = (byte)ScsiCommands.Read16; + cdb[1] = (byte)((rdprotect & 0x07) << 5); + + if(dpo) + cdb[1] += 0x10; + + if(fua) + cdb[1] += 0x08; + + if(fuaNv) + cdb[1] += 0x02; + + cdb[2] = lbaBytes[7]; + cdb[3] = lbaBytes[6]; + cdb[4] = lbaBytes[5]; + cdb[5] = lbaBytes[4]; + cdb[6] = lbaBytes[3]; + cdb[7] = lbaBytes[2]; + cdb[8] = lbaBytes[1]; + cdb[9] = lbaBytes[0]; + cdb[10] = (byte)((transferLength & 0xFF000000) >> 24); + cdb[11] = (byte)((transferLength & 0xFF0000) >> 16); + cdb[12] = (byte)((transferLength & 0xFF00) >> 8); + cdb[13] = (byte)(transferLength & 0xFF); + cdb[14] = (byte)(groupNumber & 0x1F); + + if(streaming) + cdb[14] += 0x80; + + buffer = new byte[transferLength * blockSize]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ (16) took {0} ms.", duration); + + return sense; + } + + /// Sends the SBC READ LONG (10) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ LONG response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// + /// Duration in milliseconds it took for the device to execute the command. + /// If set to true ask the drive to try to correct errors in the sector. + /// LBA to read. + /// + /// How many bytes to read. If the number is not exactly the drive's size, the command will + /// fail and incidate a delta of the size in SENSE. + /// + public bool ReadLong10(out byte[] buffer, out byte[] senseBuffer, bool correct, bool relAddr, uint lba, + ushort transferBytes, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + + cdb[0] = (byte)ScsiCommands.ReadLong; + + if(correct) + cdb[1] += 0x02; + + if(relAddr) + cdb[1] += 0x01; + + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[7] = (byte)((transferBytes & 0xFF00) >> 8); + cdb[8] = (byte)(transferBytes & 0xFF); + + buffer = new byte[transferBytes]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ LONG (10) took {0} ms.", duration); + + return sense; + } + + /// Sends the SBC READ LONG (16) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ LONG response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// If set to true ask the drive to try to correct errors in the sector. + /// LBA to read. + /// + /// How many bytes to read. If the number is not exactly the drive's size, the command will + /// fail and incidate a delta of the size in SENSE. + /// + public bool ReadLong16(out byte[] buffer, out byte[] senseBuffer, bool correct, ulong lba, uint transferBytes, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - - cdb[0] = (byte)ScsiCommands.Read10; - cdb[1] = (byte)((rdprotect & 0x07) << 5); - - if(dpo) - cdb[1] += 0x10; - - if(fua) - cdb[1] += 0x08; - - if(fuaNv) - cdb[1] += 0x02; - - if(relAddr) - cdb[1] += 0x01; - - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[6] = (byte)(groupNumber & 0x1F); - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); - - buffer = new byte[transferLength * blockSize]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ (10) took {0} ms.", duration); - - return sense; - } - - /// Sends the SBC READ (12) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Instructs the drive how to check for protection information on the medium. - /// - /// If set to true requested blocks shall be assigned the lowest retention priority on cache - /// fetch/retain. - /// - /// If set to true requested blocks MUST bu read from medium and not the cache. - /// - /// If set to true requested blocks will be returned from non-volatile cache. If they're not - /// present they shall be stored there. - /// - /// Starting block. - /// Block size in bytes. - /// Group number where attributes associated with this command should be collected. - /// How many blocks to read. - /// If set to true the stream playback operation should be used (MMC only). - /// If set to true address is relative to current position. - public bool Read12(out byte[] buffer, out byte[] senseBuffer, byte rdprotect, bool dpo, bool fua, bool fuaNv, - bool relAddr, uint lba, uint blockSize, byte groupNumber, uint transferLength, - bool streaming, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - - cdb[0] = (byte)ScsiCommands.Read12; - cdb[1] = (byte)((rdprotect & 0x07) << 5); - - if(dpo) - cdb[1] += 0x10; - - if(fua) - cdb[1] += 0x08; - - if(fuaNv) - cdb[1] += 0x02; - - if(relAddr) - cdb[1] += 0x01; - - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[6] = (byte)((transferLength & 0xFF000000) >> 24); - cdb[7] = (byte)((transferLength & 0xFF0000) >> 16); - cdb[8] = (byte)((transferLength & 0xFF00) >> 8); - cdb[9] = (byte)(transferLength & 0xFF); - cdb[10] = (byte)(groupNumber & 0x1F); - - if(streaming) - cdb[10] += 0x80; - - buffer = new byte[transferLength * blockSize]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ (12) took {0} ms.", duration); - - return sense; - } - - /// Sends the SBC READ (16) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Instructs the drive how to check for protection information on the medium. - /// - /// If set to true requested blocks shall be assigned the lowest retention priority on cache - /// fetch/retain. - /// - /// If set to true requested blocks MUST bu read from medium and not the cache. - /// - /// If set to true requested blocks will be returned from non-volatile cache. If they're not - /// present they shall be stored there. - /// - /// Starting block. - /// Block size in bytes. - /// Group number where attributes associated with this command should be collected. - /// How many blocks to read. - /// If set to true the stream playback operation should be used (MMC only). - public bool Read16(out byte[] buffer, out byte[] senseBuffer, byte rdprotect, bool dpo, bool fua, bool fuaNv, - ulong lba, uint blockSize, byte groupNumber, uint transferLength, bool streaming, - uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[16]; - byte[] lbaBytes = BitConverter.GetBytes(lba); - - cdb[0] = (byte)ScsiCommands.Read16; - cdb[1] = (byte)((rdprotect & 0x07) << 5); - - if(dpo) - cdb[1] += 0x10; - - if(fua) - cdb[1] += 0x08; - - if(fuaNv) - cdb[1] += 0x02; - - cdb[2] = lbaBytes[7]; - cdb[3] = lbaBytes[6]; - cdb[4] = lbaBytes[5]; - cdb[5] = lbaBytes[4]; - cdb[6] = lbaBytes[3]; - cdb[7] = lbaBytes[2]; - cdb[8] = lbaBytes[1]; - cdb[9] = lbaBytes[0]; - cdb[10] = (byte)((transferLength & 0xFF000000) >> 24); - cdb[11] = (byte)((transferLength & 0xFF0000) >> 16); - cdb[12] = (byte)((transferLength & 0xFF00) >> 8); - cdb[13] = (byte)(transferLength & 0xFF); - cdb[14] = (byte)(groupNumber & 0x1F); - - if(streaming) - cdb[14] += 0x80; - - buffer = new byte[transferLength * blockSize]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ (16) took {0} ms.", duration); - - return sense; - } - - /// Sends the SBC READ LONG (10) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ LONG response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// - /// Duration in milliseconds it took for the device to execute the command. - /// If set to true ask the drive to try to correct errors in the sector. - /// LBA to read. - /// - /// How many bytes to read. If the number is not exactly the drive's size, the command will - /// fail and incidate a delta of the size in SENSE. - /// - public bool ReadLong10(out byte[] buffer, out byte[] senseBuffer, bool correct, bool relAddr, uint lba, - ushort transferBytes, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - - cdb[0] = (byte)ScsiCommands.ReadLong; - - if(correct) - cdb[1] += 0x02; - - if(relAddr) - cdb[1] += 0x01; - - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[7] = (byte)((transferBytes & 0xFF00) >> 8); - cdb[8] = (byte)(transferBytes & 0xFF); - - buffer = new byte[transferBytes]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ LONG (10) took {0} ms.", duration); - - return sense; - } - - /// Sends the SBC READ LONG (16) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ LONG response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// If set to true ask the drive to try to correct errors in the sector. - /// LBA to read. - /// - /// How many bytes to read. If the number is not exactly the drive's size, the command will - /// fail and incidate a delta of the size in SENSE. - /// - public bool ReadLong16(out byte[] buffer, out byte[] senseBuffer, bool correct, ulong lba, uint transferBytes, - uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[16]; - byte[] lbaBytes = BitConverter.GetBytes(lba); - - cdb[0] = (byte)ScsiCommands.ServiceActionIn; - cdb[1] = (byte)ScsiServiceActions.ReadLong16; - cdb[2] = lbaBytes[7]; - cdb[3] = lbaBytes[6]; - cdb[4] = lbaBytes[5]; - cdb[5] = lbaBytes[4]; - cdb[6] = lbaBytes[3]; - cdb[7] = lbaBytes[2]; - cdb[8] = lbaBytes[1]; - cdb[9] = lbaBytes[0]; - cdb[12] = (byte)((transferBytes & 0xFF00) >> 8); - cdb[13] = (byte)(transferBytes & 0xFF); - - if(correct) - cdb[14] += 0x01; - - buffer = new byte[transferBytes]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ LONG (16) took {0} ms.", duration); - - return sense; - } - - /// Moves the device reading element to the specified block address - /// Sense buffer. - /// LBA. - /// Timeout. - /// Duration. - public bool Seek6(out byte[] senseBuffer, uint lba, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); - - cdb[0] = (byte)ScsiCommands.Seek6; - cdb[1] = (byte)((lba & 0x1F0000) >> 16); - cdb[2] = (byte)((lba & 0xFF00) >> 8); - cdb[3] = (byte)(lba & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "SEEK (6) took {0} ms.", duration); - - return sense; - } - - /// Moves the device reading element to the specified block address - /// Sense buffer. - /// LBA. - /// Timeout. - /// Duration. - public bool Seek10(out byte[] senseBuffer, uint lba, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - byte[] buffer = Array.Empty(); - - cdb[0] = (byte)ScsiCommands.Seek10; - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "SEEK (10) took {0} ms.", duration); - - return sense; - } + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[16]; + byte[] lbaBytes = BitConverter.GetBytes(lba); + + cdb[0] = (byte)ScsiCommands.ServiceActionIn; + cdb[1] = (byte)ScsiServiceActions.ReadLong16; + cdb[2] = lbaBytes[7]; + cdb[3] = lbaBytes[6]; + cdb[4] = lbaBytes[5]; + cdb[5] = lbaBytes[4]; + cdb[6] = lbaBytes[3]; + cdb[7] = lbaBytes[2]; + cdb[8] = lbaBytes[1]; + cdb[9] = lbaBytes[0]; + cdb[12] = (byte)((transferBytes & 0xFF00) >> 8); + cdb[13] = (byte)(transferBytes & 0xFF); + + if(correct) + cdb[14] += 0x01; + + buffer = new byte[transferBytes]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ LONG (16) took {0} ms.", duration); + + return sense; + } + + /// Moves the device reading element to the specified block address + /// Sense buffer. + /// LBA. + /// Timeout. + /// Duration. + public bool Seek6(out byte[] senseBuffer, uint lba, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.Seek6; + cdb[1] = (byte)((lba & 0x1F0000) >> 16); + cdb[2] = (byte)((lba & 0xFF00) >> 8); + cdb[3] = (byte)(lba & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "SEEK (6) took {0} ms.", duration); + + return sense; + } + + /// Moves the device reading element to the specified block address + /// Sense buffer. + /// LBA. + /// Timeout. + /// Duration. + public bool Seek10(out byte[] senseBuffer, uint lba, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.Seek10; + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "SEEK (10) took {0} ms.", duration); + + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/SMC.cs b/Aaru.Devices/Device/ScsiCommands/SMC.cs index d8b467be2..ba5e7b9f7 100644 --- a/Aaru.Devices/Device/ScsiCommands/SMC.cs +++ b/Aaru.Devices/Device/ScsiCommands/SMC.cs @@ -32,71 +32,70 @@ using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Reads an attribute from the medium auxiliary memory, or reports which elements in the changer contain one + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Element address. + /// Element type. + /// Volume number. + /// Partition number. + /// First attribute identificator. + /// If set to true device can return cached data. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, ushort element, + byte elementType, byte volume, byte partition, ushort firstAttribute, bool cache, + uint timeout, out double duration) { - /// Reads an attribute from the medium auxiliary memory, or reports which elements in the changer contain one - /// Buffer. - /// Sense buffer. - /// What to do, . - /// Element address. - /// Element type. - /// Volume number. - /// Partition number. - /// First attribute identificator. - /// If set to true device can return cached data. - /// Timeout. - /// Duration. - public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, ushort element, - byte elementType, byte volume, byte partition, ushort firstAttribute, bool cache, - uint timeout, out double duration) - { - buffer = new byte[256]; - byte[] cdb = new byte[16]; - senseBuffer = new byte[64]; + buffer = new byte[256]; + byte[] cdb = new byte[16]; + senseBuffer = new byte[64]; - cdb[0] = (byte)ScsiCommands.ReadAttribute; - cdb[1] = (byte)((byte)action & 0x1F); - cdb[2] = (byte)((element & 0xFF00) >> 8); - cdb[3] = (byte)(element & 0xFF); - cdb[4] = (byte)(elementType & 0x0F); - cdb[5] = volume; - cdb[7] = partition; - cdb[8] = (byte)((firstAttribute & 0xFF00) >> 8); - cdb[9] = (byte)(firstAttribute & 0xFF); - cdb[10] = (byte)((buffer.Length & 0xFF000000) >> 24); - cdb[11] = (byte)((buffer.Length & 0xFF0000) >> 16); - cdb[12] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[13] = (byte)(buffer.Length & 0xFF); + cdb[0] = (byte)ScsiCommands.ReadAttribute; + cdb[1] = (byte)((byte)action & 0x1F); + cdb[2] = (byte)((element & 0xFF00) >> 8); + cdb[3] = (byte)(element & 0xFF); + cdb[4] = (byte)(elementType & 0x0F); + cdb[5] = volume; + cdb[7] = partition; + cdb[8] = (byte)((firstAttribute & 0xFF00) >> 8); + cdb[9] = (byte)(firstAttribute & 0xFF); + cdb[10] = (byte)((buffer.Length & 0xFF000000) >> 24); + cdb[11] = (byte)((buffer.Length & 0xFF0000) >> 16); + cdb[12] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[13] = (byte)(buffer.Length & 0xFF); - if(cache) - cdb[14] += 0x01; + if(cache) + cdb[14] += 0x01; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - Error = LastError != 0; + Error = LastError != 0; - if(sense) - return true; + if(sense) + return true; - uint attrLen = (uint)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3] + 4); - buffer = new byte[attrLen]; - cdb[10] = (byte)((buffer.Length & 0xFF000000) >> 24); - cdb[11] = (byte)((buffer.Length & 0xFF0000) >> 16); - cdb[12] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[13] = (byte)(buffer.Length & 0xFF); - senseBuffer = new byte[64]; + uint attrLen = (uint)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3] + 4); + buffer = new byte[attrLen]; + cdb[10] = (byte)((buffer.Length & 0xFF000000) >> 24); + cdb[11] = (byte)((buffer.Length & 0xFF0000) >> 16); + cdb[12] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[13] = (byte)(buffer.Length & 0xFF); + senseBuffer = new byte[64]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); - Error = LastError != 0; + Error = LastError != 0; - AaruConsole.DebugWriteLine("SCSI Device", "READ ATTRIBUTE took {0} ms.", duration); + AaruConsole.DebugWriteLine("SCSI Device", "READ ATTRIBUTE took {0} ms.", duration); - return sense; - } + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/SPC.cs b/Aaru.Devices/Device/ScsiCommands/SPC.cs index 28aaef8b6..ecbbd1f91 100644 --- a/Aaru.Devices/Device/ScsiCommands/SPC.cs +++ b/Aaru.Devices/Device/ScsiCommands/SPC.cs @@ -35,785 +35,784 @@ using System.Diagnostics.CodeAnalysis; using Aaru.Console; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; -namespace Aaru.Devices +namespace Aaru.Devices; + +[SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +public sealed partial class Device { - [SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - public sealed partial class Device + /// Sends the SPC INQUIRY command to the device using default device timeout. + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI INQUIRY response will be stored + /// Sense buffer. + public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer) => + ScsiInquiry(out buffer, out senseBuffer, Timeout); + + /// Sends the SPC INQUIRY command to the device using default device timeout. + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI INQUIRY response will be stored + /// Sense buffer. + /// Duration in milliseconds it took for the device to execute the command. + public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, out double duration) => + ScsiInquiry(out buffer, out senseBuffer, Timeout, out duration); + + /// Sends the SPC INQUIRY command to the device. + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI INQUIRY response will be stored + /// Sense buffer. + /// Timeout in seconds. + public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, uint timeout) => + ScsiInquiry(out buffer, out senseBuffer, timeout, out _); + + /// Sends the SPC INQUIRY command to the device. + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI INQUIRY response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) { - /// Sends the SPC INQUIRY command to the device using default device timeout. - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI INQUIRY response will be stored - /// Sense buffer. - public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer) => - ScsiInquiry(out buffer, out senseBuffer, Timeout); + buffer = new byte[36]; + senseBuffer = new byte[64]; - /// Sends the SPC INQUIRY command to the device using default device timeout. - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI INQUIRY response will be stored - /// Sense buffer. - /// Duration in milliseconds it took for the device to execute the command. - public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, out double duration) => - ScsiInquiry(out buffer, out senseBuffer, Timeout, out duration); - - /// Sends the SPC INQUIRY command to the device. - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI INQUIRY response will be stored - /// Sense buffer. - /// Timeout in seconds. - public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, uint timeout) => - ScsiInquiry(out buffer, out senseBuffer, timeout, out _); - - /// Sends the SPC INQUIRY command to the device. - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI INQUIRY response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + byte[] cdb = { - buffer = new byte[36]; - senseBuffer = new byte[64]; + (byte)ScsiCommands.Inquiry, 0, 0, 0, 36, 0 + }; - byte[] cdb = - { - (byte)ScsiCommands.Inquiry, 0, 0, 0, 36, 0 - }; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + Error = LastError != 0; - Error = LastError != 0; + if(sense) + return true; - if(sense) - return true; + byte pagesLength = (byte)(buffer[4] + 5); - byte pagesLength = (byte)(buffer[4] + 5); - - cdb = new byte[] - { - (byte)ScsiCommands.Inquiry, 0, 0, 0, pagesLength, 0 - }; - - buffer = new byte[pagesLength]; - senseBuffer = new byte[64]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "INQUIRY took {0} ms.", duration); - - return sense; - } - - /// - /// Sends the SPC INQUIRY command to the device with an Extended Vital Product Data page using default device - /// timeout. - /// - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI INQUIRY response will be stored - /// Sense buffer. - /// The Extended Vital Product Data - public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, byte page) => - ScsiInquiry(out buffer, out senseBuffer, page, Timeout); - - /// - /// Sends the SPC INQUIRY command to the device with an Extended Vital Product Data page using default device - /// timeout. - /// - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI INQUIRY response will be stored - /// Sense buffer. - /// Duration in milliseconds it took for the device to execute the command. - /// The Extended Vital Product Data - public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, byte page, out double duration) => - ScsiInquiry(out buffer, out senseBuffer, page, Timeout, out duration); - - /// Sends the SPC INQUIRY command to the device with an Extended Vital Product Data page. - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI INQUIRY response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// The Extended Vital Product Data - public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, byte page, uint timeout) => - ScsiInquiry(out buffer, out senseBuffer, page, timeout, out _); - - /// Sends the SPC INQUIRY command to the device with an Extended Vital Product Data page. - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI INQUIRY response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// The Extended Vital Product Data - public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, byte page, uint timeout, out double duration) + cdb = new byte[] { - buffer = new byte[36]; - senseBuffer = new byte[64]; + (byte)ScsiCommands.Inquiry, 0, 0, 0, pagesLength, 0 + }; - byte[] cdb = - { - (byte)ScsiCommands.Inquiry, 1, page, 0, 36, 0 - }; + buffer = new byte[pagesLength]; + senseBuffer = new byte[64]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); - Error = LastError != 0; + Error = LastError != 0; - if(sense) - return true; + AaruConsole.DebugWriteLine("SCSI Device", "INQUIRY took {0} ms.", duration); - // This is because INQ was returned instead of EVPD - if(buffer[1] != page) - return true; + return sense; + } - byte pagesLength = (byte)(buffer[3] + 4); + /// + /// Sends the SPC INQUIRY command to the device with an Extended Vital Product Data page using default device + /// timeout. + /// + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI INQUIRY response will be stored + /// Sense buffer. + /// The Extended Vital Product Data + public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, byte page) => + ScsiInquiry(out buffer, out senseBuffer, page, Timeout); - cdb = new byte[] - { - (byte)ScsiCommands.Inquiry, 1, page, 0, pagesLength, 0 - }; + /// + /// Sends the SPC INQUIRY command to the device with an Extended Vital Product Data page using default device + /// timeout. + /// + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI INQUIRY response will be stored + /// Sense buffer. + /// Duration in milliseconds it took for the device to execute the command. + /// The Extended Vital Product Data + public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, byte page, out double duration) => + ScsiInquiry(out buffer, out senseBuffer, page, Timeout, out duration); - buffer = new byte[pagesLength]; - senseBuffer = new byte[64]; + /// Sends the SPC INQUIRY command to the device with an Extended Vital Product Data page. + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI INQUIRY response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// The Extended Vital Product Data + public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, byte page, uint timeout) => + ScsiInquiry(out buffer, out senseBuffer, page, timeout, out _); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); + /// Sends the SPC INQUIRY command to the device with an Extended Vital Product Data page. + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI INQUIRY response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// The Extended Vital Product Data + public bool ScsiInquiry(out byte[] buffer, out byte[] senseBuffer, byte page, uint timeout, out double duration) + { + buffer = new byte[36]; + senseBuffer = new byte[64]; - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "INQUIRY took {0} ms.", duration); - - return sense; - } - - /// Sends the SPC TEST UNIT READY command to the device - /// true, if unit is NOT ready, false otherwise. - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ScsiTestUnitReady(out byte[] senseBuffer, uint timeout, out double duration) + byte[] cdb = { - senseBuffer = new byte[64]; + (byte)ScsiCommands.Inquiry, 1, page, 0, 36, 0 + }; - byte[] cdb = - { - (byte)ScsiCommands.TestUnitReady, 0, 0, 0, 0, 0 - }; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - byte[] buffer = Array.Empty(); + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + if(sense) + return true; - Error = LastError != 0; + // This is because INQ was returned instead of EVPD + if(buffer[1] != page) + return true; - AaruConsole.DebugWriteLine("SCSI Device", "TEST UNIT READY took {0} ms.", duration); + byte pagesLength = (byte)(buffer[3] + 4); - return sense; - } - - /// Sends the SPC MODE SENSE(6) command to the device as introduced in SCSI-1 - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI MODE SENSE(6) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ModeSense(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ModeSense6(out buffer, out senseBuffer, false, ScsiModeSensePageControl.Current, 0, 0, timeout, - out duration); - - /// Sends the SPC MODE SENSE(6) command to the device as introduced in SCSI-2 - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI MODE SENSE(6) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// If set to true device MUST not return any block descriptor. - /// Page control. - /// Page code. - public bool ModeSense6(out byte[] buffer, out byte[] senseBuffer, bool dbd, - ScsiModeSensePageControl pageControl, byte pageCode, uint timeout, - out double duration) => ModeSense6(out buffer, out senseBuffer, dbd, pageControl, - pageCode, 0, timeout, out duration); - - /// Sends the SPC MODE SENSE(6) command to the device as introduced in SCSI-3 SPC-3 - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI MODE SENSE(6) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// If set to true device MUST not return any block descriptor. - /// Page control. - /// Page code. - /// Sub-page code. - public bool ModeSense6(out byte[] buffer, out byte[] senseBuffer, bool dbd, - ScsiModeSensePageControl pageControl, byte pageCode, byte subPageCode, uint timeout, - out double duration) + cdb = new byte[] { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - buffer = new byte[255]; + (byte)ScsiCommands.Inquiry, 1, page, 0, pagesLength, 0 + }; - cdb[0] = (byte)ScsiCommands.ModeSense; + buffer = new byte[pagesLength]; + senseBuffer = new byte[64]; - if(dbd) - cdb[1] = 0x08; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); - cdb[2] |= (byte)pageControl; - cdb[2] |= (byte)(pageCode & 0x3F); - cdb[3] = subPageCode; - cdb[4] = (byte)buffer.Length; - cdb[5] = 0; + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + AaruConsole.DebugWriteLine("SCSI Device", "INQUIRY took {0} ms.", duration); - Error = LastError != 0; + return sense; + } - if(sense) - return true; + /// Sends the SPC TEST UNIT READY command to the device + /// true, if unit is NOT ready, false otherwise. + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ScsiTestUnitReady(out byte[] senseBuffer, uint timeout, out double duration) + { + senseBuffer = new byte[64]; - byte modeLength = (byte)(buffer[0] + 1); - buffer = new byte[modeLength]; - cdb[4] = (byte)buffer.Length; - senseBuffer = new byte[64]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "MODE SENSE(6) took {0} ms.", duration); - - return sense; - } - - /// Sends the SPC MODE SENSE(10) command to the device as introduced in SCSI-2 - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI MODE SENSE(10) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// If set to true device MUST not return any block descriptor. - /// Page control. - /// Page code. - public bool ModeSense10(out byte[] buffer, out byte[] senseBuffer, bool dbd, - ScsiModeSensePageControl pageControl, byte pageCode, uint timeout, - out double duration) => ModeSense10(out buffer, out senseBuffer, false, dbd, - pageControl, pageCode, 0, timeout, out duration); - - /// Sends the SPC MODE SENSE(10) command to the device as introduced in SCSI-3 SPC-2 - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI MODE SENSE(10) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// If set to true device MUST not return any block descriptor. - /// Page control. - /// Page code. - /// If set means 64-bit LBAs are accepted by the caller. - public bool ModeSense10(out byte[] buffer, out byte[] senseBuffer, bool llbaa, bool dbd, - ScsiModeSensePageControl pageControl, byte pageCode, uint timeout, - out double duration) => ModeSense10(out buffer, out senseBuffer, llbaa, dbd, - pageControl, pageCode, 0, timeout, out duration); - - /// Sends the SPC MODE SENSE(10) command to the device as introduced in SCSI-3 SPC-3 - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI MODE SENSE(10) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// If set to true device MUST not return any block descriptor. - /// Page control. - /// Page code. - /// Sub-page code. - /// If set means 64-bit LBAs are accepted by the caller. - public bool ModeSense10(out byte[] buffer, out byte[] senseBuffer, bool llbaa, bool dbd, - ScsiModeSensePageControl pageControl, byte pageCode, byte subPageCode, uint timeout, - out double duration) + byte[] cdb = { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - buffer = new byte[4096]; + (byte)ScsiCommands.TestUnitReady, 0, 0, 0, 0, 0 + }; - cdb[0] = (byte)ScsiCommands.ModeSense10; + byte[] buffer = Array.Empty(); - if(llbaa) - cdb[1] |= 0x10; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); - if(dbd) - cdb[1] |= 0x08; + Error = LastError != 0; - cdb[2] |= (byte)pageControl; - cdb[2] |= (byte)(pageCode & 0x3F); - cdb[3] = subPageCode; - cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(buffer.Length & 0xFF); - cdb[9] = 0; + AaruConsole.DebugWriteLine("SCSI Device", "TEST UNIT READY took {0} ms.", duration); - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); + return sense; + } - Error = LastError != 0; + /// Sends the SPC MODE SENSE(6) command to the device as introduced in SCSI-1 + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI MODE SENSE(6) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ModeSense(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ModeSense6(out buffer, out senseBuffer, false, ScsiModeSensePageControl.Current, 0, 0, timeout, + out duration); - if(sense) - return true; + /// Sends the SPC MODE SENSE(6) command to the device as introduced in SCSI-2 + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI MODE SENSE(6) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// If set to true device MUST not return any block descriptor. + /// Page control. + /// Page code. + public bool ModeSense6(out byte[] buffer, out byte[] senseBuffer, bool dbd, + ScsiModeSensePageControl pageControl, byte pageCode, uint timeout, + out double duration) => ModeSense6(out buffer, out senseBuffer, dbd, pageControl, + pageCode, 0, timeout, out duration); - ushort modeLength = (ushort)((buffer[0] << 8) + buffer[1] + 2); - buffer = new byte[modeLength]; - cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(buffer.Length & 0xFF); - senseBuffer = new byte[64]; + /// Sends the SPC MODE SENSE(6) command to the device as introduced in SCSI-3 SPC-3 + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI MODE SENSE(6) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// If set to true device MUST not return any block descriptor. + /// Page control. + /// Page code. + /// Sub-page code. + public bool ModeSense6(out byte[] buffer, out byte[] senseBuffer, bool dbd, + ScsiModeSensePageControl pageControl, byte pageCode, byte subPageCode, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + buffer = new byte[255]; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); + cdb[0] = (byte)ScsiCommands.ModeSense; - Error = LastError != 0; + if(dbd) + cdb[1] = 0x08; - AaruConsole.DebugWriteLine("SCSI Device", "MODE SENSE(10) took {0} ms.", duration); + cdb[2] |= (byte)pageControl; + cdb[2] |= (byte)(pageCode & 0x3F); + cdb[3] = subPageCode; + cdb[4] = (byte)buffer.Length; + cdb[5] = 0; - return sense; - } + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - /// Sends the SPC PREVENT ALLOW MEDIUM REMOVAL command to prevent medium removal - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool SpcPreventMediumRemoval(out byte[] senseBuffer, uint timeout, out double duration) => - SpcPreventAllowMediumRemoval(out senseBuffer, ScsiPreventAllowMode.Prevent, timeout, out duration); + Error = LastError != 0; - /// Sends the SPC PREVENT ALLOW MEDIUM REMOVAL command to allow medium removal - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool SpcAllowMediumRemoval(out byte[] senseBuffer, uint timeout, out double duration) => - SpcPreventAllowMediumRemoval(out senseBuffer, ScsiPreventAllowMode.Allow, timeout, out duration); + if(sense) + return true; - /// Sends the SPC PREVENT ALLOW MEDIUM REMOVAL command - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true to prevent medium removal, false to allow it. - public bool SpcPreventAllowMediumRemoval(out byte[] senseBuffer, bool prevent, uint timeout, - out double duration) + byte modeLength = (byte)(buffer[0] + 1); + buffer = new byte[modeLength]; + cdb[4] = (byte)buffer.Length; + senseBuffer = new byte[64]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "MODE SENSE(6) took {0} ms.", duration); + + return sense; + } + + /// Sends the SPC MODE SENSE(10) command to the device as introduced in SCSI-2 + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI MODE SENSE(10) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// If set to true device MUST not return any block descriptor. + /// Page control. + /// Page code. + public bool ModeSense10(out byte[] buffer, out byte[] senseBuffer, bool dbd, + ScsiModeSensePageControl pageControl, byte pageCode, uint timeout, + out double duration) => ModeSense10(out buffer, out senseBuffer, false, dbd, + pageControl, pageCode, 0, timeout, out duration); + + /// Sends the SPC MODE SENSE(10) command to the device as introduced in SCSI-3 SPC-2 + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI MODE SENSE(10) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// If set to true device MUST not return any block descriptor. + /// Page control. + /// Page code. + /// If set means 64-bit LBAs are accepted by the caller. + public bool ModeSense10(out byte[] buffer, out byte[] senseBuffer, bool llbaa, bool dbd, + ScsiModeSensePageControl pageControl, byte pageCode, uint timeout, + out double duration) => ModeSense10(out buffer, out senseBuffer, llbaa, dbd, + pageControl, pageCode, 0, timeout, out duration); + + /// Sends the SPC MODE SENSE(10) command to the device as introduced in SCSI-3 SPC-3 + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI MODE SENSE(10) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// If set to true device MUST not return any block descriptor. + /// Page control. + /// Page code. + /// Sub-page code. + /// If set means 64-bit LBAs are accepted by the caller. + public bool ModeSense10(out byte[] buffer, out byte[] senseBuffer, bool llbaa, bool dbd, + ScsiModeSensePageControl pageControl, byte pageCode, byte subPageCode, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + buffer = new byte[4096]; + + cdb[0] = (byte)ScsiCommands.ModeSense10; + + if(llbaa) + cdb[1] |= 0x10; + + if(dbd) + cdb[1] |= 0x08; + + cdb[2] |= (byte)pageControl; + cdb[2] |= (byte)(pageCode & 0x3F); + cdb[3] = subPageCode; + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + cdb[9] = 0; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + if(sense) + return true; + + ushort modeLength = (ushort)((buffer[0] << 8) + buffer[1] + 2); + buffer = new byte[modeLength]; + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + senseBuffer = new byte[64]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "MODE SENSE(10) took {0} ms.", duration); + + return sense; + } + + /// Sends the SPC PREVENT ALLOW MEDIUM REMOVAL command to prevent medium removal + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool SpcPreventMediumRemoval(out byte[] senseBuffer, uint timeout, out double duration) => + SpcPreventAllowMediumRemoval(out senseBuffer, ScsiPreventAllowMode.Prevent, timeout, out duration); + + /// Sends the SPC PREVENT ALLOW MEDIUM REMOVAL command to allow medium removal + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool SpcAllowMediumRemoval(out byte[] senseBuffer, uint timeout, out double duration) => + SpcPreventAllowMediumRemoval(out senseBuffer, ScsiPreventAllowMode.Allow, timeout, out duration); + + /// Sends the SPC PREVENT ALLOW MEDIUM REMOVAL command + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true to prevent medium removal, false to allow it. + public bool SpcPreventAllowMediumRemoval(out byte[] senseBuffer, bool prevent, uint timeout, + out double duration) + { + if(prevent) + return SpcPreventAllowMediumRemoval(out senseBuffer, ScsiPreventAllowMode.Prevent, timeout, + out duration); + + return SpcPreventAllowMediumRemoval(out senseBuffer, ScsiPreventAllowMode.Allow, timeout, out duration); + } + + /// Sends the SPC PREVENT ALLOW MEDIUM REMOVAL command + /// true if the command failed and contains the sense buffer. + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Prevention mode. + public bool SpcPreventAllowMediumRemoval(out byte[] senseBuffer, ScsiPreventAllowMode preventMode, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.PreventAllowMediumRemoval; + cdb[4] = (byte)((byte)preventMode & 0x03); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "PREVENT ALLOW MEDIUM REMOVAL took {0} ms.", duration); + + return sense; + } + + /// Sends the SPC READ CAPACITY command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ CAPACITY response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadCapacity(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadCapacity(out buffer, out senseBuffer, false, 0, false, timeout, out duration); + + /// Sends the SPC READ CAPACITY command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ CAPACITY response will be stored + /// Sense buffer. + /// Indicates that is relative to current medium position + /// Address where information is requested from, only valid if is set + /// If set, it is requesting partial media capacity + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadCapacity(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, bool pmi, + uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + buffer = new byte[8]; + + cdb[0] = (byte)ScsiCommands.ReadCapacity; + + if(pmi) { - if(prevent) - return SpcPreventAllowMediumRemoval(out senseBuffer, ScsiPreventAllowMode.Prevent, timeout, - out duration); + cdb[8] = 0x01; - return SpcPreventAllowMediumRemoval(out senseBuffer, ScsiPreventAllowMode.Allow, timeout, out duration); - } - - /// Sends the SPC PREVENT ALLOW MEDIUM REMOVAL command - /// true if the command failed and contains the sense buffer. - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Prevention mode. - public bool SpcPreventAllowMediumRemoval(out byte[] senseBuffer, ScsiPreventAllowMode preventMode, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); - - cdb[0] = (byte)ScsiCommands.PreventAllowMediumRemoval; - cdb[4] = (byte)((byte)preventMode & 0x03); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "PREVENT ALLOW MEDIUM REMOVAL took {0} ms.", duration); - - return sense; - } - - /// Sends the SPC READ CAPACITY command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ CAPACITY response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadCapacity(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadCapacity(out buffer, out senseBuffer, false, 0, false, timeout, out duration); - - /// Sends the SPC READ CAPACITY command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ CAPACITY response will be stored - /// Sense buffer. - /// Indicates that is relative to current medium position - /// Address where information is requested from, only valid if is set - /// If set, it is requesting partial media capacity - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadCapacity(out byte[] buffer, out byte[] senseBuffer, bool relAddr, uint address, bool pmi, - uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - buffer = new byte[8]; - - cdb[0] = (byte)ScsiCommands.ReadCapacity; - - if(pmi) - { - cdb[8] = 0x01; - - if(relAddr) - cdb[1] = 0x01; - - cdb[2] = (byte)((address & 0xFF000000) >> 24); - cdb[3] = (byte)((address & 0xFF0000) >> 16); - cdb[4] = (byte)((address & 0xFF00) >> 8); - cdb[5] = (byte)(address & 0xFF); - } - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ CAPACITY took {0} ms.", duration); - - return sense; - } - - /// Sends the SPC READ CAPACITY(16) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ CAPACITY(16) response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadCapacity16(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadCapacity16(out buffer, out senseBuffer, 0, false, timeout, out duration); - - /// Sends the SPC READ CAPACITY(16) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ CAPACITY(16) response will be stored - /// Sense buffer. - /// Address where information is requested from, only valid if is set - /// If set, it is requesting partial media capacity - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadCapacity16(out byte[] buffer, out byte[] senseBuffer, ulong address, bool pmi, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[16]; - buffer = new byte[32]; - - cdb[0] = (byte)ScsiCommands.ServiceActionIn; - cdb[1] = (byte)ScsiServiceActions.ReadCapacity16; - - if(pmi) - { - cdb[14] = 0x01; - byte[] temp = BitConverter.GetBytes(address); - cdb[2] = temp[7]; - cdb[3] = temp[6]; - cdb[4] = temp[5]; - cdb[5] = temp[4]; - cdb[6] = temp[3]; - cdb[7] = temp[2]; - cdb[8] = temp[1]; - cdb[9] = temp[0]; - } - - cdb[10] = (byte)((buffer.Length & 0xFF000000) >> 24); - cdb[11] = (byte)((buffer.Length & 0xFF0000) >> 16); - cdb[12] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[13] = (byte)(buffer.Length & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ CAPACITY(16) took {0} ms.", duration); - - return sense; - } - - /// Sends the SPC READ MEDIA SERIAL NUMBER command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ MEDIA SERIAL NUMBER response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - public bool ReadMediaSerialNumber(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[12]; - buffer = new byte[4]; - - cdb[0] = (byte)ScsiCommands.ReadSerialNumber; - cdb[1] = 0x01; - cdb[6] = (byte)((buffer.Length & 0xFF000000) >> 24); - cdb[7] = (byte)((buffer.Length & 0xFF0000) >> 16); - cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[9] = (byte)(buffer.Length & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - if(sense) - return true; - - uint strctLength = (uint)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3] + 4); - buffer = new byte[strctLength]; - cdb[6] = (byte)((buffer.Length & 0xFF000000) >> 24); - cdb[7] = (byte)((buffer.Length & 0xFF0000) >> 16); - cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[9] = (byte)(buffer.Length & 0xFF); - senseBuffer = new byte[64]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ MEDIA SERIAL NUMBER took {0} ms.", duration); - - return sense; - } - - /// Reads an attribute from the medium auxiliary memory - /// Buffer. - /// Sense buffer. - /// What to do, . - /// Partition number. - /// First attribute identifier. - /// If set to true device can return cached data. - /// Timeout. - /// Duration. - public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte partition, - ushort firstAttribute, bool cache, uint timeout, out double duration) => - ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, partition, firstAttribute, cache, timeout, - out duration); - - /// Reads an attribute from the medium auxiliary memory - /// Buffer. - /// Sense buffer. - /// What to do, . - /// First attribute identifier. - /// If set to true device can return cached data. - /// Timeout. - /// Duration. - public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, - ushort firstAttribute, bool cache, uint timeout, out double duration) => - ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, 0, firstAttribute, cache, timeout, - out duration); - - /// Reads an attribute from the medium auxiliary memory - /// Buffer. - /// Sense buffer. - /// What to do, . - /// Partition number. - /// First attribute identifier. - /// Timeout. - /// Duration. - public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte partition, - ushort firstAttribute, uint timeout, out double duration) => - ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, partition, firstAttribute, false, timeout, - out duration); - - /// Reads an attribute from the medium auxiliary memory - /// Buffer. - /// Sense buffer. - /// What to do, . - /// First attribute identifier. - /// Timeout. - /// Duration. - public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, - ushort firstAttribute, uint timeout, out double duration) => - ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, 0, firstAttribute, false, timeout, - out duration); - - /// Reads an attribute from the medium auxiliary memory - /// Buffer. - /// Sense buffer. - /// What to do, . - /// Volume number. - /// Partition number. - /// First attribute identifier. - /// Timeout. - /// Duration. - public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte volume, - byte partition, ushort firstAttribute, uint timeout, out double duration) => - ReadAttribute(out buffer, out senseBuffer, action, 0, 0, volume, partition, firstAttribute, false, timeout, - out duration); - - /// Reads an attribute from the medium auxiliary memory - /// Buffer. - /// Sense buffer. - /// What to do, . - /// Volume number. - /// Partition number. - /// First attribute identifier. - /// If set to true device can return cached data. - /// Timeout. - /// Duration. - public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte volume, - byte partition, ushort firstAttribute, bool cache, uint timeout, - out double duration) => - ReadAttribute(out buffer, out senseBuffer, action, 0, 0, volume, partition, firstAttribute, cache, timeout, - out duration); - - /// Sends the SPC MODE SELECT(6) command - /// true if the command failed and contains the sense buffer. - /// Buffer with the data to be sent to the device - /// Sense buffer. - /// Set to save pages between resets. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Set if page is formatted. - public bool ModeSelect(byte[] buffer, out byte[] senseBuffer, bool pageFormat, bool savePages, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - - // Prevent overflows - if(buffer.Length > 255) - { - if(PlatformId != PlatformID.Win32NT && - PlatformId != PlatformID.Win32S && - PlatformId != PlatformID.Win32Windows && - PlatformId != PlatformID.WinCE && - PlatformId != PlatformID.WindowsPhone && - PlatformId != PlatformID.Xbox) - LastError = 75; - else - LastError = 111; - - Error = true; - duration = 0; - - return true; - } - - byte[] cdb = new byte[6]; - - cdb[0] = (byte)ScsiCommands.ModeSelect; - - if(pageFormat) - cdb[1] += 0x10; - - if(savePages) - cdb[1] += 0x01; - - cdb[4] = (byte)buffer.Length; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "MODE SELECT(6) took {0} ms.", duration); - - return sense; - } - - /// Sends the SPC MODE SELECT(10) command - /// true if the command failed and contains the sense buffer. - /// Buffer with the data to be sent to the device - /// Set to save pages between resets. - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Set if page is formatted. - public bool ModeSelect10(byte[] buffer, out byte[] senseBuffer, bool pageFormat, bool savePages, uint timeout, - out double duration) - { - senseBuffer = new byte[64]; - - // Prevent overflows - if(buffer.Length > 65535) - { - if(PlatformId != PlatformID.Win32NT && - PlatformId != PlatformID.Win32S && - PlatformId != PlatformID.Win32Windows && - PlatformId != PlatformID.WinCE && - PlatformId != PlatformID.WindowsPhone && - PlatformId != PlatformID.Xbox) - LastError = 75; - else - LastError = 111; - - Error = true; - duration = 0; - - return true; - } - - byte[] cdb = new byte[10]; - - cdb[0] = (byte)ScsiCommands.ModeSelect10; - - if(pageFormat) - cdb[1] += 0x10; - - if(savePages) - cdb[1] += 0x01; - - cdb[7] = (byte)((buffer.Length & 0xFF00) << 8); - cdb[8] = (byte)(buffer.Length & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "MODE SELECT(10) took {0} ms.", duration); - - return sense; - } - - /// Requests the device fixed sense - /// Sense buffer - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed. - public bool RequestSense(out byte[] buffer, uint timeout, out double duration) => - RequestSense(false, out buffer, timeout, out duration); - - /// Requests the device sense - /// Request a descriptor sense - /// Sense buffer - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// true if the command failed. - public bool RequestSense(bool descriptor, out byte[] buffer, uint timeout, out double duration) - { - byte[] cdb = new byte[6]; - buffer = new byte[252]; - - cdb[0] = (byte)ScsiCommands.RequestSense; - - if(descriptor) + if(relAddr) cdb[1] = 0x01; - cdb[2] = 0; - cdb[3] = 0; - cdb[4] = (byte)buffer.Length; - cdb[5] = 0; - - LastError = SendScsiCommand(cdb, ref buffer, out _, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "REQUEST SENSE took {0} ms.", duration); - - return sense; + cdb[2] = (byte)((address & 0xFF000000) >> 24); + cdb[3] = (byte)((address & 0xFF0000) >> 16); + cdb[4] = (byte)((address & 0xFF00) >> 8); + cdb[5] = (byte)(address & 0xFF); } + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ CAPACITY took {0} ms.", duration); + + return sense; + } + + /// Sends the SPC READ CAPACITY(16) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ CAPACITY(16) response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadCapacity16(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadCapacity16(out buffer, out senseBuffer, 0, false, timeout, out duration); + + /// Sends the SPC READ CAPACITY(16) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ CAPACITY(16) response will be stored + /// Sense buffer. + /// Address where information is requested from, only valid if is set + /// If set, it is requesting partial media capacity + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadCapacity16(out byte[] buffer, out byte[] senseBuffer, ulong address, bool pmi, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[16]; + buffer = new byte[32]; + + cdb[0] = (byte)ScsiCommands.ServiceActionIn; + cdb[1] = (byte)ScsiServiceActions.ReadCapacity16; + + if(pmi) + { + cdb[14] = 0x01; + byte[] temp = BitConverter.GetBytes(address); + cdb[2] = temp[7]; + cdb[3] = temp[6]; + cdb[4] = temp[5]; + cdb[5] = temp[4]; + cdb[6] = temp[3]; + cdb[7] = temp[2]; + cdb[8] = temp[1]; + cdb[9] = temp[0]; + } + + cdb[10] = (byte)((buffer.Length & 0xFF000000) >> 24); + cdb[11] = (byte)((buffer.Length & 0xFF0000) >> 16); + cdb[12] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[13] = (byte)(buffer.Length & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ CAPACITY(16) took {0} ms.", duration); + + return sense; + } + + /// Sends the SPC READ MEDIA SERIAL NUMBER command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ MEDIA SERIAL NUMBER response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + public bool ReadMediaSerialNumber(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[12]; + buffer = new byte[4]; + + cdb[0] = (byte)ScsiCommands.ReadSerialNumber; + cdb[1] = 0x01; + cdb[6] = (byte)((buffer.Length & 0xFF000000) >> 24); + cdb[7] = (byte)((buffer.Length & 0xFF0000) >> 16); + cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[9] = (byte)(buffer.Length & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + if(sense) + return true; + + uint strctLength = (uint)((buffer[0] << 24) + (buffer[1] << 16) + (buffer[2] << 8) + buffer[3] + 4); + buffer = new byte[strctLength]; + cdb[6] = (byte)((buffer.Length & 0xFF000000) >> 24); + cdb[7] = (byte)((buffer.Length & 0xFF0000) >> 16); + cdb[8] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[9] = (byte)(buffer.Length & 0xFF); + senseBuffer = new byte[64]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ MEDIA SERIAL NUMBER took {0} ms.", duration); + + return sense; + } + + /// Reads an attribute from the medium auxiliary memory + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Partition number. + /// First attribute identifier. + /// If set to true device can return cached data. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte partition, + ushort firstAttribute, bool cache, uint timeout, out double duration) => + ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, partition, firstAttribute, cache, timeout, + out duration); + + /// Reads an attribute from the medium auxiliary memory + /// Buffer. + /// Sense buffer. + /// What to do, . + /// First attribute identifier. + /// If set to true device can return cached data. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, + ushort firstAttribute, bool cache, uint timeout, out double duration) => + ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, 0, firstAttribute, cache, timeout, + out duration); + + /// Reads an attribute from the medium auxiliary memory + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Partition number. + /// First attribute identifier. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte partition, + ushort firstAttribute, uint timeout, out double duration) => + ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, partition, firstAttribute, false, timeout, + out duration); + + /// Reads an attribute from the medium auxiliary memory + /// Buffer. + /// Sense buffer. + /// What to do, . + /// First attribute identifier. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, + ushort firstAttribute, uint timeout, out double duration) => + ReadAttribute(out buffer, out senseBuffer, action, 0, 0, 0, 0, firstAttribute, false, timeout, + out duration); + + /// Reads an attribute from the medium auxiliary memory + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Volume number. + /// Partition number. + /// First attribute identifier. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte volume, + byte partition, ushort firstAttribute, uint timeout, out double duration) => + ReadAttribute(out buffer, out senseBuffer, action, 0, 0, volume, partition, firstAttribute, false, timeout, + out duration); + + /// Reads an attribute from the medium auxiliary memory + /// Buffer. + /// Sense buffer. + /// What to do, . + /// Volume number. + /// Partition number. + /// First attribute identifier. + /// If set to true device can return cached data. + /// Timeout. + /// Duration. + public bool ReadAttribute(out byte[] buffer, out byte[] senseBuffer, ScsiAttributeAction action, byte volume, + byte partition, ushort firstAttribute, bool cache, uint timeout, + out double duration) => + ReadAttribute(out buffer, out senseBuffer, action, 0, 0, volume, partition, firstAttribute, cache, timeout, + out duration); + + /// Sends the SPC MODE SELECT(6) command + /// true if the command failed and contains the sense buffer. + /// Buffer with the data to be sent to the device + /// Sense buffer. + /// Set to save pages between resets. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Set if page is formatted. + public bool ModeSelect(byte[] buffer, out byte[] senseBuffer, bool pageFormat, bool savePages, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + + // Prevent overflows + if(buffer.Length > 255) + { + if(PlatformId != PlatformID.Win32NT && + PlatformId != PlatformID.Win32S && + PlatformId != PlatformID.Win32Windows && + PlatformId != PlatformID.WinCE && + PlatformId != PlatformID.WindowsPhone && + PlatformId != PlatformID.Xbox) + LastError = 75; + else + LastError = 111; + + Error = true; + duration = 0; + + return true; + } + + byte[] cdb = new byte[6]; + + cdb[0] = (byte)ScsiCommands.ModeSelect; + + if(pageFormat) + cdb[1] += 0x10; + + if(savePages) + cdb[1] += 0x01; + + cdb[4] = (byte)buffer.Length; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "MODE SELECT(6) took {0} ms.", duration); + + return sense; + } + + /// Sends the SPC MODE SELECT(10) command + /// true if the command failed and contains the sense buffer. + /// Buffer with the data to be sent to the device + /// Set to save pages between resets. + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Set if page is formatted. + public bool ModeSelect10(byte[] buffer, out byte[] senseBuffer, bool pageFormat, bool savePages, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + + // Prevent overflows + if(buffer.Length > 65535) + { + if(PlatformId != PlatformID.Win32NT && + PlatformId != PlatformID.Win32S && + PlatformId != PlatformID.Win32Windows && + PlatformId != PlatformID.WinCE && + PlatformId != PlatformID.WindowsPhone && + PlatformId != PlatformID.Xbox) + LastError = 75; + else + LastError = 111; + + Error = true; + duration = 0; + + return true; + } + + byte[] cdb = new byte[10]; + + cdb[0] = (byte)ScsiCommands.ModeSelect10; + + if(pageFormat) + cdb[1] += 0x10; + + if(savePages) + cdb[1] += 0x01; + + cdb[7] = (byte)((buffer.Length & 0xFF00) << 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.Out, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "MODE SELECT(10) took {0} ms.", duration); + + return sense; + } + + /// Requests the device fixed sense + /// Sense buffer + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed. + public bool RequestSense(out byte[] buffer, uint timeout, out double duration) => + RequestSense(false, out buffer, timeout, out duration); + + /// Requests the device sense + /// Request a descriptor sense + /// Sense buffer + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// true if the command failed. + public bool RequestSense(bool descriptor, out byte[] buffer, uint timeout, out double duration) + { + byte[] cdb = new byte[6]; + buffer = new byte[252]; + + cdb[0] = (byte)ScsiCommands.RequestSense; + + if(descriptor) + cdb[1] = 0x01; + + cdb[2] = 0; + cdb[3] = 0; + cdb[4] = (byte)buffer.Length; + cdb[5] = 0; + + LastError = SendScsiCommand(cdb, ref buffer, out _, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "REQUEST SENSE took {0} ms.", duration); + + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/SSC.cs b/Aaru.Devices/Device/ScsiCommands/SSC.cs index 6099ba489..8ef85d29f 100644 --- a/Aaru.Devices/Device/ScsiCommands/SSC.cs +++ b/Aaru.Devices/Device/ScsiCommands/SSC.cs @@ -33,953 +33,952 @@ using System; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + /// Prepares the medium for reading + /// true, if load was successful, false otherwise. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool Load(out byte[] senseBuffer, uint timeout, out double duration) => + LoadUnload(out senseBuffer, false, true, false, false, false, timeout, out duration); + + /// Prepares the medium for ejection + /// true, if unload was successful, false otherwise. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool Unload(out byte[] senseBuffer, uint timeout, out double duration) => + LoadUnload(out senseBuffer, false, false, false, false, false, timeout, out duration); + + /// Prepares the medium for reading or ejection + /// true, if load/unload was successful, false otherwise. + /// Sense buffer. + /// If set to true, return from the command immediately. + /// If set to true load the medium for reading. + /// If set to true retense the tape. + /// If set to true move the medium to the EOT mark. + /// + /// If set to true and is also set to true, moves the medium to + /// the drive but does not prepare it for reading. + /// + /// Timeout. + /// Duration. + public bool LoadUnload(out byte[] senseBuffer, bool immediate, bool load, bool retense, bool endOfTape, + bool hold, uint timeout, out double duration) { - /// Prepares the medium for reading - /// true, if load was successful, false otherwise. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool Load(out byte[] senseBuffer, uint timeout, out double duration) => - LoadUnload(out senseBuffer, false, true, false, false, false, timeout, out duration); + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); - /// Prepares the medium for ejection - /// true, if unload was successful, false otherwise. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool Unload(out byte[] senseBuffer, uint timeout, out double duration) => - LoadUnload(out senseBuffer, false, false, false, false, false, timeout, out duration); + cdb[0] = (byte)ScsiCommands.LoadUnload; - /// Prepares the medium for reading or ejection - /// true, if load/unload was successful, false otherwise. - /// Sense buffer. - /// If set to true, return from the command immediately. - /// If set to true load the medium for reading. - /// If set to true retense the tape. - /// If set to true move the medium to the EOT mark. - /// - /// If set to true and is also set to true, moves the medium to - /// the drive but does not prepare it for reading. - /// - /// Timeout. - /// Duration. - public bool LoadUnload(out byte[] senseBuffer, bool immediate, bool load, bool retense, bool endOfTape, - bool hold, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); + if(immediate) + cdb[1] = 0x01; - cdb[0] = (byte)ScsiCommands.LoadUnload; + if(load) + cdb[4] += 0x01; - if(immediate) - cdb[1] = 0x01; + if(retense) + cdb[4] += 0x02; - if(load) - cdb[4] += 0x01; + if(endOfTape) + cdb[4] += 0x04; - if(retense) - cdb[4] += 0x02; + if(hold) + cdb[4] += 0x08; - if(endOfTape) - cdb[4] += 0x04; + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); - if(hold) - cdb[4] += 0x08; + Error = LastError != 0; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + AaruConsole.DebugWriteLine("SCSI Device", "LOAD UNLOAD (6) took {0} ms.", duration); - Error = LastError != 0; + return sense; + } - AaruConsole.DebugWriteLine("SCSI Device", "LOAD UNLOAD (6) took {0} ms.", duration); + /// Positions the medium to the specified block in the current partition + /// Sense buffer. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, uint lba, uint timeout, out double duration) => + Locate(out senseBuffer, false, false, false, 0, lba, timeout, out duration); - return sense; - } + /// Positions the medium to the specified block in the specified partition + /// Sense buffer. + /// Partition to position to. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, byte partition, uint lba, uint timeout, out double duration) => + Locate(out senseBuffer, false, false, false, partition, lba, timeout, out duration); - /// Positions the medium to the specified block in the current partition - /// Sense buffer. - /// Logical block address. - /// Timeout. - /// Duration. - public bool Locate(out byte[] senseBuffer, uint lba, uint timeout, out double duration) => - Locate(out senseBuffer, false, false, false, 0, lba, timeout, out duration); + /// Positions the medium to the specified block in the current partition + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, bool immediate, uint lba, uint timeout, out double duration) => + Locate(out senseBuffer, immediate, false, false, 0, lba, timeout, out duration); - /// Positions the medium to the specified block in the specified partition - /// Sense buffer. - /// Partition to position to. - /// Logical block address. - /// Timeout. - /// Duration. - public bool Locate(out byte[] senseBuffer, byte partition, uint lba, uint timeout, out double duration) => - Locate(out senseBuffer, false, false, false, partition, lba, timeout, out duration); + /// Positions the medium to the specified block in the specified partition + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Partition to position to. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, bool immediate, byte partition, uint lba, uint timeout, + out double duration) => Locate(out senseBuffer, immediate, false, false, partition, lba, + timeout, out duration); - /// Positions the medium to the specified block in the current partition - /// Sense buffer. - /// If set to true, return from the command immediately. - /// Logical block address. - /// Timeout. - /// Duration. - public bool Locate(out byte[] senseBuffer, bool immediate, uint lba, uint timeout, out double duration) => - Locate(out senseBuffer, immediate, false, false, 0, lba, timeout, out duration); + /// Positions the medium to the specified object identifier + /// Sense buffer. + /// If set to true, return from the command immediately. + /// If set to true object identifier is vendor specified. + /// If set to true change partition. + /// Partition to position to. + /// Object identifier. + /// Timeout. + /// Duration. + public bool Locate(out byte[] senseBuffer, bool immediate, bool blockType, bool changePartition, byte partition, + uint objectId, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + byte[] buffer = Array.Empty(); - /// Positions the medium to the specified block in the specified partition - /// Sense buffer. - /// If set to true, return from the command immediately. - /// Partition to position to. - /// Logical block address. - /// Timeout. - /// Duration. - public bool Locate(out byte[] senseBuffer, bool immediate, byte partition, uint lba, uint timeout, - out double duration) => Locate(out senseBuffer, immediate, false, false, partition, lba, + cdb[0] = (byte)ScsiCommands.Locate; + + if(immediate) + cdb[1] += 0x01; + + if(changePartition) + cdb[1] += 0x02; + + if(blockType) + cdb[1] += 0x04; + + cdb[3] = (byte)((objectId & 0xFF000000) >> 24); + cdb[4] = (byte)((objectId & 0xFF0000) >> 16); + cdb[5] = (byte)((objectId & 0xFF00) >> 8); + cdb[6] = (byte)(objectId & 0xFF); + cdb[8] = partition; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "LOCATE (10) took {0} ms.", duration); + + return sense; + } + + /// Positions the medium to the specified block in the current partition + /// Sense buffer. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, ulong lba, uint timeout, out double duration) => + Locate16(out senseBuffer, false, false, SscLogicalIdTypes.ObjectId, false, 0, lba, timeout, out duration); + + /// Positions the medium to the specified block in the specified partition + /// Sense buffer. + /// Partition to position to. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, byte partition, ulong lba, uint timeout, out double duration) => + Locate16(out senseBuffer, false, false, SscLogicalIdTypes.ObjectId, false, partition, lba, timeout, + out duration); + + /// Positions the medium to the specified block in the current partition + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, bool immediate, ulong lba, uint timeout, out double duration) => + Locate16(out senseBuffer, immediate, false, SscLogicalIdTypes.ObjectId, false, 0, lba, timeout, + out duration); + + /// Positions the medium to the specified block in the specified partition + /// Sense buffer. + /// If set to true, return from the command immediately. + /// Partition to position to. + /// Logical block address. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, bool immediate, byte partition, ulong lba, uint timeout, + out double duration) => Locate16(out senseBuffer, immediate, false, + SscLogicalIdTypes.ObjectId, false, partition, lba, timeout, out duration); - /// Positions the medium to the specified object identifier - /// Sense buffer. - /// If set to true, return from the command immediately. - /// If set to true object identifier is vendor specified. - /// If set to true change partition. - /// Partition to position to. - /// Object identifier. - /// Timeout. - /// Duration. - public bool Locate(out byte[] senseBuffer, bool immediate, bool blockType, bool changePartition, byte partition, - uint objectId, uint timeout, out double duration) + /// Positions the medium to the specified object identifier + /// Sense buffer. + /// If set to true, return from the command immediately. + /// If set to true change partition. + /// Destination type. + /// If set to true objectId is explicit. + /// Partition to position to. + /// Destination identifier. + /// Timeout. + /// Duration. + public bool Locate16(out byte[] senseBuffer, bool immediate, bool changePartition, SscLogicalIdTypes destType, + bool bam, byte partition, ulong identifier, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[16]; + byte[] buffer = Array.Empty(); + byte[] idBytes = BitConverter.GetBytes(identifier); + + cdb[0] = (byte)ScsiCommands.Locate16; + cdb[1] = (byte)((byte)destType << 3); + + if(immediate) + cdb[1] += 0x01; + + if(changePartition) + cdb[1] += 0x02; + + if(bam) + cdb[2] = 0x01; + + cdb[3] = partition; + + cdb[4] = idBytes[7]; + cdb[5] = idBytes[6]; + cdb[6] = idBytes[5]; + cdb[7] = idBytes[4]; + cdb[8] = idBytes[3]; + cdb[9] = idBytes[2]; + cdb[10] = idBytes[1]; + cdb[11] = idBytes[0]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "LOCATE (16) took {0} ms.", duration); + + return sense; + } + + /*/// + /// Reads the specified number of blocks from the medium + /// + /// Buffer. + /// Sense buffer. + /// How many blocks to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool Read6(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, uint timeout, out double duration) + { + return Read6(out buffer, out senseBuffer, false, true, blocks, blockSize, timeout, out duration); + }*/ + + /// Reads the specified number of bytes or of blocks from the medium + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// How many bytes to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool Read6(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, uint blockSize, + uint timeout, out double duration) => Read6(out buffer, out senseBuffer, sili, false, + transferLen, blockSize, timeout, out duration); + + /// Reads the specified number of bytes or of blocks from the medium + /// Buffer. + /// Sense buffer. + /// + /// If set to true suppress the incorrect-length indication. Cannot be set while + /// is set also. + /// + /// + /// If set to true indicates how many blocks to read of a + /// fixed size. + /// + /// Transfer length in blocks or bytes depending of status. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool Read6(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, uint transferLen, + uint blockSize, uint timeout, out double duration) + { + buffer = fixedLen ? new byte[blockSize * transferLen] : new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; + + cdb[0] = (byte)ScsiCommands.Read6; + + if(fixedLen) + cdb[1] += 0x01; + + if(sili) + cdb[1] += 0x02; + + cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[3] = (byte)((transferLen & 0xFF00) >> 8); + cdb[4] = (byte)(transferLen & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ (6) took {0} ms.", duration); + + return sense; + } + + /// Reads a number of fixed-length blocks starting at specified object + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, ulong objectId, uint blocks, + uint blockSize, uint timeout, out double duration) => + Read16(out buffer, out senseBuffer, sili, false, 0, objectId, blocks, blockSize, timeout, out duration); + + /// Reads a number of fixed-length blocks starting at specified block from the specified partition + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// Partition to read object from. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, byte partition, ulong objectId, + uint blocks, uint blockSize, uint timeout, out double duration) => + Read16(out buffer, out senseBuffer, sili, false, partition, objectId, blocks, blockSize, timeout, + out duration); + + /// Reads a number of fixed-length blocks starting at specified object + /// Buffer. + /// Sense buffer. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, ulong objectId, uint blocks, uint blockSize, + uint timeout, out double duration) => + Read16(out buffer, out senseBuffer, false, true, 0, objectId, blocks, blockSize, timeout, out duration); + + /// Reads a number of fixed-length blocks starting at specified block from the specified partition + /// Buffer. + /// Sense buffer. + /// Partition to read object from. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, byte partition, ulong objectId, uint blocks, + uint blockSize, uint timeout, out double duration) => + Read16(out buffer, out senseBuffer, false, true, partition, objectId, blocks, blockSize, timeout, + out duration); + + /// Reads a number of bytes or objects starting at specified object from the specified partition + /// Buffer. + /// Sense buffer. + /// + /// If set to true suppress the incorrect-length indication. Cannot be set while + /// is set also. + /// + /// + /// If set to true indicates how many blocks to read of a + /// fixed size. + /// + /// Partition to read object from. + /// Object identifier. + /// Transfer length in blocks or bytes depending of status. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, byte partition, + ulong objectId, uint transferLen, uint objectSize, uint timeout, out double duration) + { + buffer = fixedLen ? new byte[objectSize * transferLen] : new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; + byte[] idBytes = BitConverter.GetBytes(objectId); + + cdb[0] = (byte)ScsiCommands.Read16; + + if(fixedLen) + cdb[1] += 0x01; + + if(sili) + cdb[1] += 0x02; + + cdb[3] = partition; + cdb[4] = idBytes[7]; + cdb[5] = idBytes[6]; + cdb[6] = idBytes[5]; + cdb[7] = idBytes[4]; + cdb[8] = idBytes[3]; + cdb[9] = idBytes[2]; + cdb[10] = idBytes[1]; + cdb[11] = idBytes[0]; + cdb[12] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[13] = (byte)((transferLen & 0xFF00) >> 8); + cdb[14] = (byte)(transferLen & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ (16) took {0} ms.", duration); + + return sense; + } + + /// Requests the drive the maximum and minimum block size + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool ReadBlockLimits(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) + { + buffer = new byte[6]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; + + cdb[0] = (byte)ScsiCommands.ReadBlockLimits; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ BLOCK LIMITS took {0} ms.", duration); + + return sense; + } + + /// Reports current reading/writing elements position on the medium + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadPosition(out buffer, out senseBuffer, SscPositionForms.Short, timeout, out duration); + + /// Reports current reading/writing elements position on the medium using 32 bytes response + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool ReadPositionLong(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReadPosition(out buffer, out senseBuffer, SscPositionForms.Long, timeout, out duration); + + /// Reports current reading/writing elements position on the medium + /// Buffer. + /// Sense buffer. + /// Requests the position to be given in vendor-specified meaning. + /// Requests the response to be 32 bytes format. + /// Requests current logical position. + /// Timeout. + /// Duration. + public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, bool vendorType, bool longForm, + bool totalPosition, uint timeout, out double duration) + { + byte responseForm = 0; + + if(vendorType) + responseForm += 0x01; + + if(longForm) + responseForm += 0x02; + + if(totalPosition) + responseForm += 0x04; + + return ReadPosition(out buffer, out senseBuffer, (SscPositionForms)responseForm, timeout, out duration); + } + + /// Reports current reading/writing elements position on the medium + /// Buffer. + /// Sense buffer. + /// Response form. + /// Timeout. + /// Duration. + public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, SscPositionForms responseForm, uint timeout, + out double duration) + { + switch(responseForm) { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - byte[] buffer = Array.Empty(); + case SscPositionForms.Long: + case SscPositionForms.OldLong: + case SscPositionForms.OldLongTclpVendor: + case SscPositionForms.OldLongVendor: + case SscPositionForms.Extended: + buffer = new byte[32]; - cdb[0] = (byte)ScsiCommands.Locate; + break; + case SscPositionForms.OldTclp: + case SscPositionForms.OldTclpVendor: + case SscPositionForms.Short: + case SscPositionForms.VendorShort: + buffer = new byte[20]; - if(immediate) - cdb[1] += 0x01; + break; + default: + buffer = new byte[32]; // Invalid - if(changePartition) - cdb[1] += 0x02; - - if(blockType) - cdb[1] += 0x04; - - cdb[3] = (byte)((objectId & 0xFF000000) >> 24); - cdb[4] = (byte)((objectId & 0xFF0000) >> 16); - cdb[5] = (byte)((objectId & 0xFF00) >> 8); - cdb[6] = (byte)(objectId & 0xFF); - cdb[8] = partition; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "LOCATE (10) took {0} ms.", duration); - - return sense; + break; } - /// Positions the medium to the specified block in the current partition - /// Sense buffer. - /// Logical block address. - /// Timeout. - /// Duration. - public bool Locate16(out byte[] senseBuffer, ulong lba, uint timeout, out double duration) => - Locate16(out senseBuffer, false, false, SscLogicalIdTypes.ObjectId, false, 0, lba, timeout, out duration); + byte[] cdb = new byte[10]; + senseBuffer = new byte[64]; - /// Positions the medium to the specified block in the specified partition - /// Sense buffer. - /// Partition to position to. - /// Logical block address. - /// Timeout. - /// Duration. - public bool Locate16(out byte[] senseBuffer, byte partition, ulong lba, uint timeout, out double duration) => - Locate16(out senseBuffer, false, false, SscLogicalIdTypes.ObjectId, false, partition, lba, timeout, - out duration); + cdb[0] = (byte)ScsiCommands.ReadPosition; + cdb[1] = (byte)((byte)responseForm & 0x1F); - /// Positions the medium to the specified block in the current partition - /// Sense buffer. - /// If set to true, return from the command immediately. - /// Logical block address. - /// Timeout. - /// Duration. - public bool Locate16(out byte[] senseBuffer, bool immediate, ulong lba, uint timeout, out double duration) => - Locate16(out senseBuffer, immediate, false, SscLogicalIdTypes.ObjectId, false, 0, lba, timeout, - out duration); - - /// Positions the medium to the specified block in the specified partition - /// Sense buffer. - /// If set to true, return from the command immediately. - /// Partition to position to. - /// Logical block address. - /// Timeout. - /// Duration. - public bool Locate16(out byte[] senseBuffer, bool immediate, byte partition, ulong lba, uint timeout, - out double duration) => Locate16(out senseBuffer, immediate, false, - SscLogicalIdTypes.ObjectId, false, partition, lba, - timeout, out duration); - - /// Positions the medium to the specified object identifier - /// Sense buffer. - /// If set to true, return from the command immediately. - /// If set to true change partition. - /// Destination type. - /// If set to true objectId is explicit. - /// Partition to position to. - /// Destination identifier. - /// Timeout. - /// Duration. - public bool Locate16(out byte[] senseBuffer, bool immediate, bool changePartition, SscLogicalIdTypes destType, - bool bam, byte partition, ulong identifier, uint timeout, out double duration) + if(responseForm == SscPositionForms.Extended) { - senseBuffer = new byte[64]; - byte[] cdb = new byte[16]; - byte[] buffer = Array.Empty(); - byte[] idBytes = BitConverter.GetBytes(identifier); - - cdb[0] = (byte)ScsiCommands.Locate16; - cdb[1] = (byte)((byte)destType << 3); - - if(immediate) - cdb[1] += 0x01; - - if(changePartition) - cdb[1] += 0x02; - - if(bam) - cdb[2] = 0x01; - - cdb[3] = partition; - - cdb[4] = idBytes[7]; - cdb[5] = idBytes[6]; - cdb[6] = idBytes[5]; - cdb[7] = idBytes[4]; - cdb[8] = idBytes[3]; - cdb[9] = idBytes[2]; - cdb[10] = idBytes[1]; - cdb[11] = idBytes[0]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "LOCATE (16) took {0} ms.", duration); - - return sense; - } - - /*/// - /// Reads the specified number of blocks from the medium - /// - /// Buffer. - /// Sense buffer. - /// How many blocks to read. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool Read6(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, uint timeout, out double duration) - { - return Read6(out buffer, out senseBuffer, false, true, blocks, blockSize, timeout, out duration); - }*/ - - /// Reads the specified number of bytes or of blocks from the medium - /// Buffer. - /// Sense buffer. - /// If set to true suppress the incorrect-length indication. - /// How many bytes to read. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool Read6(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, uint blockSize, - uint timeout, out double duration) => Read6(out buffer, out senseBuffer, sili, false, - transferLen, blockSize, timeout, out duration); - - /// Reads the specified number of bytes or of blocks from the medium - /// Buffer. - /// Sense buffer. - /// - /// If set to true suppress the incorrect-length indication. Cannot be set while - /// is set also. - /// - /// - /// If set to true indicates how many blocks to read of a - /// fixed size. - /// - /// Transfer length in blocks or bytes depending of status. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool Read6(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, uint transferLen, - uint blockSize, uint timeout, out double duration) - { - buffer = fixedLen ? new byte[blockSize * transferLen] : new byte[transferLen]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; - - cdb[0] = (byte)ScsiCommands.Read6; - - if(fixedLen) - cdb[1] += 0x01; - - if(sili) - cdb[1] += 0x02; - - cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); - cdb[3] = (byte)((transferLen & 0xFF00) >> 8); - cdb[4] = (byte)(transferLen & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ (6) took {0} ms.", duration); - - return sense; - } - - /// Reads a number of fixed-length blocks starting at specified object - /// Buffer. - /// Sense buffer. - /// If set to true suppress the incorrect-length indication. - /// Object identifier. - /// How many blocks to read. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, ulong objectId, uint blocks, - uint blockSize, uint timeout, out double duration) => - Read16(out buffer, out senseBuffer, sili, false, 0, objectId, blocks, blockSize, timeout, out duration); - - /// Reads a number of fixed-length blocks starting at specified block from the specified partition - /// Buffer. - /// Sense buffer. - /// If set to true suppress the incorrect-length indication. - /// Partition to read object from. - /// Object identifier. - /// How many blocks to read. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, byte partition, ulong objectId, - uint blocks, uint blockSize, uint timeout, out double duration) => - Read16(out buffer, out senseBuffer, sili, false, partition, objectId, blocks, blockSize, timeout, - out duration); - - /// Reads a number of fixed-length blocks starting at specified object - /// Buffer. - /// Sense buffer. - /// Object identifier. - /// How many blocks to read. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool Read16(out byte[] buffer, out byte[] senseBuffer, ulong objectId, uint blocks, uint blockSize, - uint timeout, out double duration) => - Read16(out buffer, out senseBuffer, false, true, 0, objectId, blocks, blockSize, timeout, out duration); - - /// Reads a number of fixed-length blocks starting at specified block from the specified partition - /// Buffer. - /// Sense buffer. - /// Partition to read object from. - /// Object identifier. - /// How many blocks to read. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool Read16(out byte[] buffer, out byte[] senseBuffer, byte partition, ulong objectId, uint blocks, - uint blockSize, uint timeout, out double duration) => - Read16(out buffer, out senseBuffer, false, true, partition, objectId, blocks, blockSize, timeout, - out duration); - - /// Reads a number of bytes or objects starting at specified object from the specified partition - /// Buffer. - /// Sense buffer. - /// - /// If set to true suppress the incorrect-length indication. Cannot be set while - /// is set also. - /// - /// - /// If set to true indicates how many blocks to read of a - /// fixed size. - /// - /// Partition to read object from. - /// Object identifier. - /// Transfer length in blocks or bytes depending of status. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool Read16(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, byte partition, - ulong objectId, uint transferLen, uint objectSize, uint timeout, out double duration) - { - buffer = fixedLen ? new byte[objectSize * transferLen] : new byte[transferLen]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; - byte[] idBytes = BitConverter.GetBytes(objectId); - - cdb[0] = (byte)ScsiCommands.Read16; - - if(fixedLen) - cdb[1] += 0x01; - - if(sili) - cdb[1] += 0x02; - - cdb[3] = partition; - cdb[4] = idBytes[7]; - cdb[5] = idBytes[6]; - cdb[6] = idBytes[5]; - cdb[7] = idBytes[4]; - cdb[8] = idBytes[3]; - cdb[9] = idBytes[2]; - cdb[10] = idBytes[1]; - cdb[11] = idBytes[0]; - cdb[12] = (byte)((transferLen & 0xFF0000) >> 16); - cdb[13] = (byte)((transferLen & 0xFF00) >> 8); - cdb[14] = (byte)(transferLen & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ (16) took {0} ms.", duration); - - return sense; - } - - /// Requests the drive the maximum and minimum block size - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool ReadBlockLimits(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) - { - buffer = new byte[6]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; - - cdb[0] = (byte)ScsiCommands.ReadBlockLimits; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ BLOCK LIMITS took {0} ms.", duration); - - return sense; - } - - /// Reports current reading/writing elements position on the medium - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadPosition(out buffer, out senseBuffer, SscPositionForms.Short, timeout, out duration); - - /// Reports current reading/writing elements position on the medium using 32 bytes response - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool ReadPositionLong(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReadPosition(out buffer, out senseBuffer, SscPositionForms.Long, timeout, out duration); - - /// Reports current reading/writing elements position on the medium - /// Buffer. - /// Sense buffer. - /// Requests the position to be given in vendor-specified meaning. - /// Requests the response to be 32 bytes format. - /// Requests current logical position. - /// Timeout. - /// Duration. - public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, bool vendorType, bool longForm, - bool totalPosition, uint timeout, out double duration) - { - byte responseForm = 0; - - if(vendorType) - responseForm += 0x01; - - if(longForm) - responseForm += 0x02; - - if(totalPosition) - responseForm += 0x04; - - return ReadPosition(out buffer, out senseBuffer, (SscPositionForms)responseForm, timeout, out duration); - } - - /// Reports current reading/writing elements position on the medium - /// Buffer. - /// Sense buffer. - /// Response form. - /// Timeout. - /// Duration. - public bool ReadPosition(out byte[] buffer, out byte[] senseBuffer, SscPositionForms responseForm, uint timeout, - out double duration) - { - switch(responseForm) - { - case SscPositionForms.Long: - case SscPositionForms.OldLong: - case SscPositionForms.OldLongTclpVendor: - case SscPositionForms.OldLongVendor: - case SscPositionForms.Extended: - buffer = new byte[32]; - - break; - case SscPositionForms.OldTclp: - case SscPositionForms.OldTclpVendor: - case SscPositionForms.Short: - case SscPositionForms.VendorShort: - buffer = new byte[20]; - - break; - default: - buffer = new byte[32]; // Invalid - - break; - } - - byte[] cdb = new byte[10]; - senseBuffer = new byte[64]; - - cdb[0] = (byte)ScsiCommands.ReadPosition; - cdb[1] = (byte)((byte)responseForm & 0x1F); - - if(responseForm == SscPositionForms.Extended) - { - cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(buffer.Length & 0xFF); - } - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ POSITION took {0} ms.", duration); - - return sense; - } - - /// Reads the specified number of blocks from the medium, backwards - /// Buffer. - /// Sense buffer. - /// How many blocks to read. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, uint timeout, - out double duration) => - ReadReverse6(out buffer, out senseBuffer, false, false, true, blocks, blockSize, timeout, out duration); - - /// Reads the specified number of bytes or of blocks from the medium, backwards - /// Buffer. - /// Sense buffer. - /// If set to true suppress the incorrect-length indication. - /// How many bytes to read. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, uint blockSize, - uint timeout, out double duration) => - ReadReverse6(out buffer, out senseBuffer, false, sili, false, transferLen, blockSize, timeout, - out duration); - - /// Reads the specified number of bytes or of blocks from the medium, backwards - /// Buffer. - /// Sense buffer. - /// If set to true drive should un-reverse the blocks and bytes - /// - /// If set to true suppress the incorrect-length indication. Cannot be set while - /// is set also. - /// - /// - /// If set to true indicates how many blocks to read of a - /// fixed size. - /// - /// Transfer length in blocks or bytes depending of status. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, bool byteOrder, bool sili, bool fixedLen, - uint transferLen, uint blockSize, uint timeout, out double duration) - { - buffer = fixedLen ? new byte[blockSize * transferLen] : new byte[transferLen]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; - - cdb[0] = (byte)ScsiCommands.ReadReverse; - - if(fixedLen) - cdb[1] += 0x01; - - if(sili) - cdb[1] += 0x02; - - if(byteOrder) - cdb[1] += 0x04; - - cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); - cdb[3] = (byte)((transferLen & 0xFF00) >> 8); - cdb[4] = (byte)(transferLen & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ REVERSE (6) took {0} ms.", duration); - - return sense; - } - - /// Reads a number of fixed-length blocks starting at specified object, backwards - /// Buffer. - /// Sense buffer. - /// If set to true suppress the incorrect-length indication. - /// Object identifier. - /// How many blocks to read. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool sili, ulong objectId, uint blocks, - uint blockSize, uint timeout, out double duration) => - ReadReverse16(out buffer, out senseBuffer, false, sili, false, 0, objectId, blocks, blockSize, timeout, - out duration); - - /// Reads a number of fixed-length blocks starting at specified block from the specified partition, backwards - /// Buffer. - /// Sense buffer. - /// If set to true suppress the incorrect-length indication. - /// Partition to read object from. - /// Object identifier. - /// How many blocks to read. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool sili, byte partition, ulong objectId, - uint blocks, uint blockSize, uint timeout, out double duration) => - ReadReverse16(out buffer, out senseBuffer, false, sili, false, partition, objectId, blocks, blockSize, - timeout, out duration); - - /// Reads a number of fixed-length blocks starting at specified object, backwards - /// Buffer. - /// Sense buffer. - /// Object identifier. - /// How many blocks to read. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, ulong objectId, uint blocks, - uint blockSize, uint timeout, out double duration) => - ReadReverse16(out buffer, out senseBuffer, false, false, true, 0, objectId, blocks, blockSize, timeout, - out duration); - - /// Reads a number of fixed-length blocks starting at specified block from the specified partition, backwards - /// Buffer. - /// Sense buffer. - /// Partition to read object from. - /// Object identifier. - /// How many blocks to read. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, byte partition, ulong objectId, - uint blocks, uint blockSize, uint timeout, out double duration) => - ReadReverse16(out buffer, out senseBuffer, false, false, true, partition, objectId, blocks, blockSize, - timeout, out duration); - - /// Reads a number of bytes or objects starting at specified object from the specified partition, backwards - /// Buffer. - /// Sense buffer. - /// If set to true drive should un-reverse the blocks and bytes - /// - /// If set to true suppress the incorrect-length indication. Cannot be set while - /// is set also. - /// - /// - /// If set to true indicates how many blocks to read of a - /// fixed size. - /// - /// Partition to read object from. - /// Object identifier. - /// Transfer length in blocks or bytes depending of status. - /// Object size in bytes. - /// Timeout. - /// Duration. - public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool byteOrder, bool sili, bool fixedLen, - byte partition, ulong objectId, uint transferLen, uint objectSize, uint timeout, - out double duration) - { - buffer = fixedLen ? new byte[objectSize * transferLen] : new byte[transferLen]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; - byte[] idBytes = BitConverter.GetBytes(objectId); - - cdb[0] = (byte)ScsiCommands.Read16; - - if(fixedLen) - cdb[1] += 0x01; - - if(sili) - cdb[1] += 0x02; - - if(byteOrder) - cdb[1] += 0x04; - - cdb[3] = partition; - cdb[4] = idBytes[7]; - cdb[5] = idBytes[6]; - cdb[6] = idBytes[5]; - cdb[7] = idBytes[4]; - cdb[8] = idBytes[3]; - cdb[9] = idBytes[2]; - cdb[10] = idBytes[1]; - cdb[11] = idBytes[0]; - cdb[12] = (byte)((transferLen & 0xFF0000) >> 16); - cdb[13] = (byte)((transferLen & 0xFF00) >> 8); - cdb[14] = (byte)(transferLen & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "READ REVERSE (16) took {0} ms.", duration); - - return sense; - } - - /// Reads the specified number of blocks from the device's buffer - /// Buffer. - /// Sense buffer. - /// How many blocks to read. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, - uint timeout, out double duration) => - RecoverBufferedData(out buffer, out senseBuffer, false, true, blocks, blockSize, timeout, out duration); - - /// Reads the specified number of bytes or of blocks from the device's buffer - /// Buffer. - /// Sense buffer. - /// If set to true suppress the incorrect-length indication. - /// How many bytes to read. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, - uint blockSize, uint timeout, out double duration) => - RecoverBufferedData(out buffer, out senseBuffer, sili, false, transferLen, blockSize, timeout, - out duration); - - /// Reads the specified number of bytes or of blocks from the device's buffer - /// Buffer. - /// Sense buffer. - /// - /// If set to true suppress the incorrect-length indication. Cannot be set while - /// is set also. - /// - /// - /// If set to true indicates how many blocks to read of a - /// fixed size. - /// - /// Transfer length in blocks or bytes depending of status. - /// Block size in bytes. - /// Timeout. - /// Duration. - public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, - uint transferLen, uint blockSize, uint timeout, out double duration) - { - buffer = fixedLen ? new byte[blockSize * transferLen] : new byte[transferLen]; - byte[] cdb = new byte[6]; - senseBuffer = new byte[64]; - - cdb[0] = (byte)ScsiCommands.RecoverBufferedData; - - if(fixedLen) - cdb[1] += 0x01; - - if(sili) - cdb[1] += 0x02; - - cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); - cdb[3] = (byte)((transferLen & 0xFF00) >> 8); - cdb[4] = (byte)(transferLen & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "RECOVER BUFFERED DATA took {0} ms.", duration); - - return sense; - } - - /// Requests the device to return descriptors for supported densities or medium types - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool - ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => - ReportDensitySupport(out buffer, out senseBuffer, false, false, timeout, out duration); - - /// Requests the device to return descriptors for supported densities or medium types - /// Buffer. - /// Sense buffer. - /// If set to true descriptors should apply to currently inserted media. - /// Timeout. - /// Duration. - public bool ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, bool currentMedia, uint timeout, - out double duration) => - ReportDensitySupport(out buffer, out senseBuffer, false, currentMedia, timeout, out duration); - - /// Requests the device to return descriptors for supported densities or medium types - /// Buffer. - /// Sense buffer. - /// If set to true descriptors should be about medium types. - /// If set to true descriptors should apply to currently inserted media. - /// Timeout. - /// Duration. - public bool ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, bool mediumType, bool currentMedia, - uint timeout, out double duration) - { - buffer = new byte[256]; - byte[] cdb = new byte[10]; - senseBuffer = new byte[64]; - - cdb[0] = (byte)ScsiCommands.ReportDensitySupport; - - if(currentMedia) - cdb[1] += 0x01; - - if(mediumType) - cdb[1] += 0x02; - cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); cdb[8] = (byte)(buffer.Length & 0xFF); - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out bool sense); - - Error = LastError != 0; - - if(sense) - return true; - - ushort availableLength = (ushort)((buffer[0] << 8) + buffer[1] + 2); - buffer = new byte[availableLength]; - cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); - cdb[8] = (byte)(buffer.Length & 0xFF); - senseBuffer = new byte[64]; - - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "REPORT DENSITY SUPPORT took {0} ms.", duration); - - return sense; } - /// Positions the reading/writing element to the beginning of current partition - /// Sense buffer. - /// Timeout. - /// Duration. - public bool Rewind(out byte[] senseBuffer, uint timeout, out double duration) => - Rewind(out senseBuffer, false, timeout, out duration); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - /// Positions the reading/writing element to the beginning of current partition - /// Sense buffer. - /// If set to true return from the command immediately. - /// Timeout. - /// Duration. - public bool Rewind(out byte[] senseBuffer, bool immediate, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); + Error = LastError != 0; - cdb[0] = (byte)ScsiCommands.Rewind; + AaruConsole.DebugWriteLine("SCSI Device", "READ POSITION took {0} ms.", duration); - if(immediate) - cdb[1] += 0x01; + return sense; + } - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + /// Reads the specified number of blocks from the medium, backwards + /// Buffer. + /// Sense buffer. + /// How many blocks to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, uint timeout, + out double duration) => + ReadReverse6(out buffer, out senseBuffer, false, false, true, blocks, blockSize, timeout, out duration); - Error = LastError != 0; + /// Reads the specified number of bytes or of blocks from the medium, backwards + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// How many bytes to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, uint blockSize, + uint timeout, out double duration) => + ReadReverse6(out buffer, out senseBuffer, false, sili, false, transferLen, blockSize, timeout, + out duration); - AaruConsole.DebugWriteLine("SCSI Device", "REWIND took {0} ms.", duration); + /// Reads the specified number of bytes or of blocks from the medium, backwards + /// Buffer. + /// Sense buffer. + /// If set to true drive should un-reverse the blocks and bytes + /// + /// If set to true suppress the incorrect-length indication. Cannot be set while + /// is set also. + /// + /// + /// If set to true indicates how many blocks to read of a + /// fixed size. + /// + /// Transfer length in blocks or bytes depending of status. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse6(out byte[] buffer, out byte[] senseBuffer, bool byteOrder, bool sili, bool fixedLen, + uint transferLen, uint blockSize, uint timeout, out double duration) + { + buffer = fixedLen ? new byte[blockSize * transferLen] : new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; - return sense; - } + cdb[0] = (byte)ScsiCommands.ReadReverse; - /// Selects the specified track - /// true, if select was tracked, false otherwise. - /// Sense buffer. - /// Track. - /// Timeout. - /// Duration. - public bool TrackSelect(out byte[] senseBuffer, byte track, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); + if(fixedLen) + cdb[1] += 0x01; - cdb[0] = (byte)ScsiCommands.TrackSelect; - cdb[5] = track; + if(sili) + cdb[1] += 0x02; - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + if(byteOrder) + cdb[1] += 0x04; - Error = LastError != 0; + cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[3] = (byte)((transferLen & 0xFF00) >> 8); + cdb[4] = (byte)(transferLen & 0xFF); - AaruConsole.DebugWriteLine("SCSI Device", "TRACK SELECT took {0} ms.", duration); + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); - return sense; - } + Error = LastError != 0; - /// Writes a space mark in the media - /// Sense buffer. - /// Space type code. - /// How many marks to write - /// Timeout. - /// Duration. - /// true, if select was tracked, false otherwise. - public bool Space(out byte[] senseBuffer, SscSpaceCodes code, int count, uint timeout, out double duration) - { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - byte[] buffer = Array.Empty(); - byte[] countB = BitConverter.GetBytes(count); + AaruConsole.DebugWriteLine("SCSI Device", "READ REVERSE (6) took {0} ms.", duration); - cdb[0] = (byte)ScsiCommands.Space; - cdb[1] = (byte)((byte)code & 0x0F); - cdb[2] = countB[2]; - cdb[3] = countB[1]; - cdb[4] = countB[0]; + return sense; + } - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out bool sense); + /// Reads a number of fixed-length blocks starting at specified object, backwards + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool sili, ulong objectId, uint blocks, + uint blockSize, uint timeout, out double duration) => + ReadReverse16(out buffer, out senseBuffer, false, sili, false, 0, objectId, blocks, blockSize, timeout, + out duration); - Error = LastError != 0; + /// Reads a number of fixed-length blocks starting at specified block from the specified partition, backwards + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// Partition to read object from. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool sili, byte partition, ulong objectId, + uint blocks, uint blockSize, uint timeout, out double duration) => + ReadReverse16(out buffer, out senseBuffer, false, sili, false, partition, objectId, blocks, blockSize, + timeout, out duration); - AaruConsole.DebugWriteLine("SCSI Device", "SPACE took {0} ms.", duration); + /// Reads a number of fixed-length blocks starting at specified object, backwards + /// Buffer. + /// Sense buffer. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, ulong objectId, uint blocks, + uint blockSize, uint timeout, out double duration) => + ReadReverse16(out buffer, out senseBuffer, false, false, true, 0, objectId, blocks, blockSize, timeout, + out duration); - return sense; - } + /// Reads a number of fixed-length blocks starting at specified block from the specified partition, backwards + /// Buffer. + /// Sense buffer. + /// Partition to read object from. + /// Object identifier. + /// How many blocks to read. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, byte partition, ulong objectId, + uint blocks, uint blockSize, uint timeout, out double duration) => + ReadReverse16(out buffer, out senseBuffer, false, false, true, partition, objectId, blocks, blockSize, + timeout, out duration); + + /// Reads a number of bytes or objects starting at specified object from the specified partition, backwards + /// Buffer. + /// Sense buffer. + /// If set to true drive should un-reverse the blocks and bytes + /// + /// If set to true suppress the incorrect-length indication. Cannot be set while + /// is set also. + /// + /// + /// If set to true indicates how many blocks to read of a + /// fixed size. + /// + /// Partition to read object from. + /// Object identifier. + /// Transfer length in blocks or bytes depending of status. + /// Object size in bytes. + /// Timeout. + /// Duration. + public bool ReadReverse16(out byte[] buffer, out byte[] senseBuffer, bool byteOrder, bool sili, bool fixedLen, + byte partition, ulong objectId, uint transferLen, uint objectSize, uint timeout, + out double duration) + { + buffer = fixedLen ? new byte[objectSize * transferLen] : new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; + byte[] idBytes = BitConverter.GetBytes(objectId); + + cdb[0] = (byte)ScsiCommands.Read16; + + if(fixedLen) + cdb[1] += 0x01; + + if(sili) + cdb[1] += 0x02; + + if(byteOrder) + cdb[1] += 0x04; + + cdb[3] = partition; + cdb[4] = idBytes[7]; + cdb[5] = idBytes[6]; + cdb[6] = idBytes[5]; + cdb[7] = idBytes[4]; + cdb[8] = idBytes[3]; + cdb[9] = idBytes[2]; + cdb[10] = idBytes[1]; + cdb[11] = idBytes[0]; + cdb[12] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[13] = (byte)((transferLen & 0xFF00) >> 8); + cdb[14] = (byte)(transferLen & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "READ REVERSE (16) took {0} ms.", duration); + + return sense; + } + + /// Reads the specified number of blocks from the device's buffer + /// Buffer. + /// Sense buffer. + /// How many blocks to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, uint blocks, uint blockSize, + uint timeout, out double duration) => + RecoverBufferedData(out buffer, out senseBuffer, false, true, blocks, blockSize, timeout, out duration); + + /// Reads the specified number of bytes or of blocks from the device's buffer + /// Buffer. + /// Sense buffer. + /// If set to true suppress the incorrect-length indication. + /// How many bytes to read. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, bool sili, uint transferLen, + uint blockSize, uint timeout, out double duration) => + RecoverBufferedData(out buffer, out senseBuffer, sili, false, transferLen, blockSize, timeout, + out duration); + + /// Reads the specified number of bytes or of blocks from the device's buffer + /// Buffer. + /// Sense buffer. + /// + /// If set to true suppress the incorrect-length indication. Cannot be set while + /// is set also. + /// + /// + /// If set to true indicates how many blocks to read of a + /// fixed size. + /// + /// Transfer length in blocks or bytes depending of status. + /// Block size in bytes. + /// Timeout. + /// Duration. + public bool RecoverBufferedData(out byte[] buffer, out byte[] senseBuffer, bool sili, bool fixedLen, + uint transferLen, uint blockSize, uint timeout, out double duration) + { + buffer = fixedLen ? new byte[blockSize * transferLen] : new byte[transferLen]; + byte[] cdb = new byte[6]; + senseBuffer = new byte[64]; + + cdb[0] = (byte)ScsiCommands.RecoverBufferedData; + + if(fixedLen) + cdb[1] += 0x01; + + if(sili) + cdb[1] += 0x02; + + cdb[2] = (byte)((transferLen & 0xFF0000) >> 16); + cdb[3] = (byte)((transferLen & 0xFF00) >> 8); + cdb[4] = (byte)(transferLen & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "RECOVER BUFFERED DATA took {0} ms.", duration); + + return sense; + } + + /// Requests the device to return descriptors for supported densities or medium types + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool + ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, uint timeout, out double duration) => + ReportDensitySupport(out buffer, out senseBuffer, false, false, timeout, out duration); + + /// Requests the device to return descriptors for supported densities or medium types + /// Buffer. + /// Sense buffer. + /// If set to true descriptors should apply to currently inserted media. + /// Timeout. + /// Duration. + public bool ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, bool currentMedia, uint timeout, + out double duration) => + ReportDensitySupport(out buffer, out senseBuffer, false, currentMedia, timeout, out duration); + + /// Requests the device to return descriptors for supported densities or medium types + /// Buffer. + /// Sense buffer. + /// If set to true descriptors should be about medium types. + /// If set to true descriptors should apply to currently inserted media. + /// Timeout. + /// Duration. + public bool ReportDensitySupport(out byte[] buffer, out byte[] senseBuffer, bool mediumType, bool currentMedia, + uint timeout, out double duration) + { + buffer = new byte[256]; + byte[] cdb = new byte[10]; + senseBuffer = new byte[64]; + + cdb[0] = (byte)ScsiCommands.ReportDensitySupport; + + if(currentMedia) + cdb[1] += 0x01; + + if(mediumType) + cdb[1] += 0x02; + + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out bool sense); + + Error = LastError != 0; + + if(sense) + return true; + + ushort availableLength = (ushort)((buffer[0] << 8) + buffer[1] + 2); + buffer = new byte[availableLength]; + cdb[7] = (byte)((buffer.Length & 0xFF00) >> 8); + cdb[8] = (byte)(buffer.Length & 0xFF); + senseBuffer = new byte[64]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "REPORT DENSITY SUPPORT took {0} ms.", duration); + + return sense; + } + + /// Positions the reading/writing element to the beginning of current partition + /// Sense buffer. + /// Timeout. + /// Duration. + public bool Rewind(out byte[] senseBuffer, uint timeout, out double duration) => + Rewind(out senseBuffer, false, timeout, out duration); + + /// Positions the reading/writing element to the beginning of current partition + /// Sense buffer. + /// If set to true return from the command immediately. + /// Timeout. + /// Duration. + public bool Rewind(out byte[] senseBuffer, bool immediate, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.Rewind; + + if(immediate) + cdb[1] += 0x01; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "REWIND took {0} ms.", duration); + + return sense; + } + + /// Selects the specified track + /// true, if select was tracked, false otherwise. + /// Sense buffer. + /// Track. + /// Timeout. + /// Duration. + public bool TrackSelect(out byte[] senseBuffer, byte track, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + + cdb[0] = (byte)ScsiCommands.TrackSelect; + cdb[5] = track; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "TRACK SELECT took {0} ms.", duration); + + return sense; + } + + /// Writes a space mark in the media + /// Sense buffer. + /// Space type code. + /// How many marks to write + /// Timeout. + /// Duration. + /// true, if select was tracked, false otherwise. + public bool Space(out byte[] senseBuffer, SscSpaceCodes code, int count, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + byte[] buffer = Array.Empty(); + byte[] countB = BitConverter.GetBytes(count); + + cdb[0] = (byte)ScsiCommands.Space; + cdb[1] = (byte)((byte)code & 0x0F); + cdb[2] = countB[2]; + cdb[3] = countB[1]; + cdb[4] = countB[0]; + + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out bool sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "SPACE took {0} ms.", duration); + + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/ScsiCommands/SyQuest.cs b/Aaru.Devices/Device/ScsiCommands/SyQuest.cs index 9f0313fd5..dfb4763ca 100644 --- a/Aaru.Devices/Device/ScsiCommands/SyQuest.cs +++ b/Aaru.Devices/Device/ScsiCommands/SyQuest.cs @@ -33,166 +33,165 @@ using System; using Aaru.Console; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device - { - /// Sends the SyQuest READ (6) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting block. - /// Block size in bytes. - public bool SyQuestRead6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint timeout, + /// Sends the SyQuest READ (6) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting block. + /// Block size in bytes. + public bool SyQuestRead6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint timeout, + out double duration) => + SyQuestRead6(out buffer, out senseBuffer, lba, blockSize, 1, false, false, timeout, out duration); + + /// Sends the SyQuest READ LONG (6) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting block. + /// Block size in bytes. + public bool SyQuestReadLong6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint timeout, out double duration) => - SyQuestRead6(out buffer, out senseBuffer, lba, blockSize, 1, false, false, timeout, out duration); + SyQuestRead6(out buffer, out senseBuffer, lba, blockSize, 1, false, true, timeout, out duration); - /// Sends the SyQuest READ LONG (6) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting block. - /// Block size in bytes. - public bool SyQuestReadLong6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint timeout, - out double duration) => - SyQuestRead6(out buffer, out senseBuffer, lba, blockSize, 1, false, true, timeout, out duration); + /// Sends the SyQuest READ (6) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting block. + /// If set to true, block will not be transfer and will reside in the drive's buffer. + /// If set to true drive will return ECC bytes and disable error detection. + /// Block size in bytes. + /// How many blocks to read. + public bool SyQuestRead6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, + byte transferLength, bool inhibitDma, bool readLong, uint timeout, out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[6]; + bool sense; - /// Sends the SyQuest READ (6) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting block. - /// If set to true, block will not be transfer and will reside in the drive's buffer. - /// If set to true drive will return ECC bytes and disable error detection. - /// Block size in bytes. - /// How many blocks to read. - public bool SyQuestRead6(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, - byte transferLength, bool inhibitDma, bool readLong, uint timeout, out double duration) + cdb[0] = (byte)ScsiCommands.Read6; + cdb[1] = (byte)((lba & 0x1F0000) >> 16); + cdb[2] = (byte)((lba & 0xFF00) >> 8); + cdb[3] = (byte)(lba & 0xFF); + cdb[4] = transferLength; + + if(inhibitDma) + cdb[5] += 0x80; + + if(readLong) + cdb[5] += 0x40; + + if(!inhibitDma && + !readLong) + buffer = transferLength == 0 ? new byte[256 * blockSize] : new byte[transferLength * blockSize]; + else if(readLong) { - senseBuffer = new byte[64]; - byte[] cdb = new byte[6]; - bool sense; - - cdb[0] = (byte)ScsiCommands.Read6; - cdb[1] = (byte)((lba & 0x1F0000) >> 16); - cdb[2] = (byte)((lba & 0xFF00) >> 8); - cdb[3] = (byte)(lba & 0xFF); - cdb[4] = transferLength; - - if(inhibitDma) - cdb[5] += 0x80; - - if(readLong) - cdb[5] += 0x40; - - if(!inhibitDma && - !readLong) - buffer = transferLength == 0 ? new byte[256 * blockSize] : new byte[transferLength * blockSize]; - else if(readLong) - { - buffer = new byte[blockSize]; - cdb[4] = 1; - } - else - buffer = Array.Empty(); - - if(!inhibitDma) - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); - else - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "SYQUEST READ (6) took {0} ms.", duration); - - return sense; + buffer = new byte[blockSize]; + cdb[4] = 1; } + else + buffer = Array.Empty(); - /// Requests the usage, seek and error counters, and resets them - /// Buffer. - /// Sense buffer. - /// Timeout. - /// Duration. - public bool SyQuestReadUsageCounter(out byte[] buffer, out byte[] senseBuffer, uint timeout, - out double duration) => - AdaptecReadUsageCounter(out buffer, out senseBuffer, false, timeout, out duration); + if(!inhibitDma) + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); + else + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out sense); - /// Sends the SyQuest READ LONG (10) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting block. - /// Block size in bytes. - public bool SyQuestReadLong10(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint timeout, - out double duration) => - SyQuestRead10(out buffer, out senseBuffer, lba, blockSize, 1, false, true, timeout, out duration); + Error = LastError != 0; - /// Sends the SyQuest READ (10) command - /// true if the command failed and contains the sense buffer. - /// Buffer where the SCSI READ response will be stored - /// Sense buffer. - /// Timeout in seconds. - /// Duration in milliseconds it took for the device to execute the command. - /// Starting block. - /// If set to true, block will not be transfer and will reside in the drive's buffer. - /// If set to true drive will return ECC bytes and disable error detection. - /// Block size in bytes. - /// How many blocks to read. - public bool SyQuestRead10(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, - ushort transferLength, bool inhibitDma, bool readLong, uint timeout, - out double duration) + AaruConsole.DebugWriteLine("SCSI Device", "SYQUEST READ (6) took {0} ms.", duration); + + return sense; + } + + /// Requests the usage, seek and error counters, and resets them + /// Buffer. + /// Sense buffer. + /// Timeout. + /// Duration. + public bool SyQuestReadUsageCounter(out byte[] buffer, out byte[] senseBuffer, uint timeout, + out double duration) => + AdaptecReadUsageCounter(out buffer, out senseBuffer, false, timeout, out duration); + + /// Sends the SyQuest READ LONG (10) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting block. + /// Block size in bytes. + public bool SyQuestReadLong10(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, uint timeout, + out double duration) => + SyQuestRead10(out buffer, out senseBuffer, lba, blockSize, 1, false, true, timeout, out duration); + + /// Sends the SyQuest READ (10) command + /// true if the command failed and contains the sense buffer. + /// Buffer where the SCSI READ response will be stored + /// Sense buffer. + /// Timeout in seconds. + /// Duration in milliseconds it took for the device to execute the command. + /// Starting block. + /// If set to true, block will not be transfer and will reside in the drive's buffer. + /// If set to true drive will return ECC bytes and disable error detection. + /// Block size in bytes. + /// How many blocks to read. + public bool SyQuestRead10(out byte[] buffer, out byte[] senseBuffer, uint lba, uint blockSize, + ushort transferLength, bool inhibitDma, bool readLong, uint timeout, + out double duration) + { + senseBuffer = new byte[64]; + byte[] cdb = new byte[10]; + bool sense; + + cdb[0] = (byte)ScsiCommands.Read10; + cdb[2] = (byte)((lba & 0xFF000000) >> 24); + cdb[3] = (byte)((lba & 0xFF0000) >> 16); + cdb[4] = (byte)((lba & 0xFF00) >> 8); + cdb[5] = (byte)(lba & 0xFF); + cdb[7] = (byte)((transferLength & 0xFF00) >> 8); + cdb[8] = (byte)(transferLength & 0xFF); + + if(inhibitDma) + cdb[9] += 0x80; + + if(readLong) + cdb[9] += 0x40; + + if(!inhibitDma && + !readLong) + buffer = new byte[transferLength * blockSize]; + else if(readLong) { - senseBuffer = new byte[64]; - byte[] cdb = new byte[10]; - bool sense; - - cdb[0] = (byte)ScsiCommands.Read10; - cdb[2] = (byte)((lba & 0xFF000000) >> 24); - cdb[3] = (byte)((lba & 0xFF0000) >> 16); - cdb[4] = (byte)((lba & 0xFF00) >> 8); - cdb[5] = (byte)(lba & 0xFF); - cdb[7] = (byte)((transferLength & 0xFF00) >> 8); - cdb[8] = (byte)(transferLength & 0xFF); - - if(inhibitDma) - cdb[9] += 0x80; - - if(readLong) - cdb[9] += 0x40; - - if(!inhibitDma && - !readLong) - buffer = new byte[transferLength * blockSize]; - else if(readLong) - { - buffer = new byte[blockSize]; - cdb[4] = 1; - } - else - buffer = Array.Empty(); - - if(!inhibitDma) - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, - out sense); - else - LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, - out sense); - - Error = LastError != 0; - - AaruConsole.DebugWriteLine("SCSI Device", "SYQUEST READ (10) took {0} ms.", duration); - - return sense; + buffer = new byte[blockSize]; + cdb[4] = 1; } + else + buffer = Array.Empty(); + + if(!inhibitDma) + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.In, out duration, + out sense); + else + LastError = SendScsiCommand(cdb, ref buffer, out senseBuffer, timeout, ScsiDirection.None, out duration, + out sense); + + Error = LastError != 0; + + AaruConsole.DebugWriteLine("SCSI Device", "SYQUEST READ (10) took {0} ms.", duration); + + return sense; } } \ No newline at end of file diff --git a/Aaru.Devices/Device/Variables.cs b/Aaru.Devices/Device/Variables.cs index 0f65a2674..ce6462955 100644 --- a/Aaru.Devices/Device/Variables.cs +++ b/Aaru.Devices/Device/Variables.cs @@ -34,162 +34,161 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interop; using Aaru.CommonTypes.Structs.Devices.SCSI; -namespace Aaru.Devices +namespace Aaru.Devices; + +public sealed partial class Device { - public sealed partial class Device + readonly ushort _usbVendor; + readonly ushort _usbProduct; + readonly ulong _firewireGuid; + readonly uint _firewireModel; + readonly uint _firewireVendor; + + // MMC and SecureDigital, values that need to be get with card idle, something that may + // not be possible to do but usually is already done by the SDHCI driver. + readonly byte[] _cachedCsd; + readonly byte[] _cachedCid; + readonly byte[] _cachedScr; + readonly byte[] _cachedOcr; + + /// Gets the Platform ID for this device + /// The Platform ID + public PlatformID PlatformId { get; } + + /// Gets the file handle representing this device + /// The file handle + public object FileHandle { get; private set; } + + /// Gets or sets the standard timeout for commands sent to this device + /// The timeout in seconds + public uint Timeout { get; } + + /// Gets a value indicating whether this is in error. + /// true if error; otherwise, false. + public bool Error { get; private set; } + + /// Gets the last error number. + /// The last error. + public int LastError { get; private set; } + + /// Gets the device type. + /// The device type. + public DeviceType Type { get; } + + /// Gets the device's manufacturer + /// The manufacturer. + public string Manufacturer { get; } + + /// Gets the device model + /// The model. + public string Model { get; } + + /// Gets the device's firmware version. + /// The firmware version. + public string FirmwareRevision { get; } + + /// Gets the device's serial number. + /// The serial number. + public string Serial { get; } + + /// Gets the device's SCSI peripheral device type + /// The SCSI peripheral device type. + public PeripheralDeviceTypes ScsiType { get; } + + /// Gets a value indicating whether this device's media is removable. + /// true if this device's media is removable; otherwise, false. + public bool IsRemovable { get; } + + /// Gets a value indicating whether this device is attached via USB. + /// true if this device is attached via USB; otherwise, false. + public bool IsUsb { get; } + + /// Gets the USB vendor ID. + /// The USB vendor ID. + public ushort UsbVendorId => _usbVendor; + + /// Gets the USB product ID. + /// The USB product ID. + public ushort UsbProductId => _usbProduct; + + /// Gets the USB descriptors. + /// The USB descriptors. + public byte[] UsbDescriptors { get; } + + /// Gets the USB manufacturer string. + /// The USB manufacturer string. + public string UsbManufacturerString { get; } + + /// Gets the USB product string. + /// The USB product string. + public string UsbProductString { get; } + + /// Gets the USB serial string. + /// The USB serial string. + public string UsbSerialString { get; } + + /// Gets a value indicating whether this device is attached via FireWire. + /// true if this device is attached via FireWire; otherwise, false. + public bool IsFireWire { get; } + + /// Gets the FireWire GUID + /// The FireWire GUID. + public ulong FireWireGuid => _firewireGuid; + + /// Gets the FireWire model number + /// The FireWire model. + public uint FireWireModel => _firewireModel; + + /// Gets the FireWire model name. + /// The FireWire model name. + public string FireWireModelName { get; } + + /// Gets the FireWire vendor number. + /// The FireWire vendor number. + public uint FireWireVendor => _firewireVendor; + + /// Gets the FireWire vendor name. + /// The FireWire vendor name. + public string FireWireVendorName { get; } + + /// Gets a value indicating whether this device is a CompactFlash device. + /// true if this device is a CompactFlash device; otherwise, false. + public bool IsCompactFlash { get; } + + /// Gets a value indicating whether this device is a PCMCIA device. + /// true if this device is a PCMCIA device; otherwise, false. + public bool IsPcmcia { get; } + + /// Contains the PCMCIA CIS if applicable + public byte[] Cis { get; } + + readonly Remote.Remote _remote; + bool? _isRemoteAdmin; + readonly string _devicePath; + + /// Returns if remote is running under administrative (aka root) privileges + public bool IsRemoteAdmin { - readonly ushort _usbVendor; - readonly ushort _usbProduct; - readonly ulong _firewireGuid; - readonly uint _firewireModel; - readonly uint _firewireVendor; - - // MMC and SecureDigital, values that need to be get with card idle, something that may - // not be possible to do but usually is already done by the SDHCI driver. - readonly byte[] _cachedCsd; - readonly byte[] _cachedCid; - readonly byte[] _cachedScr; - readonly byte[] _cachedOcr; - - /// Gets the Platform ID for this device - /// The Platform ID - public PlatformID PlatformId { get; } - - /// Gets the file handle representing this device - /// The file handle - public object FileHandle { get; private set; } - - /// Gets or sets the standard timeout for commands sent to this device - /// The timeout in seconds - public uint Timeout { get; } - - /// Gets a value indicating whether this is in error. - /// true if error; otherwise, false. - public bool Error { get; private set; } - - /// Gets the last error number. - /// The last error. - public int LastError { get; private set; } - - /// Gets the device type. - /// The device type. - public DeviceType Type { get; } - - /// Gets the device's manufacturer - /// The manufacturer. - public string Manufacturer { get; } - - /// Gets the device model - /// The model. - public string Model { get; } - - /// Gets the device's firmware version. - /// The firmware version. - public string FirmwareRevision { get; } - - /// Gets the device's serial number. - /// The serial number. - public string Serial { get; } - - /// Gets the device's SCSI peripheral device type - /// The SCSI peripheral device type. - public PeripheralDeviceTypes ScsiType { get; } - - /// Gets a value indicating whether this device's media is removable. - /// true if this device's media is removable; otherwise, false. - public bool IsRemovable { get; } - - /// Gets a value indicating whether this device is attached via USB. - /// true if this device is attached via USB; otherwise, false. - public bool IsUsb { get; } - - /// Gets the USB vendor ID. - /// The USB vendor ID. - public ushort UsbVendorId => _usbVendor; - - /// Gets the USB product ID. - /// The USB product ID. - public ushort UsbProductId => _usbProduct; - - /// Gets the USB descriptors. - /// The USB descriptors. - public byte[] UsbDescriptors { get; } - - /// Gets the USB manufacturer string. - /// The USB manufacturer string. - public string UsbManufacturerString { get; } - - /// Gets the USB product string. - /// The USB product string. - public string UsbProductString { get; } - - /// Gets the USB serial string. - /// The USB serial string. - public string UsbSerialString { get; } - - /// Gets a value indicating whether this device is attached via FireWire. - /// true if this device is attached via FireWire; otherwise, false. - public bool IsFireWire { get; } - - /// Gets the FireWire GUID - /// The FireWire GUID. - public ulong FireWireGuid => _firewireGuid; - - /// Gets the FireWire model number - /// The FireWire model. - public uint FireWireModel => _firewireModel; - - /// Gets the FireWire model name. - /// The FireWire model name. - public string FireWireModelName { get; } - - /// Gets the FireWire vendor number. - /// The FireWire vendor number. - public uint FireWireVendor => _firewireVendor; - - /// Gets the FireWire vendor name. - /// The FireWire vendor name. - public string FireWireVendorName { get; } - - /// Gets a value indicating whether this device is a CompactFlash device. - /// true if this device is a CompactFlash device; otherwise, false. - public bool IsCompactFlash { get; } - - /// Gets a value indicating whether this device is a PCMCIA device. - /// true if this device is a PCMCIA device; otherwise, false. - public bool IsPcmcia { get; } - - /// Contains the PCMCIA CIS if applicable - public byte[] Cis { get; } - - readonly Remote.Remote _remote; - bool? _isRemoteAdmin; - readonly string _devicePath; - - /// Returns if remote is running under administrative (aka root) privileges - public bool IsRemoteAdmin + get { - get - { - _isRemoteAdmin ??= _remote.IsRoot; + _isRemoteAdmin ??= _remote.IsRoot; - return _isRemoteAdmin == true; - } + return _isRemoteAdmin == true; } - - /// Current device is remote - public bool IsRemote => _remote != null; - /// Remote application - public string RemoteApplication => _remote?.ServerApplication; - /// Remote application server - public string RemoteVersion => _remote?.ServerVersion; - /// Remote operating system name - public string RemoteOperatingSystem => _remote?.ServerOperatingSystem; - /// Remote operating system version - public string RemoteOperatingSystemVersion => _remote?.ServerOperatingSystemVersion; - /// Remote architecture - public string RemoteArchitecture => _remote?.ServerArchitecture; - /// Remote protocol version - public int RemoteProtocolVersion => _remote?.ServerProtocolVersion ?? 0; } + + /// Current device is remote + public bool IsRemote => _remote != null; + /// Remote application + public string RemoteApplication => _remote?.ServerApplication; + /// Remote application server + public string RemoteVersion => _remote?.ServerVersion; + /// Remote operating system name + public string RemoteOperatingSystem => _remote?.ServerOperatingSystem; + /// Remote operating system version + public string RemoteOperatingSystemVersion => _remote?.ServerOperatingSystemVersion; + /// Remote architecture + public string RemoteArchitecture => _remote?.ServerArchitecture; + /// Remote protocol version + public int RemoteProtocolVersion => _remote?.ServerProtocolVersion ?? 0; } \ No newline at end of file diff --git a/Aaru.Devices/Enums.cs b/Aaru.Devices/Enums.cs index 9f7dc4fc8..4f4e38163 100644 --- a/Aaru.Devices/Enums.cs +++ b/Aaru.Devices/Enums.cs @@ -41,2864 +41,2863 @@ using System.Diagnostics.CodeAnalysis; // ReSharper disable MemberCanBeInternal // ReSharper disable InconsistentNaming -namespace Aaru.Devices +namespace Aaru.Devices; + +#region ATA Commands +/// All known ATA commands +public enum AtaCommands : byte { - #region ATA Commands - /// All known ATA commands - public enum AtaCommands : byte - { - #region Commands defined on Western Digital WD1000 Winchester Disk Controller - /// Formats a track - FormatTrack = 0x50, + #region Commands defined on Western Digital WD1000 Winchester Disk Controller + /// Formats a track + FormatTrack = 0x50, - /// Reads sectors - ReadOld = 0x20, + /// Reads sectors + ReadOld = 0x20, - /// Reads sectors using DMA - ReadDmaOld = 0x28, + /// Reads sectors using DMA + ReadDmaOld = 0x28, - /// Calibrates the position of the heads Includes all commands from 0x10 to 0x1F - Restore = 0x10, + /// Calibrates the position of the heads Includes all commands from 0x10 to 0x1F + Restore = 0x10, - /// Seeks to a certain cylinder - Seek = 0x70, + /// Seeks to a certain cylinder + Seek = 0x70, - /// Writes sectors - WriteOld = 0x30, - #endregion Commands defined on Western Digital WD1000 Winchester Disk Controller + /// Writes sectors + WriteOld = 0x30, + #endregion Commands defined on Western Digital WD1000 Winchester Disk Controller - #region Commands defined on ATA rev. 4c - /// Acknowledges media change - AckMediaChange = 0xDB, + #region Commands defined on ATA rev. 4c + /// Acknowledges media change + AckMediaChange = 0xDB, - /// Sends vendor-specific information that may be required in order to pass diagnostics - PostBoot = 0xDC, + /// Sends vendor-specific information that may be required in order to pass diagnostics + PostBoot = 0xDC, - /// Prepares a removable drive to respond to boot - PreBoot = 0xDD, + /// Prepares a removable drive to respond to boot + PreBoot = 0xDD, - /// Checks drive power mode - CheckPowerMode = 0xE5, + /// Checks drive power mode + CheckPowerMode = 0xE5, - /// Checks drive power mode - CheckPowerModeAlternate = 0x98, + /// Checks drive power mode + CheckPowerModeAlternate = 0x98, - /// Locks the door of the drive - DoorLock = 0xDE, + /// Locks the door of the drive + DoorLock = 0xDE, - /// Unlocks the door of the drive - DoorUnLock = 0xDF, + /// Unlocks the door of the drive + DoorUnLock = 0xDF, - /// Executes internal drive diagnostics - ExecuteDriveDiagnostic = 0x90, + /// Executes internal drive diagnostics + ExecuteDriveDiagnostic = 0x90, - /// Gets a sector containing drive identification and capabilities - IdentifyDrive = 0xEC, + /// Gets a sector containing drive identification and capabilities + IdentifyDrive = 0xEC, - /// Requests the drive to enter idle status - Idle = 0xE3, + /// Requests the drive to enter idle status + Idle = 0xE3, - /// Requests the drive to enter idle status - IdleAlternate = 0x97, + /// Requests the drive to enter idle status + IdleAlternate = 0x97, - /// Requests the drive to enter idle status immediately - IdleImmediate = 0xE1, + /// Requests the drive to enter idle status immediately + IdleImmediate = 0xE1, - /// Requests the drive to enter idle status immediately - IdleImmediateAlternate = 0x95, + /// Requests the drive to enter idle status immediately + IdleImmediateAlternate = 0x95, - /// Changes heads and sectors per cylinder for the drive - InitializeDriveParameters = 0x91, + /// Changes heads and sectors per cylinder for the drive + InitializeDriveParameters = 0x91, - /// Does nothing - Nop = 0x00, + /// Does nothing + Nop = 0x00, - /// Reads sectors using PIO transfer - Read = 0x21, + /// Reads sectors using PIO transfer + Read = 0x21, - /// Reads the content of the drive's buffer - ReadBuffer = 0xE4, + /// Reads the content of the drive's buffer + ReadBuffer = 0xE4, - /// Reads sectors using DMA transfer - ReadDma = 0xC9, + /// Reads sectors using DMA transfer + ReadDma = 0xC9, - /// Reads sectors using DMA transfer, retrying on error - ReadDmaRetry = 0xC8, + /// Reads sectors using DMA transfer, retrying on error + ReadDmaRetry = 0xC8, - /// Reads a sector including ECC bytes without checking them - ReadLong = 0x23, + /// Reads a sector including ECC bytes without checking them + ReadLong = 0x23, - /// Reads a sector including ECC bytes without checking them, retrying on error - ReadLongRetry = 0x22, + /// Reads a sector including ECC bytes without checking them, retrying on error + ReadLongRetry = 0x22, - /// Reads multiple sectors generating interrupts at block transfers - ReadMultiple = 0xC4, + /// Reads multiple sectors generating interrupts at block transfers + ReadMultiple = 0xC4, - /// Reads sectors using PIO transfer, retrying on error - ReadRetry = 0x20, + /// Reads sectors using PIO transfer, retrying on error + ReadRetry = 0x20, - /// Verifies sectors readability without transferring them - ReadVerify = 0x41, + /// Verifies sectors readability without transferring them + ReadVerify = 0x41, - /// Verifies sectors readability without transferring them, retrying on error - ReadVerifyRetry = 0x40, + /// Verifies sectors readability without transferring them, retrying on error + ReadVerifyRetry = 0x40, - /// Moves the heads to cylinder 0 - Recalibrate = Restore, + /// Moves the heads to cylinder 0 + Recalibrate = Restore, - /// Sets drive parameters - SetFeatures = 0xEF, - - /// - /// Enables and and sets the block length for these - /// commands - /// - SetMultipleMode = 0xC6, - - /// Causes the drive to stop and sleep until a hardware or software reset - Sleep = 0xE6, - - /// Causes the drive to stop and sleep until a hardware or software reset - SleepAlternate = 0x99, - - /// Sets the drive to enter Standby mode - Standby = 0xE2, - - /// Sets the drive to enter Standby mode - StandbyAlternate = 0x96, - - /// Sets the drive to enter Standby mode, immediately - StandbyImmediate = 0xE0, - - /// Sets the drive to enter Standby mode, immediately - StandbyImmediateAlternate = 0x94, - - /// Writes sectors using PIO transfer - Write = 0x31, - - /// Writes data to the drive's sector buffer - WriteBuffer = 0xE8, - - /// Writes sectors using DMA transfer - WriteDma = 0xCB, - - /// Writes sectors using DMA transfer, retrying on error - WriteDmaRetry = 0xCA, - - /// Writes sectors with custom ECC - WriteLong = 0x33, - - /// Writes sectors with custom ECC, retrying on error - WriteLongRetry = 0x32, - - /// Writes several sectors at once setting interrupts on end of block - WriteMultiple = 0xC5, - - /// Writes the same data to several sector - WriteSame = 0xE9, - - /// Writes sectors using PIO transfer, retrying on error - WriteRetry = 0x30, - - /// Writes sectors verifying them immediately after write - WriteVerify = 0x3C, - - /// Unknown vendor command - Vendor_8X = 0x80, - - /// Unknown vendor command - Vendor_9A = 0x9A, - - /// Unknown vendor command - VendorC0 = 0xC0, - - /// Unknown vendor command - VendorC1 = 0xC1, - - /// Unknown vendor command - VendorC2 = 0xC2, - - /// Unknown vendor command - VendorC3 = 0xC3, - - /// Unknown vendor command - VendorF0 = 0xF0, - - /// Unknown vendor command - VendorF1 = 0xF1, - - /// Unknown vendor command - VendorF2 = 0xF2, - - /// Unknown vendor command - VendorF3 = 0xF3, - - /// Unknown vendor command - VendorF4 = 0xF4, - - /// Unknown vendor command - VendorF5 = 0xF5, - - /// Unknown vendor command - VendorF6 = 0xF6, - - /// Unknown vendor command - VendorF7 = 0xF7, - - /// Unknown vendor command - VendorF8 = 0xF8, - - /// Unknown vendor command - VendorF9 = 0xF9, - - /// Unknown vendor command - VendorFa = 0xFA, - - /// Unknown vendor command - VendorFb = 0xFB, - - /// Unknown vendor command - VendorFc = 0xFC, - - /// Unknown vendor command - VendorFd = 0xFD, - - /// Unknown vendor command - VendorFe = 0xFE, - - /// Unknown vendor command - VendorFf = 0xFF, - #endregion Commands defined on ATA rev. 4c - - #region Commands defined on ATA-2 rev. 4c - /// Alters the device microcode - DownloadMicrocode = 0x92, - - /// Ejects the removable medium on the device - MediaEject = 0xED, - #endregion Commands defined on ATA-2 rev. 4c - - #region Commands defined on ATA-3 rev. 7b - /// Gets a sector containing drive identification and capabilities - IdentifyDriveDma = 0xEE, - - /// Disables the security lock - SecurityDisablePassword = 0xF6, - - /// Enables usage of command - SecurityErasePrepare = 0xF3, - - /// Erases all user data and disables the security lock - SecurityEraseUnit = 0xF4, - - /// Sets the security freeze lock preventing any security command from working until hardware reset - SecurityFreezeLock = 0xF5, - - /// Sets the device user or master password - SecuritySetPassword = 0xF1, - - /// Unlocks device - SecurityUnlock = 0xF2, - - /// SMART operations - Smart = 0xB0, - #endregion Commands defined on ATA-3 rev. 7b - - #region Commands defined on CompactFlash Specification - /// Pre-erases and conditions data sectors - EraseSectors = 0xC0, - - /// Requests extended error information - RequestSense = 0x03, - - /// Provides a way to determine the exact number of times a sector has been erases and programmed - TranslateSector = 0x87, - - /// - /// For CompactFlash cards that do not support security mode, this commands is equal to For - /// those that do, this command is equal to - /// - WearLevel = 0xF5, - - /// Writes a block of sectors without erasing them previously - WriteMultipleWithoutErase = 0xCD, - - /// Writes sectors without erasing them previously - WriteWithoutErase = 0x38, - #endregion Commands defined on CompactFlash Specification - - #region Commands defined on ATA/ATAPI-4 rev. 18 - /// Resets a device - DeviceReset = 0x08, - - /// Requests the device to flush the write cache and write it to the media - FlushCache = 0xE7, - - /// Gets media status - GetMediaStatus = 0xDA, - - /// Gets a sector containing drive identification and capabilities, for ATA devices - IdentifyDevice = IdentifyDrive, - - /// Gets a sector containing drive identification and capabilities, for ATAPI devices - IdentifyPacketDevice = 0xA1, - - /// Locks the media on the device - MediaLock = DoorLock, - - /// Unlocks the media on the device - MediaUnLock = DoorUnLock, - - /// Sends a command packet - Packet = 0xA0, - - /// Queues a read of sectors - ReadDmaQueued = 0xC7, - - /// Returns the native maximum address in factory default condition - ReadNativeMaxAddress = 0xF8, - - /// Used to provide data transfer and/or status of a previous command (queue or packet) - Service = 0xA2, - - /// Redefines the maximum user-accessible address space - SetMaxAddress = 0xF9, - - /// Queues a write of sectors - WriteDmaQueued = 0xCC, - #endregion Commands defined on ATA/ATAPI-4 rev. 18 - - #region Commands defined on ATA/ATAPI-6 rev. 3b - /// Determines if the device supports the Media Card Pass Through Command feature set - CheckMediaCardType = 0xD1, - - /// Device Configuration Overlay feature set - DeviceConfiguration = 0xB1, - - /// Requests the device to flush the write cache and write it to the media (48-bit) - FlushCacheExt = 0xEA, - - /// Reads sectors using DMA transfer, retrying on error (48-bit) - ReadDmaExt = 0x25, - - /// (48-bit) Queues a read of sectors - ReadDmaQueuedExt = 0x26, - - /// Reads sectors using PIO transfer, retrying on error (48-bit) - ReadExt = 0x24, - - /// Returns the indicated log to the host (48-bit) - ReadLogExt = 0x2F, - - /// Reads multiple sectors generating interrupts at block transfers (48-bit) - ReadMultipleExt = 0x29, - - /// Returns the native maximum address in factory default condition (48-bit) - ReadNativeMaxAddressExt = 0x27, - - /// Verifies sectors readability without transferring them, retrying on error (48-bit) - ReadVerifyExt = 0x42, - - /// Sends a SET MAX subcommand, - SetMaxCommands = 0xF9, - - /// Redefines the maximum user-accessible address space (48-bit) - SetMaxAddressExt = 0x37, - - /// Writes sectors using DMA transfer, retrying on error (48-bit) - WriteDmaExt = 0x35, - - /// Queues a write of sectors (48-bit) - WriteDmaQueuedExt = 0x36, - - /// Writes sectors using PIO transfer, retrying on error (48-bit) - WriteExt = 0x34, - - /// Writes data to the indicated log (48-bit) - WriteLogExt = 0x3F, - - /// Writes several sectors at once setting interrupts on end of block (48-bit) - WriteMultipleExt = 0x39, - #endregion Commands defined on ATA/ATAPI-6 rev. 3b - - #region Commands defined on ATA/ATAPI-7 rev. 4b - /// Configures the operating parameters for a stream - ConfigureStream = 0x51, - - /// Reads data on an allotted time using DMA - ReadStreamDmaExt = 0x2A, - - /// Reads data on an allotted time using PIO - ReadStreamExt = 0x2B, - - /// Writes data on an allotted time using DMA - WriteStreamDmaExt = 0x3A, - - /// Writes data on an allotted time using PIO - WriteStreamExt = 0x3B, - #endregion Commands defined on ATA/ATAPI-7 rev. 4b - - #region Commands defined on ATA/ATAPI-8 rev. 3f - /// Sends a Non Volatile Cache subcommand. - NonVolatileCacheCommand = 0xB6, - - /// Retrieves security protocol information or the results from commands - TrustedReceive = 0x5C, - - /// - /// Retrieves security protocol information or the results from commands, using DMA - /// transfers - /// - TrustedReceiveDma = 0x5D, - - /// Sends one or more Security Protocol commands - TrustedSend = 0x5E, - - /// Sends one or more Security Protocol commands, using DMA transfers - TrustedSendDma = 0x5F, - - /// Writes sectors using DMA transfer, retrying on error (48-bit), not returning until the operation is complete - WriteDmaFuaExt = 0x3D, - - /// Queues a write of sectors (48-bit), not returning until the operation is complete - WriteDmaQueuedFuaExt = 0x3E, - - /// - /// Writes several sectors at once setting interrupts on end of block (48-bit), not returning until the operation - /// is complete - /// - WriteMultipleFuaExt = 0xCE, - - /// Writes a sector that will give an uncorrectable error on any read operation - WriteUncorrectableExt = 0x45, - #endregion Commands defined on ATA/ATAPI-8 rev. 3f - - #region Commands defined on ATA/ATAPI Command Set 2 (ACS-2) rev. 2 - /// Provides information for device optimization In SSDs, this contains trimming - DataSetManagement = 0x06, - - /// Alters the device microcode, using DMA transfers - DownloadMicrocodeDma = 0x93, - - /// Reads the content of the drive's buffer, using DMA transfers - ReadBufferDma = 0xE9, - - /// Reads sectors using NCQ - ReadFpDmaQueued = 0x60, - - /// Returns the indicated log to the host (48-bit) - ReadLogDmaExt = 0x47, - - /// Requests SPC-4 style error data - RequestSenseDataExt = 0x0B, SanitizeCommands = 0xB4, - - /// Executes a Security Protocol command that does not require a transfer of data - TrustedNonData = 0x5B, - - /// Writes data to the drive's sector buffer, using DMA transfers - WriteBufferDma = 0xE8, - - /// Writes sectors using NCQ - WriteFpDmaQueued = 0x61, - #endregion Commands defined on ATA/ATAPI Command Set 2 (ACS-2) rev. 2 - - #region Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 - /// Sends - NcqQueueManagement = 0x63, - - /// Sets the device date and time - SetDateAndTimeExt = 0x77, - #endregion Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 - - #region Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 6 - NativeMaxAddress = 0x78 - #endregion Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 6 - } - #endregion ATA Commands - - #region ATA SMART SubCommands - /// All known ATA SMART sub-commands - public enum AtaSmartSubCommands : byte - { - #region Commands defined on ATA-3 rev. 7b - /// Disables all SMART capabilities - Disable = 0xD9, - - /// Enables/disables SMART attribute autosaving - EnableDisableAttributeAutosave = 0xD2, - - /// Enables all SMART capabilities - Enable = 0xD8, - - /// Returns the device's SMART attributes thresholds - ReadAttributeThresholds = 0xD1, - - /// Returns the device's SMART attributes values - ReadAttributeValues = 0xD0, - - /// Communicates device reliability status - ReturnStatus = 0xDA, - - /// Saves any attribute values immediately - SaveAttributeValues = 0xD3, - #endregion Commands defined on ATA-3 rev. 7b - - #region Commands defined on ATA/ATAPI-4 rev. 18 - /// Causes the device to immediately initiate a SMART data collection and saves it to the device - ExecuteOfflineImmediate = 0xD4, - - /// Returns the device's SMART attributes values - ReadData = ReadAttributeValues, - #endregion Commands defined on ATA/ATAPI-4 rev. 18 - - #region Commands defined on ATA/ATAPI-5 rev. 3 - /// Returns the indicated log to the host - ReadLog = 0xD5, - - /// Writes data to the indicated log - WriteLog = 0xD6 - #endregion Commands defined on ATA/ATAPI-5 rev. 3 - } - #endregion ATA SMART SubCommands - - #region ATA Device Configuration Overlay SubCommands - /// All known ATA DEVICE CONFIGURATION sub-commands - public enum AtaDeviceConfigurationSubCommands : byte - { - #region Commands defined on ATA/ATAPI-6 rev. 3b - /// Disables any change made by - Restore = 0xC0, - - /// Prevents any from working until a power down cycle. - FreezeLock = 0xC1, - - /// Indicates the selectable commands, modes, and feature sets the device supports - Identify = 0xC2, - - /// Modifies the commands, modes and features sets the device will obey to - Set = 0xC3 - #endregion Commands defined on ATA/ATAPI-6 rev. 3b - } - #endregion ATA Device Configuration Overlay SubCommands - - #region ATA SET MAX SubCommands - /// All known ATA SET MAX sub-commands - public enum AtaSetMaxSubCommands : byte - { - #region Commands defined on ATA/ATAPI-6 rev. 3b - /// Redefines the maximum user-accessible address space - Address = 0x00, - - /// Disables any other until power cycle - FreezeLock = 0x04, - - /// - /// Disables any other except and - /// until power cycle - /// - Lock = 0x02, - - /// Sets the device password - SetPassword = 0x01, - - /// Disables - UnLock = 0x03, - #endregion Commands defined on ATA/ATAPI-6 rev. 3b - } - #endregion ATA SET MAX SubCommands - - #region ATA Non Volatile Cache SubCommands - /// All known ATA NV CACHE sub-commands - public enum AtaNonVolatileCacheSubCommands : byte - { - #region Commands defined on ATA/ATAPI-8 rev. 3f - /// Adds the specified LBA to the Non Volatile Cache - AddLbaToNvCache = 0x10, - - /// Ensures there is enough free space in the Non Volatile Cache - FlushNvCache = 0x14, - - /// Requests a list of LBAs actually stored in the Non Volatile Cache - QueryNvCachePinnedSet = 0x12, - - /// Requests a list of LBAs accessed but not in the Non Volatile Cache - QueryNvCacheMisses = 0x13, - - /// Removes the specified LBA from the Non Volatile Cache Pinned Set - RemoveLbaFromNvCache = 0x11, - - /// Disables the Non Volatile Cache Power Mode - ReturnFromNvCachePowerMode = 0x01, - - /// - /// Enables the Non Volatile Cache Power Mode, so the device tries to serve all accesses from the Non Volatile - /// Cache - /// - SetNvCachePowerMode = 0x00 - #endregion Commands defined on ATA/ATAPI-8 rev. 3f - } - #endregion ATA Non Volatile Cache SubCommands - - #region ATA Sanitize SubCommands - /// All known ATA SANITIZE sub-commands - public enum AtaSanitizeSubCommands : ushort - { - #region Commands defined on ATA/ATAPI Command Set 2 (ACS-2) rev. 2 - /// Causes a block erase on all user data - BlockEraseExt = 0x0012, - - /// Changes the internal encryption keys. Renders user data unusable - CryptoScrambleExt = 0x0011, - - /// Fills user data with specified pattern - OverwriteExt = 0x0014, - - /// Disables all except - FreezeLockExt = 0x0020, - - /// Gets the status of the sanitizing - Status = 0x0000, - #endregion Commands defined on ATA/ATAPI Command Set 2 (ACS-2) rev. 2 - - #region Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 - /// Disables the command - AntiFreezeLockExt = 0x0040 - #endregion Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 - } - #endregion ATA Sanitize SubCommands - - #region ATA NCQ Queue Management SubCommands - /// All known ATA NCQ QUEUE MANAGEMENT sub-commands - public enum AtaNcqQueueManagementSubcommands : byte - { - #region Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 - /// Aborts pending NCQ commands - AbortNcqQueue = 0x00, - - /// Controls how NCQ Streaming commands are processed by the device - DeadlineHandling = 0x01, - #endregion Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 - } - #endregion ATA NCQ Queue Management SubCommands + /// Sets drive parameters + SetFeatures = 0xEF, /// - /// All known SASI commands Commands 0x00 to 0x1F are 6-byte Commands 0x20 to 0x3F are 10-byte Commands 0x40 to - /// 0x5F are 8-byte Commands 0xA0 to 0xBF are 12-byte + /// Enables and and sets the block length for these + /// commands /// - #region SASI Commands - public enum SasiCommands : byte - { - #region SASI Class 0 commands - /// Returns zero status if requested unit is on and ready. SASI rev. 0a - TestUnitReady = 0x00, + SetMultipleMode = 0xC6, - /// Sets the unit to a specific known state. SASI rev. 0a - RezeroUnit = 0x01, + /// Causes the drive to stop and sleep until a hardware or software reset + Sleep = 0xE6, - /// Unknown SASI rev. 0a - RequestSyndrome = 0x02, + /// Causes the drive to stop and sleep until a hardware or software reset + SleepAlternate = 0x99, - /// Returns unit sense. SASI rev. 0a - RequestSense = 0x03, + /// Sets the drive to enter Standby mode + Standby = 0xE2, - /// Formats the entire media. SASI rev. 0a - FormatUnit = 0x04, + /// Sets the drive to enter Standby mode + StandbyAlternate = 0x96, - /// Unknown SASI rev. 0a - CheckTrackFormat = 0x05, + /// Sets the drive to enter Standby mode, immediately + StandbyImmediate = 0xE0, - /// Unknown SASI rev. 0a - FormatTrack = 0x06, + /// Sets the drive to enter Standby mode, immediately + StandbyImmediateAlternate = 0x94, - /// Unknown SASI rev. 0a - FormatBadTrack = 0x06, + /// Writes sectors using PIO transfer + Write = 0x31, - /// Reads a block from the device. SASI rev. 0a - Read = 0x08, + /// Writes data to the drive's sector buffer + WriteBuffer = 0xE8, - /// SASI rev. 0a Unknown - WriteProtectSector = 0x09, + /// Writes sectors using DMA transfer + WriteDma = 0xCB, - /// Writes a block to the device. SASI rev. 0a - Write = 0x0A, + /// Writes sectors using DMA transfer, retrying on error + WriteDmaRetry = 0xCA, - /// Moves the device reading mechanism to the specified block. SASI rev. 0a - Seek = 0x0B, + /// Writes sectors with custom ECC + WriteLong = 0x33, - /// Found on a vendor source code - InitDriveCharacteristics = 0x0C, + /// Writes sectors with custom ECC, retrying on error + WriteLongRetry = 0x32, - /// Unknown SASI rev. 0a - VerifyRestore = 0x0D, + /// Writes several sectors at once setting interrupts on end of block + WriteMultiple = 0xC5, - /// Unknown SASI rev. 0a - AssignAlternateDiskTrack = 0x0E, + /// Writes the same data to several sector + WriteSame = 0xE9, - /// Writes a File Mark on the device. SASI rev. 0c - WriteFileMark = 0x0F, + /// Writes sectors using PIO transfer, retrying on error + WriteRetry = 0x30, - /// Reserves the device for use by the initiator. SASI rev. 0a - ReserveUnitOld = 0x12, + /// Writes sectors verifying them immediately after write + WriteVerify = 0x3C, - /// Gets information about a device ANSI X3T9.3 No. 185 (SASI) - Inquiry = 0x12, + /// Unknown vendor command + Vendor_8X = 0x80, - /// Release the device from the reservation. SASI rev. 0a - ReleaseUnitOld = 0x13, + /// Unknown vendor command + Vendor_9A = 0x9A, - /// Unknown SASI rev. 0a - WriteProtectDrive = 0x14, + /// Unknown vendor command + VendorC0 = 0xC0, - /// Writes and verifies blocks to the device. SASI rev. 0c - WriteAndVerifyOld = 0x14, + /// Unknown vendor command + VendorC1 = 0xC1, - /// Unknown SASI rev. 0a - ReleaseWriteProtect = 0x15, + /// Unknown vendor command + VendorC2 = 0xC2, - /// Verifies blocks. SASI rev. 0c - VerifyOld = 0x15, + /// Unknown vendor command + VendorC3 = 0xC3, - /// Unknown SASI rev. 0a - ReadNoSeek = 0x16, + /// Unknown vendor command + VendorF0 = 0xF0, - /// Gets the number of blocks in device. SASI rev. 0c - ReadCapacityOld = 0x16, + /// Unknown vendor command + VendorF1 = 0xF1, - /// Reserves the device for use by the initiator. ANSI X3T9.3 No. 185 (SASI) - ReserveUnit = 0x16, + /// Unknown vendor command + VendorF2 = 0xF2, - /// Release the device from the reservation. ANSI X3T9.3 No. 185 (SASI) - ReleaseUnit = 0x17, + /// Unknown vendor command + VendorF3 = 0xF3, - /// Searches data on blocks SASI rev. 0a - SearchDataEqualOld = 0x17, + /// Unknown vendor command + VendorF4 = 0xF4, - /// Searches data on blocks using major than or equal comparison SASI rev. 0a - SearchDataHighOld = 0x18, + /// Unknown vendor command + VendorF5 = 0xF5, - /// Searches data on blocks using minor than or equal comparison SASI rev. 0a - SearchDataLowOld = 0x19, + /// Unknown vendor command + VendorF6 = 0xF6, - /// Reads analysis data from a device SASI rev. 0a - ReadDiagnosticOld = 0x1A, + /// Unknown vendor command + VendorF7 = 0xF7, - /// Unknown SASI rev. 0a - VerifyData = 0x1B, + /// Unknown vendor command + VendorF8 = 0xF8, - /// Requests a device to run a diagnostic SASI rev. 0c - WriteDiagnosticOld = 0x1B, + /// Unknown vendor command + VendorF9 = 0xF9, - /// Requests the data after completion of a ANSI X3T9.3 No. 185 (SASI) - ReadDiagnostic = 0x1C, + /// Unknown vendor command + VendorFa = 0xFA, - /// Requests the device to perform diagnostics ANSI X3T9.3 No. 185 (SASI) - WriteDiagnostic = 0x1D, + /// Unknown vendor command + VendorFb = 0xFB, - /// Gets information about a device SASI rev. 0c - InquiryOld = 0x1F, - #endregion SASI Class 0 commands + /// Unknown vendor command + VendorFc = 0xFC, - #region SASI Class 1 commands - /// SASI rev. 0a Unknown - Copy = 0x20, + /// Unknown vendor command + VendorFd = 0xFD, - /// SASI rev. 0a Unknown - Restore = 0x21, + /// Unknown vendor command + VendorFe = 0xFE, - /// SASI rev. 0a Unknown - Backup = 0x22, + /// Unknown vendor command + VendorFf = 0xFF, + #endregion Commands defined on ATA rev. 4c - /// SASI rev. 0a Unknown - SetBlockLimitsOlder = 0x26, + #region Commands defined on ATA-2 rev. 4c + /// Alters the device microcode + DownloadMicrocode = 0x92, - /// Sets write or read limits from a specified block SASI rev. 0c - SetBlockLimitsOld = 0x28, + /// Ejects the removable medium on the device + MediaEject = 0xED, + #endregion Commands defined on ATA-2 rev. 4c - /// Reads blocks from device ANSI X3T9.3 No. 185 (SASI) - ExtendedAddressRead = 0x28, + #region Commands defined on ATA-3 rev. 7b + /// Gets a sector containing drive identification and capabilities + IdentifyDriveDma = 0xEE, - /// Writes blocks to the device ANSI X3T9.3 No. 185 (SASI) - ExtendedAddressWrite = 0x2A, + /// Disables the security lock + SecurityDisablePassword = 0xF6, - /// Writes blocks to the device and then verifies them ANSI X3T9.3 No. 185 (SASI) - WriteAndVerify = 0x2E, + /// Enables usage of command + SecurityErasePrepare = 0xF3, - /// Verifies blocks on the device ANSI X3T9.3 No. 185 (SASI) - Verify = 0x2F, + /// Erases all user data and disables the security lock + SecurityEraseUnit = 0xF4, - /// Searches data on blocks ANSI X3T9.3 No. 185 (SASI) - SearchDataEqual = 0x31, + /// Sets the security freeze lock preventing any security command from working until hardware reset + SecurityFreezeLock = 0xF5, - /// Searches data on blocks using major than or equal comparison ANSI X3T9.3 No. 185 (SASI) - SearchDataHigh = 0x30, + /// Sets the device user or master password + SecuritySetPassword = 0xF1, - /// Searches data on blocks using minor than or equal comparison ANSI X3T9.3 No. 185 (SASI) - SearchDataLow = 0x32, - #endregion SASI Class 1 commands + /// Unlocks device + SecurityUnlock = 0xF2, - #region SASI Class 2 commands - /// Unknown SASI rev. 0a - Load = 0x40, + /// SMART operations + Smart = 0xB0, + #endregion Commands defined on ATA-3 rev. 7b - /// Unknown SASI rev. 0a - Unload = 0x41, + #region Commands defined on CompactFlash Specification + /// Pre-erases and conditions data sectors + EraseSectors = 0xC0, - /// Unknown SASI rev. 0a - Rewind = 0x42, + /// Requests extended error information + RequestSense = 0x03, - /// Unknown SASI rev. 0a - SpaceForward = 0x43, + /// Provides a way to determine the exact number of times a sector has been erases and programmed + TranslateSector = 0x87, - /// Unknown SASI rev. 0a - SpaceForwardFileMark = 0x44, + /// + /// For CompactFlash cards that do not support security mode, this commands is equal to For + /// those that do, this command is equal to + /// + WearLevel = 0xF5, - /// Unknown SASI rev. 0a - SpaceReverse = 0x45, + /// Writes a block of sectors without erasing them previously + WriteMultipleWithoutErase = 0xCD, - /// Unknown SASI rev. 0a - SpaceReverseFileMark = 0x46, + /// Writes sectors without erasing them previously + WriteWithoutErase = 0x38, + #endregion Commands defined on CompactFlash Specification - /// Unknown SASI rev. 0a - TrackSelect = 0x47, + #region Commands defined on ATA/ATAPI-4 rev. 18 + /// Resets a device + DeviceReset = 0x08, - /// Reads blocks from device SASI rev. 0a - Read8 = 0x48, + /// Requests the device to flush the write cache and write it to the media + FlushCache = 0xE7, - /// Unknown SASI rev. 0a - ReadVerify = 0x49, + /// Gets media status + GetMediaStatus = 0xDA, - /// Unknown SASI rev. 0a - ReadDiagnosticClass2 = 0x4A, + /// Gets a sector containing drive identification and capabilities, for ATA devices + IdentifyDevice = IdentifyDrive, - /// Writes blocks to device SASI rev. 0a - Write8 = 0x4B, + /// Gets a sector containing drive identification and capabilities, for ATAPI devices + IdentifyPacketDevice = 0xA1, - /// Unknown SASI rev. 0a - WriteFileMarkClass2 = 0x4C, + /// Locks the media on the device + MediaLock = DoorLock, - /// Unknown SASI rev. 0a - WriteExtended = 0x4D, + /// Unlocks the media on the device + MediaUnLock = DoorUnLock, - /// Unknown SASI rev. 0a - WriteExtendedFileMark = 0x4E, + /// Sends a command packet + Packet = 0xA0, - /// Unknown SASI rev. 0a - WriteErase = 0x4F, + /// Queues a read of sectors + ReadDmaQueued = 0xC7, - /// Writes and verifies blocks to the device. SASI rev. 0c - WriteVerify8 = 0x54, + /// Returns the native maximum address in factory default condition + ReadNativeMaxAddress = 0xF8, - /// Verifies blocks. SASI rev. 0c - Verify8 = 0x55, + /// Used to provide data transfer and/or status of a previous command (queue or packet) + Service = 0xA2, - /// Searches data on blocks using major than or equal comparison SASI rev. 0c - SearchDataHigh8 = 0x57, + /// Redefines the maximum user-accessible address space + SetMaxAddress = 0xF9, - /// Searches data on blocks SASI rev. 0c - SearchDataEqual8 = 0x58, + /// Queues a write of sectors + WriteDmaQueued = 0xCC, + #endregion Commands defined on ATA/ATAPI-4 rev. 18 - /// Searches data on blocks using minor than or equal comparison SASI rev. 0c - SearchDataLow8 = 0x59, - #endregion SASI Class 2 commands + #region Commands defined on ATA/ATAPI-6 rev. 3b + /// Determines if the device supports the Media Card Pass Through Command feature set + CheckMediaCardType = 0xD1, - #region SASI Class 3 commands - /// SASI rev. 0a - Skip = 0x60, + /// Device Configuration Overlay feature set + DeviceConfiguration = 0xB1, - /// SASI rev. 0a - Space = 0x61, + /// Requests the device to flush the write cache and write it to the media (48-bit) + FlushCacheExt = 0xEA, - /// SASI rev. 0a - Return = 0x62, + /// Reads sectors using DMA transfer, retrying on error (48-bit) + ReadDmaExt = 0x25, - /// SASI rev. 0a - Tab = 0x63, + /// (48-bit) Queues a read of sectors + ReadDmaQueuedExt = 0x26, - /// SASI rev. 0a - ReadControl = 0x64, + /// Reads sectors using PIO transfer, retrying on error (48-bit) + ReadExt = 0x24, - /// SASI rev. 0a - Write3 = 0x65, + /// Returns the indicated log to the host (48-bit) + ReadLogExt = 0x2F, - /// SASI rev. 0a - WriteControl = 0x66, - #endregion SASI Class 3 commands + /// Reads multiple sectors generating interrupts at block transfers (48-bit) + ReadMultipleExt = 0x29, - #region SASI Class 5 commands - /// Gets the number of blocks in device. ANSI X3T9.3 No. 185 (SASI) - ReadCapacity = 0xA5, + /// Returns the native maximum address in factory default condition (48-bit) + ReadNativeMaxAddressExt = 0x27, - /// Sets write or read limits from a specified block ANSI X3T9.3 No. 185 (SASI) - SetBlockLimits = 0xA9, - #endregion SASI Class 5 commands + /// Verifies sectors readability without transferring them, retrying on error (48-bit) + ReadVerifyExt = 0x42, - #region SASI Class 6 commands - /// SASI rev. 0a - DefineFloppyDiskTrackFormat = 0xC0, + /// Sends a SET MAX subcommand, + SetMaxCommands = 0xF9, - /// Unknown vendor command in X68000 - Specify = 0xC2, + /// Redefines the maximum user-accessible address space (48-bit) + SetMaxAddressExt = 0x37, - /// SASI rev. 0a - FormatDriveErrorMap = 0xC4, + /// Writes sectors using DMA transfer, retrying on error (48-bit) + WriteDmaExt = 0x35, - /// SASI rev. 0a - ReadErrorMap = 0xC5, + /// Queues a write of sectors (48-bit) + WriteDmaQueuedExt = 0x36, - /// SASI rev. 0a - ReadDriveType = 0xC6, - #endregion SASI Class 6 commands + /// Writes sectors using PIO transfer, retrying on error (48-bit) + WriteExt = 0x34, - #region SASI Class 7 commands - /// SASI rev. 0a - RamDiagnostic = 0xE0, + /// Writes data to the indicated log (48-bit) + WriteLogExt = 0x3F, - /// SASI rev. 0a - WriteEcc = 0xE1, + /// Writes several sectors at once setting interrupts on end of block (48-bit) + WriteMultipleExt = 0x39, + #endregion Commands defined on ATA/ATAPI-6 rev. 3b - /// SASI rev. 0a - ReadId = 0xE2, + #region Commands defined on ATA/ATAPI-7 rev. 4b + /// Configures the operating parameters for a stream + ConfigureStream = 0x51, - /// SASI rev. 0a - DriveDiagnostic = 0xE3, + /// Reads data on an allotted time using DMA + ReadStreamDmaExt = 0x2A, - /// Found on a vendor source code - ControllerDiagnostic = 0xE4, + /// Reads data on an allotted time using PIO + ReadStreamExt = 0x2B, - /// Found on a vendor document - ReadLong = 0xE5, + /// Writes data on an allotted time using DMA + WriteStreamDmaExt = 0x3A, - /// Found on a vendor document - WriteLong = 0xE6 - #endregion SASI Class 7 commands - } - #endregion SASI Commands + /// Writes data on an allotted time using PIO + WriteStreamExt = 0x3B, + #endregion Commands defined on ATA/ATAPI-7 rev. 4b - #region SCSI Commands - /// All known SCSI and ATAPI commands - public enum ScsiCommands : byte - { - #region SCSI Primary Commands (SPC) - /// Commands used to obtain information about the access controls that are active SPC-4 rev. 16 - AccessControlIn = 0x86, + #region Commands defined on ATA/ATAPI-8 rev. 3f + /// Sends a Non Volatile Cache subcommand. + NonVolatileCacheCommand = 0xB6, - /// Commands used to limit or grant access to LUNs SPC-4 rev. 16 - AccessControlOut = 0x87, + /// Retrieves security protocol information or the results from commands + TrustedReceive = 0x5C, - /// Modifies the operating definition of the device with respect to commands. SCSI-2 X3T9.2/375R rev. 10l - ChangeDefinition = 0x40, + /// + /// Retrieves security protocol information or the results from commands, using DMA + /// transfers + /// + TrustedReceiveDma = 0x5D, - /// Compares data between two devices ECMA-111 (SCSI-1) - Compare = 0x39, + /// Sends one or more Security Protocol commands + TrustedSend = 0x5E, - /// Copies data between two devices ECMA-111 (SCSI-1) - Copy = 0x18, + /// Sends one or more Security Protocol commands, using DMA transfers + TrustedSendDma = 0x5F, - /// Copies data between two devices and verifies the copy is correct. ECMA-111 (SCSI-1) - CopyAndVerify = 0x3A, + /// Writes sectors using DMA transfer, retrying on error (48-bit), not returning until the operation is complete + WriteDmaFuaExt = 0x3D, - /// Copies data between two devices SPC-2 rev. 20 - ExtendedCopy = 0x83, + /// Queues a write of sectors (48-bit), not returning until the operation is complete + WriteDmaQueuedFuaExt = 0x3E, - /// Requests information about the device ECMA-111 (SCSI-1) - Inquiry = SasiCommands.Inquiry, + /// + /// Writes several sectors at once setting interrupts on end of block (48-bit), not returning until the operation + /// is complete + /// + WriteMultipleFuaExt = 0xCE, - /// Manages device statistics SCSI-2 X3T9.2/375R rev. 10l - LogSelect = 0x4C, + /// Writes a sector that will give an uncorrectable error on any read operation + WriteUncorrectableExt = 0x45, + #endregion Commands defined on ATA/ATAPI-8 rev. 3f - /// Gets device statistics SCSI-2 X3T9.2/375R rev. 10l - LogSense = 0x4D, + #region Commands defined on ATA/ATAPI Command Set 2 (ACS-2) rev. 2 + /// Provides information for device optimization In SSDs, this contains trimming + DataSetManagement = 0x06, - /// Retrieves management protocol information SPC-2 rev. 20 - ManagementProtocolIn = 0xA3, + /// Alters the device microcode, using DMA transfers + DownloadMicrocodeDma = 0x93, - /// Transfers management protocol information SPC-2 rev. 20 - ManagementProtocolOut = 0xA4, + /// Reads the content of the drive's buffer, using DMA transfers + ReadBufferDma = 0xE9, - /// Sets device parameters ECMA-111 (SCSI-1) - ModeSelect = 0x15, + /// Reads sectors using NCQ + ReadFpDmaQueued = 0x60, - /// Sets device parameters SCSI-2 X3T9.2/375R rev. 10l - ModeSelect10 = 0x55, + /// Returns the indicated log to the host (48-bit) + ReadLogDmaExt = 0x47, - /// Gets device parameters ECMA-111 (SCSI-1) - ModeSense = 0x1A, + /// Requests SPC-4 style error data + RequestSenseDataExt = 0x0B, SanitizeCommands = 0xB4, - /// Gets device parameters SCSI-2 X3T9.2/375R rev. 10l - ModeSense10 = 0x5A, + /// Executes a Security Protocol command that does not require a transfer of data + TrustedNonData = 0x5B, - /// Obtains information about persistent reservations and reservation keys SPC-1 rev. 10 - PersistentReserveIn = 0x5E, + /// Writes data to the drive's sector buffer, using DMA transfers + WriteBufferDma = 0xE8, - /// Reserves a LUN or an extent within a LUN for exclusive or shared use SPC-1 rev. 10 - PersistentReserveOut = 0x5F, + /// Writes sectors using NCQ + WriteFpDmaQueued = 0x61, + #endregion Commands defined on ATA/ATAPI Command Set 2 (ACS-2) rev. 2 - /// Requests the device to disable or enable the removal of the medium inside it ECMA-111 (SCSI-1) - PreventAllowMediumRemoval = 0x1E, + #region Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 + /// Sends + NcqQueueManagement = 0x63, - /// Reads attribute values from medium auxiliary memory SPC-3 rev. 21b - ReadAttribute = 0x8C, + /// Sets the device date and time + SetDateAndTimeExt = 0x77, + #endregion Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 - /// Reads the device buffer SCSI-2 X3T9.2/375R rev. 10l - ReadBuffer = 0x3C, + #region Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 6 + NativeMaxAddress = 0x78 + #endregion Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 6 +} +#endregion ATA Commands + +#region ATA SMART SubCommands +/// All known ATA SMART sub-commands +public enum AtaSmartSubCommands : byte +{ + #region Commands defined on ATA-3 rev. 7b + /// Disables all SMART capabilities + Disable = 0xD9, - /// Reads the media serial number SPC-3 rev. 21b - ReadSerialNumber = 0xAB, + /// Enables/disables SMART attribute autosaving + EnableDisableAttributeAutosave = 0xD2, - /// Receives information about a previous or current SPC-2 rev. 20 - ReceiveCopyResults = 0x84, + /// Enables all SMART capabilities + Enable = 0xD8, - /// Requests the data after completion of a ECMA-111 (SCSI-1) - ReceiveDiagnostic = SasiCommands.ReadDiagnostic, + /// Returns the device's SMART attributes thresholds + ReadAttributeThresholds = 0xD1, + + /// Returns the device's SMART attributes values + ReadAttributeValues = 0xD0, + + /// Communicates device reliability status + ReturnStatus = 0xDA, + + /// Saves any attribute values immediately + SaveAttributeValues = 0xD3, + #endregion Commands defined on ATA-3 rev. 7b + + #region Commands defined on ATA/ATAPI-4 rev. 18 + /// Causes the device to immediately initiate a SMART data collection and saves it to the device + ExecuteOfflineImmediate = 0xD4, + + /// Returns the device's SMART attributes values + ReadData = ReadAttributeValues, + #endregion Commands defined on ATA/ATAPI-4 rev. 18 + + #region Commands defined on ATA/ATAPI-5 rev. 3 + /// Returns the indicated log to the host + ReadLog = 0xD5, + + /// Writes data to the indicated log + WriteLog = 0xD6 + #endregion Commands defined on ATA/ATAPI-5 rev. 3 +} +#endregion ATA SMART SubCommands + +#region ATA Device Configuration Overlay SubCommands +/// All known ATA DEVICE CONFIGURATION sub-commands +public enum AtaDeviceConfigurationSubCommands : byte +{ + #region Commands defined on ATA/ATAPI-6 rev. 3b + /// Disables any change made by + Restore = 0xC0, + + /// Prevents any from working until a power down cycle. + FreezeLock = 0xC1, + + /// Indicates the selectable commands, modes, and feature sets the device supports + Identify = 0xC2, + + /// Modifies the commands, modes and features sets the device will obey to + Set = 0xC3 + #endregion Commands defined on ATA/ATAPI-6 rev. 3b +} +#endregion ATA Device Configuration Overlay SubCommands + +#region ATA SET MAX SubCommands +/// All known ATA SET MAX sub-commands +public enum AtaSetMaxSubCommands : byte +{ + #region Commands defined on ATA/ATAPI-6 rev. 3b + /// Redefines the maximum user-accessible address space + Address = 0x00, + + /// Disables any other until power cycle + FreezeLock = 0x04, + + /// + /// Disables any other except and + /// until power cycle + /// + Lock = 0x02, + + /// Sets the device password + SetPassword = 0x01, + + /// Disables + UnLock = 0x03, + #endregion Commands defined on ATA/ATAPI-6 rev. 3b +} +#endregion ATA SET MAX SubCommands + +#region ATA Non Volatile Cache SubCommands +/// All known ATA NV CACHE sub-commands +public enum AtaNonVolatileCacheSubCommands : byte +{ + #region Commands defined on ATA/ATAPI-8 rev. 3f + /// Adds the specified LBA to the Non Volatile Cache + AddLbaToNvCache = 0x10, + + /// Ensures there is enough free space in the Non Volatile Cache + FlushNvCache = 0x14, + + /// Requests a list of LBAs actually stored in the Non Volatile Cache + QueryNvCachePinnedSet = 0x12, + + /// Requests a list of LBAs accessed but not in the Non Volatile Cache + QueryNvCacheMisses = 0x13, + + /// Removes the specified LBA from the Non Volatile Cache Pinned Set + RemoveLbaFromNvCache = 0x11, + + /// Disables the Non Volatile Cache Power Mode + ReturnFromNvCachePowerMode = 0x01, + + /// + /// Enables the Non Volatile Cache Power Mode, so the device tries to serve all accesses from the Non Volatile + /// Cache + /// + SetNvCachePowerMode = 0x00 + #endregion Commands defined on ATA/ATAPI-8 rev. 3f +} +#endregion ATA Non Volatile Cache SubCommands + +#region ATA Sanitize SubCommands +/// All known ATA SANITIZE sub-commands +public enum AtaSanitizeSubCommands : ushort +{ + #region Commands defined on ATA/ATAPI Command Set 2 (ACS-2) rev. 2 + /// Causes a block erase on all user data + BlockEraseExt = 0x0012, + + /// Changes the internal encryption keys. Renders user data unusable + CryptoScrambleExt = 0x0011, + + /// Fills user data with specified pattern + OverwriteExt = 0x0014, + + /// Disables all except + FreezeLockExt = 0x0020, + + /// Gets the status of the sanitizing + Status = 0x0000, + #endregion Commands defined on ATA/ATAPI Command Set 2 (ACS-2) rev. 2 + + #region Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 + /// Disables the command + AntiFreezeLockExt = 0x0040 + #endregion Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 +} +#endregion ATA Sanitize SubCommands - /// Releases a previously reserved LUN or extents ECMA-111 (SCSI-1) - Release = SasiCommands.ReleaseUnit, +#region ATA NCQ Queue Management SubCommands +/// All known ATA NCQ QUEUE MANAGEMENT sub-commands +public enum AtaNcqQueueManagementSubcommands : byte +{ + #region Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 + /// Aborts pending NCQ commands + AbortNcqQueue = 0x00, - /// Releases a previously reserved LUN or extents SPC-1 rev. 10 - Release10 = 0x57, + /// Controls how NCQ Streaming commands are processed by the device + DeadlineHandling = 0x01, + #endregion Commands defined on ATA/ATAPI Command Set 3 (ACS-3) rev. 5 +} +#endregion ATA NCQ Queue Management SubCommands - /// Requests the LUNs that are present on the device SPC-1 rev. 10 - ReportLuns = 0xA0, +/// +/// All known SASI commands Commands 0x00 to 0x1F are 6-byte Commands 0x20 to 0x3F are 10-byte Commands 0x40 to +/// 0x5F are 8-byte Commands 0xA0 to 0xBF are 12-byte +/// +#region SASI Commands +public enum SasiCommands : byte +{ + #region SASI Class 0 commands + /// Returns zero status if requested unit is on and ready. SASI rev. 0a + TestUnitReady = 0x00, - /// Requests the device's sense ECMA-111 (SCSI-1) - RequestSense = SasiCommands.RequestSense, + /// Sets the unit to a specific known state. SASI rev. 0a + RezeroUnit = 0x01, - /// Reserves a LUN or extent ECMA-111 (SCSI-1) - Reserve = SasiCommands.ReserveUnit, + /// Unknown SASI rev. 0a + RequestSyndrome = 0x02, - /// Reserves a LUN or extent SPC-1 rev. 10 - Reserve10 = 0x56, + /// Returns unit sense. SASI rev. 0a + RequestSense = 0x03, - /// Retrieves security protocol information SPC-4 rev. 16 - SecurityProtocolIn = 0xA2, + /// Formats the entire media. SASI rev. 0a + FormatUnit = 0x04, - /// Transfers security protocol information SPC-4 rev. 16 - SecurityProtocolOut = 0xB5, + /// Unknown SASI rev. 0a + CheckTrackFormat = 0x05, - /// Requests the device to perform diagnostics ECMA-111 (SCSI-1) - SendDiagnostic = SasiCommands.WriteDiagnostic, + /// Unknown SASI rev. 0a + FormatTrack = 0x06, - /// Extended commands SPC-4 - ServiceActionIn = 0x9E, + /// Unknown SASI rev. 0a + FormatBadTrack = 0x06, - /// Extended commands SPC-4 - ServiceActionOut = 0x9F, + /// Reads a block from the device. SASI rev. 0a + Read = 0x08, - /// Checks if a LUN is ready to access its medium ECMA-111 (SCSI-1) - TestUnitReady = SasiCommands.TestUnitReady, + /// SASI rev. 0a Unknown + WriteProtectSector = 0x09, - /// Writes attribute values to medium auxiliary memory SPC-3 rev. 21b - WriteAttribute = 0x8D, + /// Writes a block to the device. SASI rev. 0a + Write = 0x0A, - /// Writes to the device's buffer SCSI-2 X3T9.2/375R rev. 10l - WriteBuffer = 0x3B, - #endregion SCSI Primary Commands (SPC) + /// Moves the device reading mechanism to the specified block. SASI rev. 0a + Seek = 0x0B, - #region SCSI Block Commands (SBC) - /// Compares blocks with sent data, and if equal, writes those block to device, atomically SBC-3 rev. 25 - CompareAndWrite = 0x89, + /// Found on a vendor source code + InitDriveCharacteristics = 0x0C, - /// Formats the medium into addressable logical blocks ECMA-111 (SCSI-1) - FormatUnit = SasiCommands.FormatUnit, FormatWithPreset = 0x38, + /// Unknown SASI rev. 0a + VerifyRestore = 0x0D, - /// Locks blocks from eviction of device's cache SCSI-2 X3T9.2/375R rev. 10l - LockUnlockCache = 0x36, + /// Unknown SASI rev. 0a + AssignAlternateDiskTrack = 0x0E, - /// Locks blocks from eviction of device's cache SBC-2 rev. 4 - LockUnlockCache16 = 0x92, + /// Writes a File Mark on the device. SASI rev. 0c + WriteFileMark = 0x0F, - /// - /// Requests the device to perform the following uninterrupted series of actions: 1.- Read the specified blocks - /// 2.- Transfer blocks from the data out buffer 3.- Perform an OR operation between the read blocks and the buffer 4.- - /// Write the buffer to the blocks SBC-3 rev. 16 - /// - OrWrite = 0x8B, + /// Reserves the device for use by the initiator. SASI rev. 0a + ReserveUnitOld = 0x12, - /// Transfers requested blocks to devices' cache SCSI-2 X3T9.2/375R rev. 10l - PreFetch = 0x34, + /// Gets information about a device ANSI X3T9.3 No. 185 (SASI) + Inquiry = 0x12, - /// Transfers requested blocks to devices' cache SBC-3 rev. 16 - PreFetch16 = 0x90, + /// Release the device from the reservation. SASI rev. 0a + ReleaseUnitOld = 0x13, - /// Reads blocks from device ECMA-111 (SCSI-1) - Read = SasiCommands.Read, + /// Unknown SASI rev. 0a + WriteProtectDrive = 0x14, - /// Reads blocks from device ECMA-111 (SCSI-1) - Read10 = SasiCommands.ExtendedAddressRead, + /// Writes and verifies blocks to the device. SASI rev. 0c + WriteAndVerifyOld = 0x14, - /// Reads blocks from device SBC-2 rev. 4 - Read16 = 0x88, + /// Unknown SASI rev. 0a + ReleaseWriteProtect = 0x15, - /// Gets device capacity ECMA-111 (SCSI-1) - ReadCapacity = 0x25, + /// Verifies blocks. SASI rev. 0c + VerifyOld = 0x15, - /// Gets device's defect data SCSI-2 X3T9.2/375R rev. 10l - ReadDefectData = 0x37, + /// Unknown SASI rev. 0a + ReadNoSeek = 0x16, - /// - /// Reads blocks from device in a vendor-specific way that should include the ECC alongside the data SCSI-2 - /// X3T9.2/375R rev. 10l - /// - ReadLong = 0x3E, + /// Gets the number of blocks in device. SASI rev. 0c + ReadCapacityOld = 0x16, - /// Requests the device to reassign the defective blocks to another area of the medium ECMA-111 (SCSI-1) - ReassignBlocks = 0x07, + /// Reserves the device for use by the initiator. ANSI X3T9.3 No. 185 (SASI) + ReserveUnit = 0x16, - /// Requests the target write to the medium the XOR data generated from the specified source devices SBC-1 rev. 8c - Rebuild = 0x81, + /// Release the device from the reservation. ANSI X3T9.3 No. 185 (SASI) + ReleaseUnit = 0x17, - /// - /// Requests the target write to the buffer the XOR data from its own medium and the specified source devices - /// SBC-1 rev. 8c - /// - Regenerate = 0x82, + /// Searches data on blocks SASI rev. 0a + SearchDataEqualOld = 0x17, - /// Requests the device to set the LUN in a vendor specific state ECMA-111 (SCSI-1) - RezeroUnit = SasiCommands.RezeroUnit, Sanitize = 0x48, + /// Searches data on blocks using major than or equal comparison SASI rev. 0a + SearchDataHighOld = 0x18, - /// Searches data on blocks ECMA-111 (SCSI-1) - SearchDataEqual = SasiCommands.SearchDataEqual, + /// Searches data on blocks using minor than or equal comparison SASI rev. 0a + SearchDataLowOld = 0x19, - /// Searches data on blocks using major than or equal comparison ECMA-111 (SCSI-1) - SearchDataHigh = SasiCommands.SearchDataHigh, + /// Reads analysis data from a device SASI rev. 0a + ReadDiagnosticOld = 0x1A, - /// Searches data on blocks using minor than or equal comparison ECMA-111 (SCSI-1) - SearchDataLow = SasiCommands.SearchDataLow, + /// Unknown SASI rev. 0a + VerifyData = 0x1B, - /// Requests the device to seek to a specified blocks ECMA-111 (SCSI-1) - Seek = SasiCommands.Seek, + /// Requests a device to run a diagnostic SASI rev. 0c + WriteDiagnosticOld = 0x1B, - /// Requests the device to seek to a specified blocks ECMA-111 (SCSI-1) - Seek10 = 0x2B, + /// Requests the data after completion of a ANSI X3T9.3 No. 185 (SASI) + ReadDiagnostic = 0x1C, - /// Defines the range within which subsequent linked commands may operate ECMA-111 (SCSI-1) - SetLimits = 0x33, + /// Requests the device to perform diagnostics ANSI X3T9.3 No. 185 (SASI) + WriteDiagnostic = 0x1D, - /// Requests the device to enable or disable the LUN for media access operations ECMA-111 (SCSI-1) - StartStopUnit = 0x1B, + /// Gets information about a device SASI rev. 0c + InquiryOld = 0x1F, + #endregion SASI Class 0 commands - /// Ensures that the blocks in the cache are written to the medium SCSI-2 X3T9.2/375R rev. 10l - SynchronizeCache = 0x35, + #region SASI Class 1 commands + /// SASI rev. 0a Unknown + Copy = 0x20, - /// Ensures that the blocks in the cache are written to the medium SBC-2 rev. 4 - SynchronizeCache16 = 0x91, + /// SASI rev. 0a Unknown + Restore = 0x21, - /// Unmaps one or more LBAs In SSDs, this is trimming SBC-3 rev. 25 - Unmap = 0x42, + /// SASI rev. 0a Unknown + Backup = 0x22, - /// Verifies blocks on the device ECMA-111 (SCSI-1) - Verify10 = SasiCommands.Verify, + /// SASI rev. 0a Unknown + SetBlockLimitsOlder = 0x26, - /// Verifies blocks on the device SBC-2 rev. 4 - Verify16 = 0x8F, + /// Sets write or read limits from a specified block SASI rev. 0c + SetBlockLimitsOld = 0x28, - /// Writes blocks to the device ECMA-111 (SCSI-1) - Write = SasiCommands.Write, + /// Reads blocks from device ANSI X3T9.3 No. 185 (SASI) + ExtendedAddressRead = 0x28, - /// Writes blocks to the device ECMA-111 (SCSI-1) - Write10 = SasiCommands.ExtendedAddressWrite, + /// Writes blocks to the device ANSI X3T9.3 No. 185 (SASI) + ExtendedAddressWrite = 0x2A, - /// Writes blocks to the device SBC-2 rev. 4 - Write16 = 0x8A, + /// Writes blocks to the device and then verifies them ANSI X3T9.3 No. 185 (SASI) + WriteAndVerify = 0x2E, - /// Writes blocks to the device and then verifies them ECMA-111 (SCSI-1) - WriteAndVerify = SasiCommands.WriteAndVerify, + /// Verifies blocks on the device ANSI X3T9.3 No. 185 (SASI) + Verify = 0x2F, - /// Writes blocks to the device and then verifies them SBC-2 rev. 4 - WriteAndVerify16 = 0x8E, + /// Searches data on blocks ANSI X3T9.3 No. 185 (SASI) + SearchDataEqual = 0x31, - /// - /// Writes blocks to the device with a vendor specified format that shall include the ECC alongside the data - /// SCSI-2 X3T9.2/375R rev. 10l - /// - WriteLong = 0x3F, + /// Searches data on blocks using major than or equal comparison ANSI X3T9.3 No. 185 (SASI) + SearchDataHigh = 0x30, - /// Writes a single block several times SCSI-2 X3T9.2/375R rev. 10l - WriteSame = 0x41, + /// Searches data on blocks using minor than or equal comparison ANSI X3T9.3 No. 185 (SASI) + SearchDataLow = 0x32, + #endregion SASI Class 1 commands - /// Writes a single block several times SBC-2 rev. 4 - WriteSame16 = 0x93, + #region SASI Class 2 commands + /// Unknown SASI rev. 0a + Load = 0x40, - /// Requests XOR data generated by an or command SBC-1 rev. 8c - XdRead = 0x52, + /// Unknown SASI rev. 0a + Unload = 0x41, - /// - /// XORs the data sent with data on the medium and stores it until an is issued SBC-1 rev. - /// 8c - /// - XdWrite = 0x50, + /// Unknown SASI rev. 0a + Rewind = 0x42, - /// - /// XORs the data sent with data on the medium and stores it until an is issued SBC-1 rev. - /// 8c - /// - XdWrite16 = 0x80, + /// Unknown SASI rev. 0a + SpaceForward = 0x43, - /// Requests the target to XOR the sent data with the data on the medium and return the results - XdWriteRead = 0x53, + /// Unknown SASI rev. 0a + SpaceForwardFileMark = 0x44, - /// - /// Requests the target to XOR the data transferred with the data on the medium and writes it to the medium SBC-1 - /// rev. 8c - /// - XpWrite = 0x51, - #endregion SCSI Block Commands (SBC) + /// Unknown SASI rev. 0a + SpaceReverse = 0x45, - #region SCSI Streaming Commands (SSC) - /// Prepares the medium for use by the LUN SSC-1 rev. 22 - FormatMedium = 0x04, + /// Unknown SASI rev. 0a + SpaceReverseFileMark = 0x46, - /// Erases part of all of the medium from the current position ECMA-111 (SCSI-1) - Erase = 0x19, + /// Unknown SASI rev. 0a + TrackSelect = 0x47, - /// Enables or disables the LUN for further operations ECMA-111 (SCSI-1) - LoadUnload = 0x1B, + /// Reads blocks from device SASI rev. 0a + Read8 = 0x48, - /// Positions the LUN to a specified block in a specified partition SCSI-2 X3T9.2/375R rev. 10l - Locate = 0x2B, + /// Unknown SASI rev. 0a + ReadVerify = 0x49, - /// Positions the LUN to a specified block in a specified partition SSC-2 rev. 09 - Locate16 = 0x92, + /// Unknown SASI rev. 0a + ReadDiagnosticClass2 = 0x4A, - /// Requests the block length limits capability ECMA-111 (SCSI-1) - ReadBlockLimits = 0x05, + /// Writes blocks to device SASI rev. 0a + Write8 = 0x4B, - /// Reads the current position SCSI-2 X3T9.2/375R rev. 10l - ReadPosition = 0x34, + /// Unknown SASI rev. 0a + WriteFileMarkClass2 = 0x4C, - /// Reads blocks from the device, in reverse order ECMA-111 (SCSI-1) - ReadReverse = 0x0F, + /// Unknown SASI rev. 0a + WriteExtended = 0x4D, - /// - /// Retrieves data from the device buffer that has not been successfully written to the medium (or printed) - /// ECMA-111 (SCSI-1) - /// - RecoverBufferedData = 0x14, + /// Unknown SASI rev. 0a + WriteExtendedFileMark = 0x4E, - /// Requests information regarding the supported densities for the logical unit SSC-1 rev. 22 - ReportDensitySupport = 0x44, + /// Unknown SASI rev. 0a + WriteErase = 0x4F, - /// Seeks the medium to the beginning of partition in current partition ECMA-111 (SCSI-1) - Rewind = 0x01, + /// Writes and verifies blocks to the device. SASI rev. 0c + WriteVerify8 = 0x54, - /// A variety of positioning functions ECMA-111 (SCSI-1) - Space = 0x11, + /// Verifies blocks. SASI rev. 0c + Verify8 = 0x55, - /// A variety of positioning functions SSC-2 rev. 09 - Space16 = 0x91, + /// Searches data on blocks using major than or equal comparison SASI rev. 0c + SearchDataHigh8 = 0x57, - /// Selects the specified track ECMA-111 (SCSI-1) - TrackSelect = 0x0B, + /// Searches data on blocks SASI rev. 0c + SearchDataEqual8 = 0x58, - /// Verifies one or more blocks from the next one ECMA-111 (SCSI-1) - Verify = 0x13, + /// Searches data on blocks using minor than or equal comparison SASI rev. 0c + SearchDataLow8 = 0x59, + #endregion SASI Class 2 commands - /// Writes the specified number of filemarks or setmarks in the current position ECMA-111 (SCSI-1) - WriteFileMarks = 0x10, - #endregion SCSI Streaming Commands (SSC) + #region SASI Class 3 commands + /// SASI rev. 0a + Skip = 0x60, - #region SCSI Streaming Commands for Printers (SSC) - /// - /// Assures that the data in the buffer has been printed, or, for other devices, written to media ECMA-111 - /// (SCSI-1) - /// - FlushBuffer = 0x10, + /// SASI rev. 0a + Space = 0x61, - /// Specifies forms or fronts ECMA-111 (SCSI-1) - Format = 0x04, + /// SASI rev. 0a + Return = 0x62, - /// Transfers data to be printed ECMA-111 (SCSI-1) - Print = 0x0A, + /// SASI rev. 0a + Tab = 0x63, - /// Transfers data to be printed with a slew value ECMA-111 (SCSI-1) - SlewAndPrint = 0x0B, + /// SASI rev. 0a + ReadControl = 0x64, - /// Halts printing ECMA-111 (SCSI-1) - StopPrint = 0x1B, + /// SASI rev. 0a + Write3 = 0x65, - /// - /// Assures that the data in the buffer has been printed, or, for other devices, written to media SCSI-2 - /// X3T9.2/375R rev. 10l - /// - SynchronizeBuffer = FlushBuffer, - #endregion SCSI Streaming Commands for Printers (SSC) + /// SASI rev. 0a + WriteControl = 0x66, + #endregion SASI Class 3 commands - #region SCSI Processor Commands - /// Transfers data from the device ECMA-111 (SCSI-1) - Receive = 0x08, + #region SASI Class 5 commands + /// Gets the number of blocks in device. ANSI X3T9.3 No. 185 (SASI) + ReadCapacity = 0xA5, - /// Sends data to the device ECMA-111 (SCSI-1) - Send = 0x0A, - #endregion SCSI Processor Commands + /// Sets write or read limits from a specified block ANSI X3T9.3 No. 185 (SASI) + SetBlockLimits = 0xA9, + #endregion SASI Class 5 commands - #region SCSI Multimedia Commands (MMC) - /// Erases any part of a CD-RW MMC-1 rev. 9 - Blank = 0xA1, + #region SASI Class 6 commands + /// SASI rev. 0a + DefineFloppyDiskTrackFormat = 0xC0, - /// Closes a track or session MMC-1 rev. 9 - CloseTrackSession = 0x5B, + /// Unknown vendor command in X68000 + Specify = 0xC2, - /// - /// Gets information about the overall capabilities of the device and the current capabilities of the device MMC-2 - /// rev. 11a - /// - GetConfiguration = 0x46, + /// SASI rev. 0a + FormatDriveErrorMap = 0xC4, - /// Requests the LUN to report events and statuses MMC-2 rev. 11a - GetEventStatusNotification = 0x4A, + /// SASI rev. 0a + ReadErrorMap = 0xC5, - /// Provides a method to profile the performance of the drive MMC-2 rev. 11a - GetPerformance = 0xAC, + /// SASI rev. 0a + ReadDriveType = 0xC6, + #endregion SASI Class 6 commands - /// Requests the device changer to load or unload a disc MMC-1 rev. 9 - LoadUnloadCd = 0xA6, + #region SASI Class 7 commands + /// SASI rev. 0a + RamDiagnostic = 0xE0, - /// Requests the device changer to load or unload a disc MMC-2 rev. 11a - LoadUnloadMedium = 0xA6, + /// SASI rev. 0a + WriteEcc = 0xE1, - /// Requests information about the current status of the CD device, including any changer mechanism MMC-1 rev. 9 - MechanicalStatus = 0xBD, + /// SASI rev. 0a + ReadId = 0xE2, - /// Requests the device to start or stop an audio play operation SCSI-2 X3T9.2/375R rev. 10l - PauseResume = 0x4B, + /// SASI rev. 0a + DriveDiagnostic = 0xE3, - /// Begins an audio playback SCSI-2 X3T9.2/375R rev. 10l - PlayAudio = 0x45, + /// Found on a vendor source code + ControllerDiagnostic = 0xE4, - /// Begins an audio playback SCSI-2 X3T9.2/375R rev. 10l - PlayAudio12 = 0xA5, + /// Found on a vendor document + ReadLong = 0xE5, - /// Begins an audio playback using MSF addressing SCSI-2 X3T9.2/375R rev. 10l - PlayAudioMsf = 0x47, + /// Found on a vendor document + WriteLong = 0xE6 + #endregion SASI Class 7 commands +} +#endregion SASI Commands - /// Begins an audio playback from the specified index of the specified track SCSI-2 X3T9.2/375R rev. 10l - PlayAudioTrackIndex = 0x48, +#region SCSI Commands +/// All known SCSI and ATAPI commands +public enum ScsiCommands : byte +{ + #region SCSI Primary Commands (SPC) + /// Commands used to obtain information about the access controls that are active SPC-4 rev. 16 + AccessControlIn = 0x86, - /// Begins an audio playback from the position relative of a track SCSI-2 X3T9.2/375R rev. 10l - PlayTrackRelative = 0x49, + /// Commands used to limit or grant access to LUNs SPC-4 rev. 16 + AccessControlOut = 0x87, - /// Begins an audio playback from the position relative of a track SCSI-2 X3T9.2/375R rev. 10l - PlayTrackRelative12 = 0xA9, + /// Modifies the operating definition of the device with respect to commands. SCSI-2 X3T9.2/375R rev. 10l + ChangeDefinition = 0x40, - /// Reports the total and blank area of the device buffer MMC-1 rev. 9 - ReadBufferCapacity = 0x5C, + /// Compares data between two devices ECMA-111 (SCSI-1) + Compare = 0x39, - /// Reads a block from a CD with any of the requested CD data streams MMC-1 rev. 9 - ReadCd = 0xBE, + /// Copies data between two devices ECMA-111 (SCSI-1) + Copy = 0x18, - /// Reads a block from a CD with any of the requested CD data streams using MSF addressing MMC-1 rev. 9 - ReadCdMsf = 0xB9, + /// Copies data between two devices and verifies the copy is correct. ECMA-111 (SCSI-1) + CopyAndVerify = 0x3A, - /// Returns the recorded size of the CD MMC-1 rev. 9 - ReadCdRecordedCapacity = 0x25, + /// Copies data between two devices SPC-2 rev. 20 + ExtendedCopy = 0x83, - /// Gets information about all discs: CD-ROM, CD-R and CD-RW MMC-1 rev. 9 - ReadDiscInformation = 0x51, + /// Requests information about the device ECMA-111 (SCSI-1) + Inquiry = SasiCommands.Inquiry, - /// Reads areas from the DVD or BD media MMC-5 rev. 2c - ReadDiscStructure = 0xAD, + /// Manages device statistics SCSI-2 X3T9.2/375R rev. 10l + LogSelect = 0x4C, - /// Reads areas from the DVD media MMC-2 rev. 11a - ReadDvdStructure = 0xAD, + /// Gets device statistics SCSI-2 X3T9.2/375R rev. 10l + LogSense = 0x4D, - /// Requests a list of the possible format capacities for an installed random-writable media MMC-2 rev. 11a - ReadFormatCapacities = 0x23, + /// Retrieves management protocol information SPC-2 rev. 20 + ManagementProtocolIn = 0xA3, - /// Reads the data block header of the specified CD-ROM block SCSI-2 X3T9.2/375R rev. 10l - ReadHeader = 0x44, + /// Transfers management protocol information SPC-2 rev. 20 + ManagementProtocolOut = 0xA4, - /// Reads the mastering information from a Master CD. MMC-1 rev. 9 - ReadMasterCue = 0x59, + /// Sets device parameters ECMA-111 (SCSI-1) + ModeSelect = 0x15, - /// Requests the Q subchannel and the current audio playback status SCSI-2 X3T9.2/375R rev. 10l - ReadSubChannel = 0x42, + /// Sets device parameters SCSI-2 X3T9.2/375R rev. 10l + ModeSelect10 = 0x55, - /// Requests the medium TOC, PMA or ATIP from the device SCSI-2 X3T9.2/375R rev. 10l - ReadTocPmaAtip = 0x43, + /// Gets device parameters ECMA-111 (SCSI-1) + ModeSense = 0x1A, - /// Gets information about a track regardless of its status MMC-1 rev. 9 - ReadTrackInformation = 0x52, + /// Gets device parameters SCSI-2 X3T9.2/375R rev. 10l + ModeSense10 = 0x5A, - /// Repairs an incomplete ECC block at the end of an RZone Mt. Fuji ver. 7 rev. 1.21 - RepairRZone = 0x58, + /// Obtains information about persistent reservations and reservation keys SPC-1 rev. 10 + PersistentReserveIn = 0x5E, - /// Repairs an incomplete packet at the end of a packet writing track MMC-1 rev. 9 - RepairTrack = 0x58, + /// Reserves a LUN or an extent within a LUN for exclusive or shared use SPC-1 rev. 10 + PersistentReserveOut = 0x5F, - /// - /// Requests the start of the authentication process and provides data necessary for authentication and for - /// generating a Bus Key MMC-2 rev. 11a - /// - ReportKey = 0xA4, + /// Requests the device to disable or enable the removal of the medium inside it ECMA-111 (SCSI-1) + PreventAllowMediumRemoval = 0x1E, - /// Reserves disc space for a track MMC-1 rev. 9 - ReserveTrack = 0x53, + /// Reads attribute values from medium auxiliary memory SPC-3 rev. 21b + ReadAttribute = 0x8C, - /// - /// Fast-forwards or fast-reverses the audio playback to the specified block. Stops if it encounters a data track - /// MMC-1 rev. 9 - /// - ScanMmc = 0xBA, + /// Reads the device buffer SCSI-2 X3T9.2/375R rev. 10l + ReadBuffer = 0x3C, - /// Sends a cue sheet for session-at-once recording MMC-1 rev. 9 - SendCueSheet = 0x5D, + /// Reads the media serial number SPC-3 rev. 21b + ReadSerialNumber = 0xAB, - /// Transfer a DVD or BD structure for media writing MMC-5 rev. 2c - SendDiscStructure = 0xAD, + /// Receives information about a previous or current SPC-2 rev. 20 + ReceiveCopyResults = 0x84, - /// Transfer a DVD structure for media writing MMC-2 rev. 11a - SendDvdStructure = 0xAD, + /// Requests the data after completion of a ECMA-111 (SCSI-1) + ReceiveDiagnostic = SasiCommands.ReadDiagnostic, - /// Requests the LUN to process an event MMC-2 rev. 11a - SendEvent = 0xA2, + /// Releases a previously reserved LUN or extents ECMA-111 (SCSI-1) + Release = SasiCommands.ReleaseUnit, - /// Provides data necessary for authentication and for generating a Bus Key MMC-2 rev. 11a - SendKey = 0xA3, + /// Releases a previously reserved LUN or extents SPC-1 rev. 10 + Release10 = 0x57, - /// Restores the Optimum Power Calibration values to the drive for a specific disc MMC-1 rev. 9 - SendOpcInformation = 0x54, + /// Requests the LUNs that are present on the device SPC-1 rev. 10 + ReportLuns = 0xA0, - /// Sets the spindle speed to be used while reading/writing data to a CD MMC-1 rev. 9 - SetCdRomSpeed = 0xBB, + /// Requests the device's sense ECMA-111 (SCSI-1) + RequestSense = SasiCommands.RequestSense, - /// Requests the LUN to perform read ahead caching operations from the specified block MMC-2 rev. 11a - SetReadAhead = 0xA7, + /// Reserves a LUN or extent ECMA-111 (SCSI-1) + Reserve = SasiCommands.ReserveUnit, - /// Indicates the LUN to try to achieve a specified performance MMC-2 rev. 11a - SetStreaming = 0xB6, + /// Reserves a LUN or extent SPC-1 rev. 10 + Reserve10 = 0x56, - /// Stops a scan and continues audio playback from current scanning position MMC-1 rev. 9 - StopPlayScan = 0x4E, - #endregion SCSI Multimedia Commands (MMC) + /// Retrieves security protocol information SPC-4 rev. 16 + SecurityProtocolIn = 0xA2, - #region SCSI Scanner Commands - /// Gets information about the data buffer SCSI-2 X3T9.2/375R rev. 10l - GetDataBufferStatus = 0x34, + /// Transfers security protocol information SPC-4 rev. 16 + SecurityProtocolOut = 0xB5, - /// Gets information about previously defined windows SCSI-2 X3T9.2/375R rev. 10l - GetWindow = 0x25, + /// Requests the device to perform diagnostics ECMA-111 (SCSI-1) + SendDiagnostic = SasiCommands.WriteDiagnostic, - /// Provides positioning functions SCSI-2 X3T9.2/375R rev. 10l - ObjectPosition = 0x31, + /// Extended commands SPC-4 + ServiceActionIn = 0x9E, - /// Begins a scan operation SCSI-2 X3T9.2/375R rev. 10l - Scan = 0x1B, + /// Extended commands SPC-4 + ServiceActionOut = 0x9F, - /// Specifies one or more windows within the device's scanning range SCSI-2 X3T9.2/375R rev. 10l - SetWindow = 0x24, + /// Checks if a LUN is ready to access its medium ECMA-111 (SCSI-1) + TestUnitReady = SasiCommands.TestUnitReady, - /// Sends data to the device SCSI-2 X3T9.2/375R rev. 10l - Send10 = 0x2A, - #endregion SCSI Scanner Commands + /// Writes attribute values to medium auxiliary memory SPC-3 rev. 21b + WriteAttribute = 0x8D, - #region SCSI Block Commands for Optical Media (SBC) - /// Erases the specified number of blocks - Erase10 = 0x2C, + /// Writes to the device's buffer SCSI-2 X3T9.2/375R rev. 10l + WriteBuffer = 0x3B, + #endregion SCSI Primary Commands (SPC) - /// Erases the specified number of blocks - Erase12 = 0xAC, + #region SCSI Block Commands (SBC) + /// Compares blocks with sent data, and if equal, writes those block to device, atomically SBC-3 rev. 25 + CompareAndWrite = 0x89, - /// Searches the medium for a contiguous set of written or blank blocks - MediumScan = 0x38, + /// Formats the medium into addressable logical blocks ECMA-111 (SCSI-1) + FormatUnit = SasiCommands.FormatUnit, FormatWithPreset = 0x38, - /// Reads blocks from device SCSI-2 X3T9.2/375R rev. 10l - Read12 = 0xA8, + /// Locks blocks from eviction of device's cache SCSI-2 X3T9.2/375R rev. 10l + LockUnlockCache = 0x36, - /// Gets medium's defect data SCSI-2 X3T9.2/375R rev. 10l - ReadDefectData12 = 0xB7, + /// Locks blocks from eviction of device's cache SBC-2 rev. 4 + LockUnlockCache16 = 0x92, - /// Gets the maximum generation address for the specified block - ReadGeneration = 0x29, + /// + /// Requests the device to perform the following uninterrupted series of actions: 1.- Read the specified blocks + /// 2.- Transfer blocks from the data out buffer 3.- Perform an OR operation between the read blocks and the buffer 4.- + /// Write the buffer to the blocks SBC-3 rev. 16 + /// + OrWrite = 0x8B, - /// Reads a specified generation of a specified block - ReadUpdatedBlock = 0x2D, + /// Transfers requested blocks to devices' cache SCSI-2 X3T9.2/375R rev. 10l + PreFetch = 0x34, - /// Searches data on blocks SCSI-2 X3T9.2/375R rev. 10l - SearchDataEqual12 = 0xB1, + /// Transfers requested blocks to devices' cache SBC-3 rev. 16 + PreFetch16 = 0x90, - /// Searches data on blocks using major than or equal comparison SCSI-2 X3T9.2/375R rev. 10l - SearchDataHigh12 = 0xB0, + /// Reads blocks from device ECMA-111 (SCSI-1) + Read = SasiCommands.Read, - /// Searches data on blocks using minor than or equal comparison SCSI-2 X3T9.2/375R rev. 10l - SearchDataLow12 = 0xB2, + /// Reads blocks from device ECMA-111 (SCSI-1) + Read10 = SasiCommands.ExtendedAddressRead, - /// Defines the range within which subsequent linked commands may operate SCSI-2 X3T9.2/375R rev. 10l - SetLimits12 = 0xB3, + /// Reads blocks from device SBC-2 rev. 4 + Read16 = 0x88, - /// Replaces a block with data - UpdateBlock = 0x3D, + /// Gets device capacity ECMA-111 (SCSI-1) + ReadCapacity = 0x25, - /// Verifies blocks on the device SCSI-2 X3T9.2/375R rev. 10l - Verify12 = 0xAF, + /// Gets device's defect data SCSI-2 X3T9.2/375R rev. 10l + ReadDefectData = 0x37, - /// Writes blocks to the device SCSI-2 X3T9.2/375R rev. 10l - Write12 = 0xAA, + /// + /// Reads blocks from device in a vendor-specific way that should include the ECC alongside the data SCSI-2 + /// X3T9.2/375R rev. 10l + /// + ReadLong = 0x3E, - /// Writes blocks to the device and then verifies them SCSI-2 X3T9.2/375R rev. 10l - WriteAndVerify12 = 0xAE, - #endregion SCSI Block Commands for Optical Media (SBC) + /// Requests the device to reassign the defective blocks to another area of the medium ECMA-111 (SCSI-1) + ReassignBlocks = 0x07, - #region SCSI Medium Changer Commands (SMC) - /// - /// Provides a means to exchange the medium in the source element with the medium at destination element SCSI-2 - /// X3T9.2/375R rev. 10l - /// - ExchangeMedium = 0xA6, + /// Requests the target write to the medium the XOR data generated from the specified source devices SBC-1 rev. 8c + Rebuild = 0x81, - /// Checks all elements for medium and any other relevant status SCSI-2 X3T9.2/375R rev. 10l - InitializeElementStatus = 0x07, + /// + /// Requests the target write to the buffer the XOR data from its own medium and the specified source devices + /// SBC-1 rev. 8c + /// + Regenerate = 0x82, - /// Checks all elements for medium and any other relevant status in the specified range of elements SMC-2 rev. 7 - InitializeElementStatusWithRange = 0x37, + /// Requests the device to set the LUN in a vendor specific state ECMA-111 (SCSI-1) + RezeroUnit = SasiCommands.RezeroUnit, Sanitize = 0x48, - /// Moves a medium from an element to another SCSI-2 X3T9.2/375R rev. 10l - MoveMedium = 0xA5, + /// Searches data on blocks ECMA-111 (SCSI-1) + SearchDataEqual = SasiCommands.SearchDataEqual, - /// Moves a medium that's currently attached to another element SPC-1 rev. 10 - MoveMediumAttached = 0xA7, + /// Searches data on blocks using major than or equal comparison ECMA-111 (SCSI-1) + SearchDataHigh = SasiCommands.SearchDataHigh, - /// Provides a method to change the open/closed state of the specified import/export element SMC-3 rev. 12 - OpenCloseImportExportElement = 0x1B, + /// Searches data on blocks using minor than or equal comparison ECMA-111 (SCSI-1) + SearchDataLow = SasiCommands.SearchDataLow, - /// Positions the transport element in front of the destination element SCSI-2 X3T9.2/375R rev. 10l - PositionToElement = 0x2B, + /// Requests the device to seek to a specified blocks ECMA-111 (SCSI-1) + Seek = SasiCommands.Seek, - /// Requests the status of the elements SCSI-2 X3T9.2/375R rev. 10l - ReadElementStatus = 0xB8, + /// Requests the device to seek to a specified blocks ECMA-111 (SCSI-1) + Seek10 = 0x2B, - /// Requests the status of the attached element SPC-1 rev. 10 - ReadElementStatusAttached = 0xB4, + /// Defines the range within which subsequent linked commands may operate ECMA-111 (SCSI-1) + SetLimits = 0x33, - /// Releases a reserved LUN SCSI-2 X3T9.2/375R rev. 10l - ReleaseElement = 0x17, + /// Requests the device to enable or disable the LUN for media access operations ECMA-111 (SCSI-1) + StartStopUnit = 0x1B, - /// Releases a reserved LUN SMC-1 rev. 10a - ReleaseElement10 = 0x57, + /// Ensures that the blocks in the cache are written to the medium SCSI-2 X3T9.2/375R rev. 10l + SynchronizeCache = 0x35, - /// Requests information regarding the supported volume types for the device SMC-3 rev. 12 - ReportVolumeTypesSupported = 0x44, + /// Ensures that the blocks in the cache are written to the medium SBC-2 rev. 4 + SynchronizeCache16 = 0x91, - /// Gets the results of SCSI-2 X3T9.2/375R rev. 10l - RequestVolumeElementAddress = 0xB5, + /// Unmaps one or more LBAs In SSDs, this is trimming SBC-3 rev. 25 + Unmap = 0x42, - /// Reserves a LUN SCSI-2 X3T9.2/375R rev. 10l - ReserveElement = 0x16, + /// Verifies blocks on the device ECMA-111 (SCSI-1) + Verify10 = SasiCommands.Verify, - /// Reserves a LUN SMC-1 rev. 10a - ReserveElement10 = 0x56, + /// Verifies blocks on the device SBC-2 rev. 4 + Verify16 = 0x8F, - /// - /// Transfers a volume tag template to be searched or new volume tag information for one or more elements SCSI-2 - /// X3T9.2/375R rev. 10l - /// - SendVolumeTag = 0xB6, - #endregion SCSI Medium Changer Commands (SMC) + /// Writes blocks to the device ECMA-111 (SCSI-1) + Write = SasiCommands.Write, - #region SCSI Communication Commands - /// Gets data from the device SCSI-2 X3T9.2/375R rev. 10l - GetMessage = 0x08, + /// Writes blocks to the device ECMA-111 (SCSI-1) + Write10 = SasiCommands.ExtendedAddressWrite, - /// Gets data from the device SCSI-2 X3T9.2/375R rev. 10l - GetMessage10 = 0x28, + /// Writes blocks to the device SBC-2 rev. 4 + Write16 = 0x8A, - /// Gets data from the device SCSI-2 X3T9.2/375R rev. 10l - GetMessage12 = 0xA8, + /// Writes blocks to the device and then verifies them ECMA-111 (SCSI-1) + WriteAndVerify = SasiCommands.WriteAndVerify, - /// Sends data to the device SCSI-2 X3T9.2/375R rev. 10l - SendMessage = 0x0A, + /// Writes blocks to the device and then verifies them SBC-2 rev. 4 + WriteAndVerify16 = 0x8E, - /// Sends data to the device SCSI-2 X3T9.2/375R rev. 10l - SendMessage10 = 0x2A, + /// + /// Writes blocks to the device with a vendor specified format that shall include the ECC alongside the data + /// SCSI-2 X3T9.2/375R rev. 10l + /// + WriteLong = 0x3F, - /// Sends data to the device SCSI-2 X3T9.2/375R rev. 10l - SendMessage12 = 0xAA, - #endregion SCSI Communication Commands + /// Writes a single block several times SCSI-2 X3T9.2/375R rev. 10l + WriteSame = 0x41, - #region SCSI Controller Commands - /// Commands that get information about redundancy groups SCC-2 rev. 4 - RedundancyGroupIn = 0xBA, + /// Writes a single block several times SBC-2 rev. 4 + WriteSame16 = 0x93, - /// Commands that set information about redundancy groups SCC-2 rev. 4 - RedundancyGroupOut = 0xBB, + /// Requests XOR data generated by an or command SBC-1 rev. 8c + XdRead = 0x52, - /// Commands that get information about volume sets SCC-2 rev. 4 - VolumeSetIn = 0xBE, + /// + /// XORs the data sent with data on the medium and stores it until an is issued SBC-1 rev. + /// 8c + /// + XdWrite = 0x50, - /// Commands that set information about volume sets SCC-2 rev. 4 - VolumeSetOut = 0xBF, - #endregion SCSI Controller Commands + /// + /// XORs the data sent with data on the medium and stores it until an is issued SBC-1 rev. + /// 8c + /// + XdWrite16 = 0x80, - #region Pioneer CD-ROM SCSI-2 Command Set - /// Scans for a block playing a block on each track cross - AudioScan = 0xCD, + /// Requests the target to XOR the sent data with the data on the medium and return the results + XdWriteRead = 0x53, - /// Requests the drive the status from the previous WriteCDP command. - ReadCdp = 0xE4, + /// + /// Requests the target to XOR the data transferred with the data on the medium and writes it to the medium SBC-1 + /// rev. 8c + /// + XpWrite = 0x51, + #endregion SCSI Block Commands (SBC) - /// Requests status from the drive - ReadDriveStatus = 0xE0, + #region SCSI Streaming Commands (SSC) + /// Prepares the medium for use by the LUN SSC-1 rev. 22 + FormatMedium = 0x04, - /// Reads CD-DA data and/or subcode data - ReadCdDa = 0xD8, + /// Erases part of all of the medium from the current position ECMA-111 (SCSI-1) + Erase = 0x19, - /// Reads CD-DA data and/or subcode data using MSF addressing - ReadCdDaMsf = 0xD9, + /// Enables or disables the LUN for further operations ECMA-111 (SCSI-1) + LoadUnload = 0x1B, - /// Reads CD-XA data - ReadCdXa = 0xDB, + /// Positions the LUN to a specified block in a specified partition SCSI-2 X3T9.2/375R rev. 10l + Locate = 0x2B, - /// Reads all subcode data - ReadAllSubCode = 0xDF, + /// Positions the LUN to a specified block in a specified partition SSC-2 rev. 09 + Locate16 = 0x92, - /// Sets the spindle speed to be used while reading/writing data to a CD - SetCdSpeed = 0xDA, WriteCdp = 0xE3, - #endregion + /// Requests the block length limits capability ECMA-111 (SCSI-1) + ReadBlockLimits = 0x05, - #region ATA Command Pass-Through - /// Sends a 24-bit ATA command to the device Clashes with ATA CPT rev. 8a - AtaPassThrough = 0xA1, + /// Reads the current position SCSI-2 X3T9.2/375R rev. 10l + ReadPosition = 0x34, - /// Sends a 48-bit ATA command to the device ATA CPT rev. 8a - AtaPassThrough16 = 0x85, - #endregion ATA Command Pass-Through + /// Reads blocks from the device, in reverse order ECMA-111 (SCSI-1) + ReadReverse = 0x0F, - #region 6-byte CDB aliases - ModeSelect6 = ModeSelect, ModeSense6 = ModeSense, Read6 = Read, - Seek6 = Seek, Write6 = Write, - #endregion 6-byte CDB aliases + /// + /// Retrieves data from the device buffer that has not been successfully written to the medium (or printed) + /// ECMA-111 (SCSI-1) + /// + RecoverBufferedData = 0x14, - #region SCSI Zoned Block Commands - /// ZBC commands with host->device information - ZbcOut = 0x94, + /// Requests information regarding the supported densities for the logical unit SSC-1 rev. 22 + ReportDensitySupport = 0x44, - /// ZBC commands with device->host information - ZbcIn = 0x95, - #endregion + /// Seeks the medium to the beginning of partition in current partition ECMA-111 (SCSI-1) + Rewind = 0x01, - #region SCSI Commands with unknown meaning, mostly vendor specific - SetCdSpeedUnk = 0xB8, WriteCdMsf = 0xA2, WriteCd = 0xAA, - ReadDefectTag = 0xB7, PlayCd = 0xBC, SpareIn = 0xBC, - SpareOut = 0xBD, WriteStream16 = 0x9A, WriteAtomic = 0x9C, - ServiceActionBidirectional = 0x9D, WriteLong2 = 0xEA, UnknownCdCommand = 0xD4, - UnknownCdCommand2 = 0xD5, - #endregion SCSI Commands with unknown meaning, mostly vendor specific + /// A variety of positioning functions ECMA-111 (SCSI-1) + Space = 0x11, - #region SEGA Packet Interface (all are 12-byte CDB) - /// Verifies that the device can be accessed Sega SPI ver. 1.30 - SegaTestUnit = TestUnitReady, + /// A variety of positioning functions SSC-2 rev. 09 + Space16 = 0x91, - /// Gets current CD status Sega SPI ver. 1.30 - SegaRequestStatus = 0x10, + /// Selects the specified track ECMA-111 (SCSI-1) + TrackSelect = 0x0B, - /// Gets CD block mode info Sega SPI ver. 1.30 - SegaRequestMode = 0x11, + /// Verifies one or more blocks from the next one ECMA-111 (SCSI-1) + Verify = 0x13, - /// Sets CD block mode Sega SPI ver. 1.30 - SegaSetMode = 0x12, + /// Writes the specified number of filemarks or setmarks in the current position ECMA-111 (SCSI-1) + WriteFileMarks = 0x10, + #endregion SCSI Streaming Commands (SSC) - /// Requests device error info Sega SPI ver. 1.30 - SegaRequestError = 0x13, + #region SCSI Streaming Commands for Printers (SSC) + /// + /// Assures that the data in the buffer has been printed, or, for other devices, written to media ECMA-111 + /// (SCSI-1) + /// + FlushBuffer = 0x10, - /// Gets disc TOC Sega SPI ver. 1.30 - SegaGetToc = 0x14, + /// Specifies forms or fronts ECMA-111 (SCSI-1) + Format = 0x04, - /// Gets specified session data Sega SPI ver. 1.30 - SegaRequestSession = 0x15, + /// Transfers data to be printed ECMA-111 (SCSI-1) + Print = 0x0A, - /// - /// Stops the drive and opens the drive tray, or, on manual trays, stays busy until it is opened Sega SPI ver. - /// 1.30 - /// - SegaOpenTray = 0x16, + /// Transfers data to be printed with a slew value ECMA-111 (SCSI-1) + SlewAndPrint = 0x0B, - /// Starts audio playback Sega SPI ver. 1.30 - SegaPlayCd = 0x20, + /// Halts printing ECMA-111 (SCSI-1) + StopPrint = 0x1B, - /// Moves drive pickup to specified block Sega SPI ver. 1.30 - SegaSeek = 0x21, + /// + /// Assures that the data in the buffer has been printed, or, for other devices, written to media SCSI-2 + /// X3T9.2/375R rev. 10l + /// + SynchronizeBuffer = FlushBuffer, + #endregion SCSI Streaming Commands for Printers (SSC) - /// - /// Fast-forwards or fast-reverses until Lead-In or Lead-Out arrive, or until another command is issued Sega SPI - /// ver. 1.30 - /// - SegaScan = 0x22, + #region SCSI Processor Commands + /// Transfers data from the device ECMA-111 (SCSI-1) + Receive = 0x08, - /// Reads blocks from the disc Sega SPI ver. 1.30 - SegaRead = 0x30, + /// Sends data to the device ECMA-111 (SCSI-1) + Send = 0x0A, + #endregion SCSI Processor Commands - /// Reads blocks from the disc seeking to another position at end Sega SPI ver. 1.30 - SegaRead2 = 0x31, + #region SCSI Multimedia Commands (MMC) + /// Erases any part of a CD-RW MMC-1 rev. 9 + Blank = 0xA1, - /// Reads disc subcode Sega SPI ver. 1.30 - SegaGetSubcode = 0x40, - #endregion SEGA Packet Interface (all are 12-byte CDB) + /// Closes a track or session MMC-1 rev. 9 + CloseTrackSession = 0x5B, - /// Variable sized Command Description Block SPC-4 rev. 16 - VariableSizedCdb = 0x7F, + /// + /// Gets information about the overall capabilities of the device and the current capabilities of the device MMC-2 + /// rev. 11a + /// + GetConfiguration = 0x46, - #region Plextor vendor commands - /// Sends extended commands (like SpeedRead) to Plextor drives - PlextorExtend = 0xE9, - - /// Command for Plextor PoweRec - PlextorPoweRec = 0xEB, - - /// Sends extended commands (like PoweRec) to Plextor drives - PlextorExtend2 = 0xED, - - /// Resets Plextor drives - PlextorReset = 0xEE, - - /// Reads drive statistics from Plextor drives EEPROM - PlextorReadEeprom = 0xF1, - #endregion Plextor vendor commands - - #region HL-DT-ST vendor commands - /// Sends debugging commands to HL-DT-ST DVD drives - HlDtStVendor = 0xE7, - #endregion HL-DT-ST vendor commands - - #region NEC vendor commands - /// Reads CD-DA data - NecReadCdDa = 0xD4, - #endregion NEC vendor commands - - #region Adaptec vendor commands - /// Translates a SCSI LBA to a drive's CHS - AdaptecTranslate = 0x0F, - - /// Configures Adaptec controller error threshold - AdaptecSetErrorThreshold = 0x10, - - /// Reads and resets error and statistical counters - AdaptecReadCounters = 0x11, - - /// Writes to controller's RAM - AdaptecWriteBuffer = 0x13, - - /// Reads controller's RAM - AdaptecReadBuffer = 0x14, - #endregion Adaptec vendor commands - - #region Archive Corp. vendor commands - /// Gets current position's block address - ArchiveRequestBlockAddress = 0x02, - - /// Seeks to specified block address - ArchiveSeekBlock = 0x0C, - #endregion Archive Corp. vendor commands + /// Requests the LUN to report events and statuses MMC-2 rev. 11a + GetEventStatusNotification = 0x4A, - #region Certance vendor commands - /// Parks the load arm in preparation for transport - CertanceParkUnpark = 0x06, - #endregion Certance vendor commands + /// Provides a method to profile the performance of the drive MMC-2 rev. 11a + GetPerformance = 0xAC, - #region Fujitsu vendor commands - /// Used to check the controller's data and control path - FujitsuLoopWriteToRead = 0xC1, + /// Requests the device changer to load or unload a disc MMC-1 rev. 9 + LoadUnloadCd = 0xA6, - /// Used to display a message on the operator panel - FujitsuDisplay = 0xCF, - #endregion Fujitsu vendor commands + /// Requests the device changer to load or unload a disc MMC-2 rev. 11a + LoadUnloadMedium = 0xA6, - #region M-Systems vendor commands - /// Securely erases all flash blocks, including defective, spared and unused - MSystemsSecurityErase = 0xFF, + /// Requests information about the current status of the CD device, including any changer mechanism MMC-1 rev. 9 + MechanicalStatus = 0xBD, - /// Securely erases all flash blocks, including defective, spared and unused - MSystemsSecurityEraseOld = 0xDF, - #endregion M-Systems vendor commands + /// Requests the device to start or stop an audio play operation SCSI-2 X3T9.2/375R rev. 10l + PauseResume = 0x4B, - #region Plasmon vendor commands - /// Retrieves sector address - PlasmonReadSectorLocation = 0xE6, + /// Begins an audio playback SCSI-2 X3T9.2/375R rev. 10l + PlayAudio = 0x45, - /// Makes a Compliant WORM block completely unreadable - PlasmonShred = 0xEE, - #endregion Plasmon vendor commands + /// Begins an audio playback SCSI-2 X3T9.2/375R rev. 10l + PlayAudio12 = 0xA5, - #region Kreon vendor commands - /// Most Kreon commands start with this - KreonCommand = 0xFF, + /// Begins an audio playback using MSF addressing SCSI-2 X3T9.2/375R rev. 10l + PlayAudioMsf = 0x47, - /// Kreon extract Security Sectors command start with this - KreonSsCommand = 0xAD, - #endregion Kreon vendor commands + /// Begins an audio playback from the specified index of the specified track SCSI-2 X3T9.2/375R rev. 10l + PlayAudioTrackIndex = 0x48, - #region MiniDisc vendor commands - /// Gets some list of pointers only present on MD-DATA discs - MiniDiscReadDTOC = 0xD1, - /// Writes some list of pointers only present on MD-DATA discs - MiniDiscWriteDTOC = 0xD2, - /// Reads UTOC - MiniDiscReadUTOC = 0xD4, - /// Unknown, returns 4 empty bytes - MiniDiscD5 = 0xD5, - /// Stops playing audio - MiniDiscStopPlay = 0xD6, - /// Gets current audio playing position - MiniDiscReadPosition = 0xD7, - /// Gets some values that are identical amongst audio discs and data discs, different between them - MiniDiscGetType = 0xD8, - #endregion + /// Begins an audio playback from the position relative of a track SCSI-2 X3T9.2/375R rev. 10l + PlayTrackRelative = 0x49, - #region MediaTek vendor commands - MediaTekVendorCommand = 0xF1 - #endregion - } - #endregion SCSI Commands - - /// SCSI command transfer direction - public enum ScsiDirection - { - /// No data transfer happens - None = 0, - - /// From host to device - Out = 1, - - /// From device to host - In = 2, - - /// Bidirectional device/host - Bidirectional = 3, - - /// Unspecified - Unspecified = -1 - } - - #region SCSI's ATA Command Pass-Through - public enum AtaProtocol : byte - { - /// Requests a device hard reset (pin 1) - HardReset = 0, - - /// Requests a device soft reset (COMRESET issue) - SoftReset = 1, - - /// No data is to be transferred - NonData = 3, - - /// Requests a device->host transfer using PIO - PioIn = 4, - - /// Requests a host->device transfer using PIO - PioOut = 5, - - /// Requests a DMA transfer - Dma = 6, - - /// Requests to queue a DMA transfer - DmaQueued = 7, - - /// Requests device diagnostics - DeviceDiagnostic = 8, - - /// Requests device reset - DeviceReset = 9, - - /// Requests a device->host transfer using UltraDMA - UDmaIn = 10, - - /// Requests a host->device transfer using UltraDMA - UDmaOut = 11, - - /// Unknown Serial ATA - FpDma = 12, - - /// Requests the Extended ATA Status Return Descriptor - ReturnResponse = 15 - } - - /// Indicates the STL which ATA register contains the length of data to be transferred - public enum AtaTransferRegister : byte - { - /// There is no transfer - NoTransfer = 0, - - /// FEATURE register contains the data length - Feature = 1, - - /// SECTOR_COUNT register contains the data length - SectorCount = 2, - - /// The STPSIU contains the data length - Sptsiu = 3 - } - #endregion SCSI's ATA Command Pass-Through - - /// ZBC sub-commands, mask 0x1F - public enum ZbcSubCommands : byte - { - /// Returns list with zones of specified types - ReportZones = 0x00, + /// Begins an audio playback from the position relative of a track SCSI-2 X3T9.2/375R rev. 10l + PlayTrackRelative12 = 0xA9, - /// Closes a zone - CloseZone = 0x01, + /// Reports the total and blank area of the device buffer MMC-1 rev. 9 + ReadBufferCapacity = 0x5C, - /// Finishes a zone - FinishZone = 0x02, + /// Reads a block from a CD with any of the requested CD data streams MMC-1 rev. 9 + ReadCd = 0xBE, - /// Opens a zone - OpenZone = 0x03, + /// Reads a block from a CD with any of the requested CD data streams using MSF addressing MMC-1 rev. 9 + ReadCdMsf = 0xB9, - /// Resets zone's write pointer to zone start - ResetWritePointer = 0x04, + /// Returns the recorded size of the CD MMC-1 rev. 9 + ReadCdRecordedCapacity = 0x25, - /// Requests device to transfer parameters describing realms - ReportRealms = 0x06, - /// Requests device to transfer parameters describing the zone domains structure - ReportZoneDomains = 0x07, - /// Requests device to perform a zone activation operation - ZoneActivate = 0x08, - /// Requests information about a zone - ZoneQuery = 0x09, - /// Requests device to perform sequentialize zone operations - SequentializeZone = 0x10 - } + /// Gets information about all discs: CD-ROM, CD-R and CD-RW MMC-1 rev. 9 + ReadDiscInformation = 0x51, - /// MODE SENSE page control, mask 0xC0 - public enum ScsiModeSensePageControl : byte - { - /// Current values - Current = 0x00, + /// Reads areas from the DVD or BD media MMC-5 rev. 2c + ReadDiscStructure = 0xAD, - /// Changeable values - Changeable = 0x40, + /// Reads areas from the DVD media MMC-2 rev. 11a + ReadDvdStructure = 0xAD, - /// Default values - Default = 0x80, + /// Requests a list of the possible format capacities for an installed random-writable media MMC-2 rev. 11a + ReadFormatCapacities = 0x23, - /// Saved values - Saved = 0xC0 - } + /// Reads the data block header of the specified CD-ROM block SCSI-2 X3T9.2/375R rev. 10l + ReadHeader = 0x44, - public enum ScsiPreventAllowMode : byte - { - /// Allows medium removal from data transport and from medium changer - Allow = 0x00, + /// Reads the mastering information from a Master CD. MMC-1 rev. 9 + ReadMasterCue = 0x59, - /// Prevents medium removal from data transport but allows it from medium changer - Prevent = 0x01, + /// Requests the Q subchannel and the current audio playback status SCSI-2 X3T9.2/375R rev. 10l + ReadSubChannel = 0x42, - /// Allows medium removal from data transport but prevents it from medium changer - PreventChanger = 0x02, + /// Requests the medium TOC, PMA or ATIP from the device SCSI-2 X3T9.2/375R rev. 10l + ReadTocPmaAtip = 0x43, - /// Prevents medium removal from both data transport and medium changer - PreventAll = 0x03 - } + /// Gets information about a track regardless of its status MMC-1 rev. 9 + ReadTrackInformation = 0x52, - public enum MmcGetConfigurationRt : byte - { - /// Drive shall return the Feature Header and all Feature Descriptors - All = 0x00, + /// Repairs an incomplete ECC block at the end of an RZone Mt. Fuji ver. 7 rev. 1.21 + RepairRZone = 0x58, - /// Drive shall return the Feature Header and current Feature Descriptors - Current = 0x01, + /// Repairs an incomplete packet at the end of a packet writing track MMC-1 rev. 9 + RepairTrack = 0x58, - /// Drive shall return only the Feature Header with the chosen Feature Descriptor - Single = 0x02, Reserved = 0x03 - } + /// + /// Requests the start of the authentication process and provides data necessary for authentication and for + /// generating a Bus Key MMC-2 rev. 11a + /// + ReportKey = 0xA4, - public enum MmcDiscStructureMediaType : byte - { - /// Disc Structures for DVD and HD DVD - Dvd = 0x00, + /// Reserves disc space for a track MMC-1 rev. 9 + ReserveTrack = 0x53, - /// Disc Structures for BD - Bd = 0x01 - } + /// + /// Fast-forwards or fast-reverses the audio playback to the specified block. Stops if it encounters a data track + /// MMC-1 rev. 9 + /// + ScanMmc = 0xBA, - public enum MmcDiscStructureFormat : byte - { - // Generic Format Codes + /// Sends a cue sheet for session-at-once recording MMC-1 rev. 9 + SendCueSheet = 0x5D, - /// AACS Volume Identifier - AacsVolId = 0x80, + /// Transfer a DVD or BD structure for media writing MMC-5 rev. 2c + SendDiscStructure = 0xAD, - /// AACS Pre-recorded Media Serial Number - AacsMediaSerial = 0x81, + /// Transfer a DVD structure for media writing MMC-2 rev. 11a + SendDvdStructure = 0xAD, - /// AACS Media Identifier - AacsMediaId = 0x82, + /// Requests the LUN to process an event MMC-2 rev. 11a + SendEvent = 0xA2, - /// AACS Lead-in Media Key Block - Aacsmkb = 0x83, + /// Provides data necessary for authentication and for generating a Bus Key MMC-2 rev. 11a + SendKey = 0xA3, - /// AACS Data Keys - AacsDataKeys = 0x84, + /// Restores the Optimum Power Calibration values to the drive for a specific disc MMC-1 rev. 9 + SendOpcInformation = 0x54, - /// AACS LBA extents - AacslbaExtents = 0x85, + /// Sets the spindle speed to be used while reading/writing data to a CD MMC-1 rev. 9 + SetCdRomSpeed = 0xBB, - /// CPRM Media Key Block specified by AACS - Aacsmkbcprm = 0x86, + /// Requests the LUN to perform read ahead caching operations from the specified block MMC-2 rev. 11a + SetReadAhead = 0xA7, - /// Recognized format layers - RecognizedFormatLayers = 0x90, + /// Indicates the LUN to try to achieve a specified performance MMC-2 rev. 11a + SetStreaming = 0xB6, - /// Write protection status - WriteProtectionStatus = 0xC0, + /// Stops a scan and continues audio playback from current scanning position MMC-1 rev. 9 + StopPlayScan = 0x4E, + #endregion SCSI Multimedia Commands (MMC) - /// READ/SEND DISC STRUCTURE capability list - CapabilityList = 0xFF, + #region SCSI Scanner Commands + /// Gets information about the data buffer SCSI-2 X3T9.2/375R rev. 10l + GetDataBufferStatus = 0x34, - // DVD Disc Structures - /// DVD Lead-in Physical Information - PhysicalInformation = 0x00, + /// Gets information about previously defined windows SCSI-2 X3T9.2/375R rev. 10l + GetWindow = 0x25, - /// DVD Lead-in Copyright Information - CopyrightInformation = 0x01, + /// Provides positioning functions SCSI-2 X3T9.2/375R rev. 10l + ObjectPosition = 0x31, - /// CSS/CPPM Disc key - DiscKey = 0x02, + /// Begins a scan operation SCSI-2 X3T9.2/375R rev. 10l + Scan = 0x1B, - /// DVD Burst Cutting Area - BurstCuttingArea = 0x03, + /// Specifies one or more windows within the device's scanning range SCSI-2 X3T9.2/375R rev. 10l + SetWindow = 0x24, - /// DVD Lead-in Disc Manufacturing Information - DiscManufacturingInformation = 0x04, + /// Sends data to the device SCSI-2 X3T9.2/375R rev. 10l + Send10 = 0x2A, + #endregion SCSI Scanner Commands - /// DVD Copyright Information from specified sector - SectorCopyrightInformation = 0x05, + #region SCSI Block Commands for Optical Media (SBC) + /// Erases the specified number of blocks + Erase10 = 0x2C, - /// CSS/CPPM Media Identifier - MediaIdentifier = 0x06, + /// Erases the specified number of blocks + Erase12 = 0xAC, - /// CSS/CPPM Media Key Block - MediaKeyBlock = 0x07, + /// Searches the medium for a contiguous set of written or blank blocks + MediumScan = 0x38, - /// DDS from DVD-RAM - DvdramDds = 0x08, + /// Reads blocks from device SCSI-2 X3T9.2/375R rev. 10l + Read12 = 0xA8, - /// DVD-RAM Medium Status - DvdramMediumStatus = 0x09, + /// Gets medium's defect data SCSI-2 X3T9.2/375R rev. 10l + ReadDefectData12 = 0xB7, - /// DVD-RAM Spare Area Information - DvdramSpareAreaInformation = 0x0A, + /// Gets the maximum generation address for the specified block + ReadGeneration = 0x29, - /// DVD-RAM Recording Type Information - DvdramRecordingType = 0x0B, + /// Reads a specified generation of a specified block + ReadUpdatedBlock = 0x2D, - /// DVD-R/-RW RMD in last Border-out - LastBorderOutRmd = 0x0C, + /// Searches data on blocks SCSI-2 X3T9.2/375R rev. 10l + SearchDataEqual12 = 0xB1, - /// Specified RMD from last recorded Border-out - SpecifiedRmd = 0x0D, + /// Searches data on blocks using major than or equal comparison SCSI-2 X3T9.2/375R rev. 10l + SearchDataHigh12 = 0xB0, - /// DVD-R/-RW Lead-in pre-recorded information - PreRecordedInfo = 0x0E, + /// Searches data on blocks using minor than or equal comparison SCSI-2 X3T9.2/375R rev. 10l + SearchDataLow12 = 0xB2, - /// DVD-R/-RW Media Identifier - DvdrMediaIdentifier = 0x0F, + /// Defines the range within which subsequent linked commands may operate SCSI-2 X3T9.2/375R rev. 10l + SetLimits12 = 0xB3, - /// DVD-R/-RW Physical Format Information - DvdrPhysicalInformation = 0x10, + /// Replaces a block with data + UpdateBlock = 0x3D, - /// ADIP - Adip = 0x11, + /// Verifies blocks on the device SCSI-2 X3T9.2/375R rev. 10l + Verify12 = 0xAF, - /// HD DVD Lead-in Copyright Protection Information - HddvdCopyrightInformation = 0x12, + /// Writes blocks to the device SCSI-2 X3T9.2/375R rev. 10l + Write12 = 0xAA, - /// AACS Lead-in Copyright Data Section - DvdAacs = 0x15, + /// Writes blocks to the device and then verifies them SCSI-2 X3T9.2/375R rev. 10l + WriteAndVerify12 = 0xAE, + #endregion SCSI Block Commands for Optical Media (SBC) - /// HD DVD-R Medium Status - HddvdrMediumStatus = 0x19, + #region SCSI Medium Changer Commands (SMC) + /// + /// Provides a means to exchange the medium in the source element with the medium at destination element SCSI-2 + /// X3T9.2/375R rev. 10l + /// + ExchangeMedium = 0xA6, - /// HD DVD-R Last recorded RMD in the latest RMZ - HddvdrLastRmd = 0x1A, + /// Checks all elements for medium and any other relevant status SCSI-2 X3T9.2/375R rev. 10l + InitializeElementStatus = 0x07, - /// DVD+/-R DL and DVD-Download DL layer capacity - DvdrLayerCapacity = 0x20, + /// Checks all elements for medium and any other relevant status in the specified range of elements SMC-2 rev. 7 + InitializeElementStatusWithRange = 0x37, - /// DVD-R DL Middle Zone start address - MiddleZoneStart = 0x21, + /// Moves a medium from an element to another SCSI-2 X3T9.2/375R rev. 10l + MoveMedium = 0xA5, - /// DVD-R DL Jump Interval Size - JumpIntervalSize = 0x22, + /// Moves a medium that's currently attached to another element SPC-1 rev. 10 + MoveMediumAttached = 0xA7, - /// DVD-R DL Start LBA of the manual layer jump - ManualLayerJumpStartLba = 0x23, + /// Provides a method to change the open/closed state of the specified import/export element SMC-3 rev. 12 + OpenCloseImportExportElement = 0x1B, - /// DVD-R DL Remapping information of the specified Anchor Point - RemapAnchorPoint = 0x24, + /// Positions the transport element in front of the destination element SCSI-2 X3T9.2/375R rev. 10l + PositionToElement = 0x2B, - /// Disc Control Block - Dcb = 0x30, + /// Requests the status of the elements SCSI-2 X3T9.2/375R rev. 10l + ReadElementStatus = 0xB8, - // BD Disc Structures - /// Blu-ray Disc Information - DiscInformation = 0x00, + /// Requests the status of the attached element SPC-1 rev. 10 + ReadElementStatusAttached = 0xB4, - /// Blu-ray Burst Cutting Area - BdBurstCuttingArea = 0x03, + /// Releases a reserved LUN SCSI-2 X3T9.2/375R rev. 10l + ReleaseElement = 0x17, - /// Blu-ray DDS - BdDds = 0x08, + /// Releases a reserved LUN SMC-1 rev. 10a + ReleaseElement10 = 0x57, - /// Blu-ray Cartridge Status - CartridgeStatus = 0x09, + /// Requests information regarding the supported volume types for the device SMC-3 rev. 12 + ReportVolumeTypesSupported = 0x44, - /// Blu-ray Spare Area Information - BdSpareAreaInformation = 0x0A, + /// Gets the results of SCSI-2 X3T9.2/375R rev. 10l + RequestVolumeElementAddress = 0xB5, - /// Unmodified DFL - RawDfl = 0x12, + /// Reserves a LUN SCSI-2 X3T9.2/375R rev. 10l + ReserveElement = 0x16, - /// Physical Access Control - Pac = 0x30 - } + /// Reserves a LUN SMC-1 rev. 10a + ReserveElement10 = 0x56, - public enum ScsiServiceActions : byte - { - // SERVICE ACTION IN + /// + /// Transfers a volume tag template to be searched or new volume tag information for one or more elements SCSI-2 + /// X3T9.2/375R rev. 10l + /// + SendVolumeTag = 0xB6, + #endregion SCSI Medium Changer Commands (SMC) - /// Requests parameter data describing provisioning status for the specified LBA SBC-3 rev. 25 - GetLbaStatus = 0x12, + #region SCSI Communication Commands + /// Gets data from the device SCSI-2 X3T9.2/375R rev. 10l + GetMessage = 0x08, - /// Gets device capacity SBC-2 rev. 4 - ReadCapacity16 = 0x10, + /// Gets data from the device SCSI-2 X3T9.2/375R rev. 10l + GetMessage10 = 0x28, - /// Reads blocks from device in a vendor-specific way that should include the ECC alongside the data SBC-2 rev. 4 - ReadLong16 = 0x11, + /// Gets data from the device SCSI-2 X3T9.2/375R rev. 10l + GetMessage12 = 0xA8, - /// Requests information indicating the user data segments on the ports and LUNs to access them SBC-3 rev. 25 - ReportReferrals = 0x13, + /// Sends data to the device SCSI-2 X3T9.2/375R rev. 10l + SendMessage = 0x0A, - // SERVICE ACTION OUT + /// Sends data to the device SCSI-2 X3T9.2/375R rev. 10l + SendMessage10 = 0x2A, - /// - /// Writes blocks to the device with a vendor specified format that shall include the ECC alongside the data SBC-2 - /// rev. 4 - /// - WriteLong16 = ReadLong16 - } + /// Sends data to the device SCSI-2 X3T9.2/375R rev. 10l + SendMessage12 = 0xAA, + #endregion SCSI Communication Commands - public enum MmcDiscInformationDataTypes : byte - { - /// Standard Disc Information - DiscInformation = 0x00, + #region SCSI Controller Commands + /// Commands that get information about redundancy groups SCC-2 rev. 4 + RedundancyGroupIn = 0xBA, - /// Track Resources Information - TrackResources = 0x01, + /// Commands that set information about redundancy groups SCC-2 rev. 4 + RedundancyGroupOut = 0xBB, - /// POW Resources Information - PowResources = 0x02 - } + /// Commands that get information about volume sets SCC-2 rev. 4 + VolumeSetIn = 0xBE, - public enum MmcSectorTypes : byte - { - /// No checking of data type is performed - AllTypes = 0x00, + /// Commands that set information about volume sets SCC-2 rev. 4 + VolumeSetOut = 0xBF, + #endregion SCSI Controller Commands - /// Only CD-DA sectors shall be returned - Cdda = 0x01, + #region Pioneer CD-ROM SCSI-2 Command Set + /// Scans for a block playing a block on each track cross + AudioScan = 0xCD, - /// Only Mode 1 sectors shall be returned - Mode1 = 0x02, + /// Requests the drive the status from the previous WriteCDP command. + ReadCdp = 0xE4, - /// Only Mode 2 formless sectors shall be returned - Mode2 = 0x03, + /// Requests status from the drive + ReadDriveStatus = 0xE0, - /// Only Mode 2 Form 1 sectors shall be returned - Mode2Form1 = 0x04, + /// Reads CD-DA data and/or subcode data + ReadCdDa = 0xD8, - /// Only Mode 2 Form 2 sectors shall be returned - Mode2Form2 = 0x05 - } + /// Reads CD-DA data and/or subcode data using MSF addressing + ReadCdDaMsf = 0xD9, - public enum MmcHeaderCodes : byte - { - /// No header information shall be returned - None = 0x00, + /// Reads CD-XA data + ReadCdXa = 0xDB, - /// Only the four byte header shall be returned - HeaderOnly = 0x01, + /// Reads all subcode data + ReadAllSubCode = 0xDF, - /// Only the mode 2 form x subheader shall be returned - SubHeaderOnly = 0x02, + /// Sets the spindle speed to be used while reading/writing data to a CD + SetCdSpeed = 0xDA, WriteCdp = 0xE3, + #endregion - /// Return both header and subheader - AllHeaders = 0x03 - } + #region ATA Command Pass-Through + /// Sends a 24-bit ATA command to the device Clashes with ATA CPT rev. 8a + AtaPassThrough = 0xA1, - public enum MmcErrorField : byte - { - /// No error information is returned - None = 0x00, + /// Sends a 48-bit ATA command to the device ATA CPT rev. 8a + AtaPassThrough16 = 0x85, + #endregion ATA Command Pass-Through - /// The C2 pointer bits will be included - C2Pointers = 0x01, + #region 6-byte CDB aliases + ModeSelect6 = ModeSelect, ModeSense6 = ModeSense, Read6 = Read, + Seek6 = Seek, Write6 = Write, + #endregion 6-byte CDB aliases - /// The C2 pointer bits will be included as well as the block error byte with a padding byte - C2PointersAndBlock = 0x02 - } + #region SCSI Zoned Block Commands + /// ZBC commands with host->device information + ZbcOut = 0x94, - public enum MmcSubchannel : byte - { - /// No subchannel shall be returned - None = 0x00, + /// ZBC commands with device->host information + ZbcIn = 0x95, + #endregion - /// The raw P to W subchannel data shall be transferred - Raw = 0x01, + #region SCSI Commands with unknown meaning, mostly vendor specific + SetCdSpeedUnk = 0xB8, WriteCdMsf = 0xA2, WriteCd = 0xAA, + ReadDefectTag = 0xB7, PlayCd = 0xBC, SpareIn = 0xBC, + SpareOut = 0xBD, WriteStream16 = 0x9A, WriteAtomic = 0x9C, + ServiceActionBidirectional = 0x9D, WriteLong2 = 0xEA, UnknownCdCommand = 0xD4, + UnknownCdCommand2 = 0xD5, + #endregion SCSI Commands with unknown meaning, mostly vendor specific - /// Q data shall be transferred - Q16 = 0x02, + #region SEGA Packet Interface (all are 12-byte CDB) + /// Verifies that the device can be accessed Sega SPI ver. 1.30 + SegaTestUnit = TestUnitReady, - /// De-interleaved and error-corrected R to W subchannel data shall be transferred - Rw = 0x04 - } + /// Gets current CD status Sega SPI ver. 1.30 + SegaRequestStatus = 0x10, - public enum PioneerSubchannel : byte - { - /// No subchannel shall be returned - None = 0x00, + /// Gets CD block mode info Sega SPI ver. 1.30 + SegaRequestMode = 0x11, - /// Q data shall be transferred - Q16 = 0x01, + /// Sets CD block mode Sega SPI ver. 1.30 + SegaSetMode = 0x12, - /// The raw P to W subchannel data shall be transferred - All = 0x02, + /// Requests device error info Sega SPI ver. 1.30 + SegaRequestError = 0x13, - /// The raw P to W subchannel data shall be transferred WITHOUT user data - Only = 0x03 - } + /// Gets disc TOC Sega SPI ver. 1.30 + SegaGetToc = 0x14, - public enum PlextorSubchannel : byte - { - /// No subchannel shall be returned - None = 0x00, + /// Gets specified session data Sega SPI ver. 1.30 + SegaRequestSession = 0x15, - /// Q data shall be transferred - Q16 = 0x01, + /// + /// Stops the drive and opens the drive tray, or, on manual trays, stays busy until it is opened Sega SPI ver. + /// 1.30 + /// + SegaOpenTray = 0x16, - /// The packed and corrected P to W subchannel data shall be transferred - Pack = 0x02, + /// Starts audio playback Sega SPI ver. 1.30 + SegaPlayCd = 0x20, - /// The raw P to W subchannel data shall be transferred - All = 0x03, + /// Moves drive pickup to specified block Sega SPI ver. 1.30 + SegaSeek = 0x21, - /// The raw P to W subchannel data, plus C2 error data shall be transferred - RawC2 = 0x08 - } + /// + /// Fast-forwards or fast-reverses until Lead-In or Lead-Out arrive, or until another command is issued Sega SPI + /// ver. 1.30 + /// + SegaScan = 0x22, - public enum PlextorSubCommands : byte - { - /// Gets Plextor mode - GetMode = 0x00, + /// Reads blocks from the disc Sega SPI ver. 1.30 + SegaRead = 0x30, - /// Sets Plextor mode - SetMode = 0x10, + /// Reads blocks from the disc seeking to another position at end Sega SPI ver. 1.30 + SegaRead2 = 0x31, - /// Plextor force single session or hide CD-R - SessionHide = 0x01, + /// Reads disc subcode Sega SPI ver. 1.30 + SegaGetSubcode = 0x40, + #endregion SEGA Packet Interface (all are 12-byte CDB) - /// Plextor VariRec - VariRec = 0x02, + /// Variable sized Command Description Block SPC-4 rev. 16 + VariableSizedCdb = 0x7F, - /// Plextor GigaRec - GigaRec = 0x04, + #region Plextor vendor commands + /// Sends extended commands (like SpeedRead) to Plextor drives + PlextorExtend = 0xE9, + + /// Command for Plextor PoweRec + PlextorPoweRec = 0xEB, + + /// Sends extended commands (like PoweRec) to Plextor drives + PlextorExtend2 = 0xED, + + /// Resets Plextor drives + PlextorReset = 0xEE, + + /// Reads drive statistics from Plextor drives EEPROM + PlextorReadEeprom = 0xF1, + #endregion Plextor vendor commands + + #region HL-DT-ST vendor commands + /// Sends debugging commands to HL-DT-ST DVD drives + HlDtStVendor = 0xE7, + #endregion HL-DT-ST vendor commands + + #region NEC vendor commands + /// Reads CD-DA data + NecReadCdDa = 0xD4, + #endregion NEC vendor commands + + #region Adaptec vendor commands + /// Translates a SCSI LBA to a drive's CHS + AdaptecTranslate = 0x0F, + + /// Configures Adaptec controller error threshold + AdaptecSetErrorThreshold = 0x10, + + /// Reads and resets error and statistical counters + AdaptecReadCounters = 0x11, + + /// Writes to controller's RAM + AdaptecWriteBuffer = 0x13, + + /// Reads controller's RAM + AdaptecReadBuffer = 0x14, + #endregion Adaptec vendor commands + + #region Archive Corp. vendor commands + /// Gets current position's block address + ArchiveRequestBlockAddress = 0x02, + + /// Seeks to specified block address + ArchiveSeekBlock = 0x0C, + #endregion Archive Corp. vendor commands - /// Plextor acoustic management (disc related) - SilentDisc = 0x06, + #region Certance vendor commands + /// Parks the load arm in preparation for transport + CertanceParkUnpark = 0x06, + #endregion Certance vendor commands - /// Plextor acoustic management (tra related) - SilentTray = 0x07, + #region Fujitsu vendor commands + /// Used to check the controller's data and control path + FujitsuLoopWriteToRead = 0xC1, - /// Plextor acoustic management - Silent = 0x08, + /// Used to display a message on the operator panel + FujitsuDisplay = 0xCF, + #endregion Fujitsu vendor commands - /// Plextor test write DVD+ - TestWriteDvdPlus = 0x21, + #region M-Systems vendor commands + /// Securely erases all flash blocks, including defective, spared and unused + MSystemsSecurityErase = 0xFF, - /// Plextor book setting - BitSet = 0x22, + /// Securely erases all flash blocks, including defective, spared and unused + MSystemsSecurityEraseOld = 0xDF, + #endregion M-Systems vendor commands - /// Plextor SecuRec - SecuRec = 0xD5, + #region Plasmon vendor commands + /// Retrieves sector address + PlasmonReadSectorLocation = 0xE6, - /// Book setting for DVD+R - BitSetR = 0x0A, + /// Makes a Compliant WORM block completely unreadable + PlasmonShred = 0xEE, + #endregion Plasmon vendor commands - /// Book setting for DVD+R DL - BitSetRdl = 0x0E, + #region Kreon vendor commands + /// Most Kreon commands start with this + KreonCommand = 0xFF, - /// Plextor SpeedRead - SpeedRead = 0xBB - } + /// Kreon extract Security Sectors command start with this + KreonSsCommand = 0xAD, + #endregion Kreon vendor commands - public enum SscLogicalIdTypes : byte - { - /// Logical object identifier - ObjectId = 0, + #region MiniDisc vendor commands + /// Gets some list of pointers only present on MD-DATA discs + MiniDiscReadDTOC = 0xD1, + /// Writes some list of pointers only present on MD-DATA discs + MiniDiscWriteDTOC = 0xD2, + /// Reads UTOC + MiniDiscReadUTOC = 0xD4, + /// Unknown, returns 4 empty bytes + MiniDiscD5 = 0xD5, + /// Stops playing audio + MiniDiscStopPlay = 0xD6, + /// Gets current audio playing position + MiniDiscReadPosition = 0xD7, + /// Gets some values that are identical amongst audio discs and data discs, different between them + MiniDiscGetType = 0xD8, + #endregion - /// Logical file identifier - FileId = 1, + #region MediaTek vendor commands + MediaTekVendorCommand = 0xF1 + #endregion +} +#endregion SCSI Commands + +/// SCSI command transfer direction +public enum ScsiDirection +{ + /// No data transfer happens + None = 0, + + /// From host to device + Out = 1, + + /// From device to host + In = 2, + + /// Bidirectional device/host + Bidirectional = 3, + + /// Unspecified + Unspecified = -1 +} + +#region SCSI's ATA Command Pass-Through +public enum AtaProtocol : byte +{ + /// Requests a device hard reset (pin 1) + HardReset = 0, + + /// Requests a device soft reset (COMRESET issue) + SoftReset = 1, + + /// No data is to be transferred + NonData = 3, + + /// Requests a device->host transfer using PIO + PioIn = 4, + + /// Requests a host->device transfer using PIO + PioOut = 5, + + /// Requests a DMA transfer + Dma = 6, + + /// Requests to queue a DMA transfer + DmaQueued = 7, + + /// Requests device diagnostics + DeviceDiagnostic = 8, + + /// Requests device reset + DeviceReset = 9, + + /// Requests a device->host transfer using UltraDMA + UDmaIn = 10, + + /// Requests a host->device transfer using UltraDMA + UDmaOut = 11, + + /// Unknown Serial ATA + FpDma = 12, + + /// Requests the Extended ATA Status Return Descriptor + ReturnResponse = 15 +} + +/// Indicates the STL which ATA register contains the length of data to be transferred +public enum AtaTransferRegister : byte +{ + /// There is no transfer + NoTransfer = 0, + + /// FEATURE register contains the data length + Feature = 1, + + /// SECTOR_COUNT register contains the data length + SectorCount = 2, + + /// The STPSIU contains the data length + Sptsiu = 3 +} +#endregion SCSI's ATA Command Pass-Through + +/// ZBC sub-commands, mask 0x1F +public enum ZbcSubCommands : byte +{ + /// Returns list with zones of specified types + ReportZones = 0x00, - /// Logical set identifier - SetId = 2, + /// Closes a zone + CloseZone = 0x01, - /// Reserved - Reserved = 3 - } + /// Finishes a zone + FinishZone = 0x02, - public enum SscPositionForms : byte - { - /// 20 bytes using logical block addresses - Short = 0, + /// Opens a zone + OpenZone = 0x03, - /// 20 bytes using vendor-specified values - VendorShort = 1, + /// Resets zone's write pointer to zone start + ResetWritePointer = 0x04, - /// Equivalent to on SSC-1 - OldLong = 2, + /// Requests device to transfer parameters describing realms + ReportRealms = 0x06, + /// Requests device to transfer parameters describing the zone domains structure + ReportZoneDomains = 0x07, + /// Requests device to perform a zone activation operation + ZoneActivate = 0x08, + /// Requests information about a zone + ZoneQuery = 0x09, + /// Requests device to perform sequentialize zone operations + SequentializeZone = 0x10 +} - /// Invalid: Equivalent to LONG + BT on SSC-1 - OldLongVendor = 3, +/// MODE SENSE page control, mask 0xC0 +public enum ScsiModeSensePageControl : byte +{ + /// Current values + Current = 0x00, - /// Invalid: Equivalent to TCLP on SSC-1 - OldTclp = 4, + /// Changeable values + Changeable = 0x40, - /// Invalid: Equivalent to TCLP + BT on SSC-1 - OldTclpVendor = 5, + /// Default values + Default = 0x80, - /// 32 bytes - Long = 6, + /// Saved values + Saved = 0xC0 +} - /// Invalid: Equivalent to TCLP + LONG + BT on SSC-1 - OldLongTclpVendor = 7, +public enum ScsiPreventAllowMode : byte +{ + /// Allows medium removal from data transport and from medium changer + Allow = 0x00, - /// From 28 bytes to allocation length - Extended = 8 - } + /// Prevents medium removal from data transport but allows it from medium changer + Prevent = 0x01, - public enum ScsiAttributeAction : byte - { - /// Return attribute values - Values = 0, + /// Allows medium removal from data transport but prevents it from medium changer + PreventChanger = 0x02, - /// Return a list of available attributes - List = 1, + /// Prevents medium removal from both data transport and medium changer + PreventAll = 0x03 +} - /// Returns a list of known logical volume numbers - VolumeList = 2, +public enum MmcGetConfigurationRt : byte +{ + /// Drive shall return the Feature Header and all Feature Descriptors + All = 0x00, - /// Returns a list of known partition numbers - PartitionList = 3, + /// Drive shall return the Feature Header and current Feature Descriptors + Current = 0x01, - /// Returns a list of elements containing volumes with MAM - ElementList = 4, + /// Drive shall return only the Feature Header with the chosen Feature Descriptor + Single = 0x02, Reserved = 0x03 +} - /// Returns a list of supported attribute identifiers - Supported = 5 - } +public enum MmcDiscStructureMediaType : byte +{ + /// Disc Structures for DVD and HD DVD + Dvd = 0x00, - public enum FujitsuDisplayModes : byte - { - /// Message is displayed until next tape operation starts - Idle = 0, + /// Disc Structures for BD + Bd = 0x01 +} - /// Message is displayed only if a cartridge is inserted, until its removal - Cart = 1, +public enum MmcDiscStructureFormat : byte +{ + // Generic Format Codes - /// Message is only displayed when drive is ready - Ready = 2, + /// AACS Volume Identifier + AacsVolId = 0x80, - /// Cancels current display - Cancel = 3, + /// AACS Pre-recorded Media Serial Number + AacsMediaSerial = 0x81, - /// - /// Message is displayed only if a cartridge is inserted. When removed, only second half of the message is - /// displayed. - /// - Half = 7 - } + /// AACS Media Identifier + AacsMediaId = 0x82, - // TODO: Check obsoletes - public enum SscSpaceCodes : byte - { - /// Logical blocks - LogicalBlock = 0, + /// AACS Lead-in Media Key Block + Aacsmkb = 0x83, - /// Filemarks - Filemark = 1, + /// AACS Data Keys + AacsDataKeys = 0x84, - /// Sequential filemarks - SequentialFilemark = 2, + /// AACS LBA extents + AacslbaExtents = 0x85, - /// End-of-data - EndOfData = 3, Obsolete1 = 4, Obsolete2 = 5 - } + /// CPRM Media Key Block specified by AACS + Aacsmkbcprm = 0x86, - /// MMC / SecureDigital commands - public enum MmcCommands : byte - { - #region Class 1 MMC Commands (Basic and read-stream) - /// Resets device to idle (BC) - GoIdle = 0, + /// Recognized format layers + RecognizedFormatLayers = 0x90, - /// Resets the device to pre-idle (BC) - GoPreIdleState = 0, + /// Write protection status + WriteProtectionStatus = 0xC0, - /// Initiate alternative boot operation - BootInitiation = 0, + /// READ/SEND DISC STRUCTURE capability list + CapabilityList = 0xFF, - /// Asks device in idle state to send their operation conditions in response (BCR, R3) - SendOpCond = 1, + // DVD Disc Structures + /// DVD Lead-in Physical Information + PhysicalInformation = 0x00, - /// Asks device to send their CID numbers (BCR, R2) - AllSendCid = 2, + /// DVD Lead-in Copyright Information + CopyrightInformation = 0x01, - /// Assigns a relative address to the device (AC, R1) - SetRelativeAddress = 3, + /// CSS/CPPM Disc key + DiscKey = 0x02, - /// Programs the DSR of the device (BC) - SetDsr = 4, + /// DVD Burst Cutting Area + BurstCuttingArea = 0x03, - /// Toggles the device between sleep and standby (AC, R1b) - SleepAwake = 5, + /// DVD Lead-in Disc Manufacturing Information + DiscManufacturingInformation = 0x04, - /// Switches device mode of operation (AC, R1b) - Switch = 6, + /// DVD Copyright Information from specified sector + SectorCopyrightInformation = 0x05, - /// - /// Toggles a device between the stand-by and transfer stats or between the programming and disconnect states (AC, - /// R1b) - /// - SelectCard = 7, + /// CSS/CPPM Media Identifier + MediaIdentifier = 0x06, - /// Asks device to send its extended card-specific data (ExtCSD) (ADTC, R1) - SendExtCsd = 8, + /// CSS/CPPM Media Key Block + MediaKeyBlock = 0x07, - /// Asks device to send its card-specific data (CSD) (AC, R2) - SendCsd = 9, + /// DDS from DVD-RAM + DvdramDds = 0x08, - /// Asks device to send its card identification (CID) (AC, R2) - SendCid = 10, + /// DVD-RAM Medium Status + DvdramMediumStatus = 0x09, - /// - /// Reads data stream from device, starting at given address, until a follows - /// (ADTC, R1) - /// - [Obsolete] - ReadDatUntilStop = 11, + /// DVD-RAM Spare Area Information + DvdramSpareAreaInformation = 0x0A, - /// Terminates a read/write stream/multiple block operation (AC, R1 / R1b) - StopTransmission = 12, + /// DVD-RAM Recording Type Information + DvdramRecordingType = 0x0B, - /// Asks device to send its status register (AC, R1) - SendStatus = 13, + /// DVD-R/-RW RMD in last Border-out + LastBorderOutRmd = 0x0C, - /// The host reads the reversed bus testing data pattern from a device (ADTC, R1) - BusTestRead = 14, + /// Specified RMD from last recorded Border-out + SpecifiedRmd = 0x0D, - /// Sets the card to inactive state (AC) - GoInactiveState = 15, + /// DVD-R/-RW Lead-in pre-recorded information + PreRecordedInfo = 0x0E, - /// The host sends the bus testing data pattern to a device (ADTC, R1) - BusTestWrite = 19, SpiReadOcr = 58, SpicrcOnOff = 59, - #endregion Class 1 MMC Commands (Basic and read-stream) + /// DVD-R/-RW Media Identifier + DvdrMediaIdentifier = 0x0F, - #region Class 2 MMC Commands (Block-oriented read) - /// Sets the block length in bytes (AC, R1) - SetBlocklen = 16, + /// DVD-R/-RW Physical Format Information + DvdrPhysicalInformation = 0x10, - /// Reads a block (ADTC, R1) - ReadSingleBlock = 17, + /// ADIP + Adip = 0x11, - /// Transfers data blocks from card to host until interrupted (ADTC, R1) - ReadMultipleBlock = 18, + /// HD DVD Lead-in Copyright Protection Information + HddvdCopyrightInformation = 0x12, - /// 128 blocks of tuning pattern is sent for HS200 optimal sampling point detection (ADTC, R1) - SendTuningBlockHs200 = 21, - #endregion Class 2 MMC Commands (Block-oriented read) + /// AACS Lead-in Copyright Data Section + DvdAacs = 0x15, - #region Class 3 MMC Commands (Stream write) - /// Writes data stream from host until a follows (ADTC, R1) - [Obsolete] - WriteDatUntilStop = 20, - #endregion Class 3 MMC Commands (Stream write) + /// HD DVD-R Medium Status + HddvdrMediumStatus = 0x19, - #region Class 4 MMC Commands (Block-oriented write) - /// - /// Defines the number of blocks which are going to be transferred in the immediately succeeding multiple block - /// command (AC, R1) - /// - SetBlockCount = 23, + /// HD DVD-R Last recorded RMD in the latest RMZ + HddvdrLastRmd = 0x1A, - /// Writes a block (ADTC, R1) - WriteBlock = 24, + /// DVD+/-R DL and DVD-Download DL layer capacity + DvdrLayerCapacity = 0x20, - /// Continuously writes blocks until interrupted (ADTC, R1) - WriteMultipleBlock = 25, + /// DVD-R DL Middle Zone start address + MiddleZoneStart = 0x21, - /// Programs the Card Information register (ADTC, R1) - ProgramCid = 26, + /// DVD-R DL Jump Interval Size + JumpIntervalSize = 0x22, - /// Programs the programmable bits of the CSD (ADTC, R1) - ProgramCsd = 27, + /// DVD-R DL Start LBA of the manual layer jump + ManualLayerJumpStartLba = 0x23, - /// Sets the real time clock according to information in block (ADTC, R1) - SetTime = 49, - #endregion Class 4 MMC Commands (Block-oriented write) + /// DVD-R DL Remapping information of the specified Anchor Point + RemapAnchorPoint = 0x24, - #region Class 5 MMC Commands (Erase) - /// Sets the address of the first erase group (AC, R1) - EraseGroupStart = 35, + /// Disc Control Block + Dcb = 0x30, - /// Sets the address of the last erase group (AC, R1) - EraseGroupEnd = 36, + // BD Disc Structures + /// Blu-ray Disc Information + DiscInformation = 0x00, - /// Erases previously selected write blocks (AC, R1b) - Erase = 38, - #endregion Class 5 MMC Commands (Erase) + /// Blu-ray Burst Cutting Area + BdBurstCuttingArea = 0x03, - #region Class 6 MMC Commands (Block-oriented write protection) - /// Sets the write protection bit (AC, R1b) - SetWriteProtect = 28, + /// Blu-ray DDS + BdDds = 0x08, - /// Clears the write protection bit (AC, R1b) - ClearWriteProtect = 29, + /// Blu-ray Cartridge Status + CartridgeStatus = 0x09, - /// Asks the device to send the status of the write protection bit (ADTC, R1) - SendWriteProtect = 30, + /// Blu-ray Spare Area Information + BdSpareAreaInformation = 0x0A, - /// Sends the type of write protection that is set for the different write protection groups (ADTC, R1) - SentWriteProtectType = 31, - #endregion Class 6 MMC Commands (Block-oriented write protection) + /// Unmodified DFL + RawDfl = 0x12, - #region Class 7 MMC Commands (Lock) - /// Used to set/reset the password or lock/unlock the card (ADTC, R1b) - LockUnlock = 42, - #endregion Class 7 MMC Commands (Lock) + /// Physical Access Control + Pac = 0x30 +} - #region Class 8 MMC Commands (Application-specific) - /// Indicates the card that the next command is an application specific command (AC, R1) - ApplicationCommand = 55, +public enum ScsiServiceActions : byte +{ + // SERVICE ACTION IN - /// Transfers a data block to/from the card for general purpose / application specific commands (ADTC, R1b) - GenericCommand = 56, - #endregion Class 8 MMC Commands (Application-specific) + /// Requests parameter data describing provisioning status for the specified LBA SBC-3 rev. 25 + GetLbaStatus = 0x12, - #region Class 9 MMC Commands (I/O mode) - /// - /// Used to write and read 8 bit data field, used to access application dependent registers not defined in MMC - /// standard (AC, R4) - /// - FastIo = 39, - - /// Sets the system into interrupt mode (BCR, R5) - GoIrqState = 40, - #endregion Class 9 MMC Commands (I/O mode) - - #region Class 10 MMC Commands (Security Protocols) - /// Reads data blocks (ADTC, R1) - ProtocolRead = 53, - - /// Writes data blocks (ADTC, R1) - ProtocolWrite = 54, - #endregion Class 10 MMC Commands (Security Protocols) - - #region Class 11 MMC Commands (Command Queue) - /// Defines data direction, priority, task ID and block count of queued task (AC, R1) - QueuedTaskParameters = 44, - - /// Defines the block address of queued task (AC, R1) - QueuedTaskAddress = 45, - - /// Executes the task queue for reading (ADTC, R1) - ExecuteTaskRead = 46, - - /// Executes the task queue for writing (ADTC, R1) - ExecuteTaskWrite = 47, - - /// Manages queues and tasks (AC, R1b) - CmdQTaskManagement = 48, - #endregion Class 11 MMC Commands (Command Queue) - - #region Class 1 SecureDigital Commands (Basic) - /// Sends SD interface condition (BCR, R7) - SendInterfaceCondition = 8, - - /// Switch to 1.8V bus signaling level (AC, R1) - VoltageSwitch = 11, - #endregion Class 1 SecureDigital Commands (Basic) + /// Gets device capacity SBC-2 rev. 4 + ReadCapacity16 = 0x10, - #region Class 2 SecureDigital Commands (Block-oriented read) - /// 64 bytes of tuning pattern is sent for SDR50 and SDR104 optimal sampling point detection (ADTC, R1) - SendTuningBlock = 19, + /// Reads blocks from device in a vendor-specific way that should include the ECC alongside the data SBC-2 rev. 4 + ReadLong16 = 0x11, - /// Speed class control command (AC, R1b) - SpeedClassControl = 20, - #endregion Class 2 SecureDigital Commands (Block-oriented read) + /// Requests information indicating the user data segments on the ports and LUNs to access them SBC-3 rev. 25 + ReportReferrals = 0x13, - #region Class 11 SecureDigital Commands (Function Extension) - /// Single block read type (ADTC, R1) - ReadExtraSingle = 48, + // SERVICE ACTION OUT - /// Single block write type (ADTC, R1) - WriteExtraSingle = 49, + /// + /// Writes blocks to the device with a vendor specified format that shall include the ECC alongside the data SBC-2 + /// rev. 4 + /// + WriteLong16 = ReadLong16 +} - /// Multiple block read type (ADTC, R1) - ReadExtraMulti = 58, +public enum MmcDiscInformationDataTypes : byte +{ + /// Standard Disc Information + DiscInformation = 0x00, - /// Multiple block write type (ADTC, R1) - WriteExtraMulti = 59, - #endregion Class 11 SecureDigital Commands (Function Extension) - } + /// Track Resources Information + TrackResources = 0x01, - /// SecureDigital application-specific commands - public enum SecureDigitalCommands : byte - { - /// Defines the data bus width to be used for data transfer (AC, R1) - SetBusWidth = 6, + /// POW Resources Information + PowResources = 0x02 +} - /// Sends the SD status register (ADTC, R1) - SendStatus = 13, +public enum MmcSectorTypes : byte +{ + /// No checking of data type is performed + AllTypes = 0x00, - /// Send the number of the written write blocks (ADTC, R1) - SendNumWriteBlocks = 22, + /// Only CD-DA sectors shall be returned + Cdda = 0x01, - /// Set the number of write blocks to be pre-erased before writing (AC, R1) - SetWriteBlockEraseCount = 23, + /// Only Mode 1 sectors shall be returned + Mode1 = 0x02, - /// Sends host capacity support information and asks the card to send its operating condition register (BCR, R3) - SendOperatingCondition = 41, + /// Only Mode 2 formless sectors shall be returned + Mode2 = 0x03, - /// Connects/Disconnects the 50 kOhm pull-up resistor on CD/DAT3 pin of card (AC, R1) - SetClearCardDetect = 42, + /// Only Mode 2 Form 1 sectors shall be returned + Mode2Form1 = 0x04, - /// Reads the SD Configuration Register SCR (ADTC, R1) - SendScr = 51 - } + /// Only Mode 2 Form 2 sectors shall be returned + Mode2Form2 = 0x05 +} - [Flags, SuppressMessage("ReSharper", "ShiftExpressionZeroLeftOperand")] - public enum MmcFlags : uint - { - ResponsePresent = 1 << 0, Response136 = 1 << 1, ResponseCrc = 1 << 2, - ResponseBusy = 1 << 3, ResponseOpcode = 1 << 4, CommandMask = 3 << 5, - CommandAc = 0 << 5, CommandAdtc = 1 << 5, CommandBc = 2 << 5, - CommandBcr = 3 << 5, ResponseSpiS1 = 1 << 7, ResponseSpiS2 = 1 << 8, - ResponseSpiB4 = 1 << 9, ResponseSpiBusy = 1 << 10, ResponseNone = 0, - ResponseR1 = ResponsePresent | ResponseCrc | ResponseOpcode, - ResponseR1B = ResponsePresent | ResponseCrc | ResponseOpcode | ResponseBusy, - ResponseR2 = ResponsePresent | Response136 | ResponseCrc, ResponseR3 = ResponsePresent, - ResponseR4 = ResponsePresent, ResponseR5 = ResponsePresent | ResponseCrc | ResponseOpcode, - ResponseR6 = ResponsePresent | ResponseCrc | ResponseOpcode, - ResponseR7 = ResponsePresent | ResponseCrc | ResponseOpcode, ResponseSpiR1 = ResponseSpiS1, - ResponseSpiR1B = ResponseSpiS1 | ResponseSpiBusy, ResponseSpiR2 = ResponseSpiS1 | ResponseSpiS2, - ResponseSpiR3 = ResponseSpiS1 | ResponseSpiB4, ResponseSpiR4 = ResponseSpiS1 | ResponseSpiB4, - ResponseSpiR5 = ResponseSpiS1 | ResponseSpiS2, ResponseSpiR7 = ResponseSpiS1 | ResponseSpiB4 - } +public enum MmcHeaderCodes : byte +{ + /// No header information shall be returned + None = 0x00, - [Flags] - public enum KreonFeatures - { - /// Drive can set the xtreme unlock state with Xbox 360 discs - XtremeUnlock360, + /// Only the four byte header shall be returned + HeaderOnly = 0x01, - /// Drive can set the wxripper unlock state with Xbox 360 discs - WxripperUnlock360, + /// Only the mode 2 form x subheader shall be returned + SubHeaderOnly = 0x02, - /// Drive can read and decrypt the SS from Xbox 360 discs - DecryptSs360, + /// Return both header and subheader + AllHeaders = 0x03 +} - /// Drive has full challenge response capabilities with Xbox 360 discs - ChallengeResponse360, +public enum MmcErrorField : byte +{ + /// No error information is returned + None = 0x00, - /// Drive can set the xtreme unlock state with Xbox discs - XtremeUnlock, + /// The C2 pointer bits will be included + C2Pointers = 0x01, - /// Drive can set the wxripper unlock state with Xbox discs - WxripperUnlock, + /// The C2 pointer bits will be included as well as the block error byte with a padding byte + C2PointersAndBlock = 0x02 +} - /// Drive can read and decrypt the SS from Xbox discs - DecryptSs, +public enum MmcSubchannel : byte +{ + /// No subchannel shall be returned + None = 0x00, - /// Drive has full challenge response capabilities with Xbox discs - ChallengeResponse, + /// The raw P to W subchannel data shall be transferred + Raw = 0x01, - /// Drive supports the locked state - Lock, + /// Q data shall be transferred + Q16 = 0x02, - /// Drive supports skipping read errors - ErrorSkipping - } + /// De-interleaved and error-corrected R to W subchannel data shall be transferred + Rw = 0x04 +} - public enum AtaFeatures : byte - { - /// Enable 8-bit data transfers - Enable8Bit = 0x01, +public enum PioneerSubchannel : byte +{ + /// No subchannel shall be returned + None = 0x00, - /// Enable write cache - EnableWriteCache = 0x02, + /// Q data shall be transferred + Q16 = 0x01, - /// Set transfer mode based on value in sector count register - SetTransferMode = 0x03, + /// The raw P to W subchannel data shall be transferred + All = 0x02, - /// Enable all automatic defect reassignment - EnableDefectReassignment = 0x04, + /// The raw P to W subchannel data shall be transferred WITHOUT user data + Only = 0x03 +} - /// Enable advanced power management - EnableApm = 0x05, +public enum PlextorSubchannel : byte +{ + /// No subchannel shall be returned + None = 0x00, - /// Enable Power-Up In Standby feature set - EnablePowerUpInStandby = 0x06, + /// Q data shall be transferred + Q16 = 0x01, - /// Power-Up In Standby feature set device spin-up - PowerUpInStandByFeature = 0x07, + /// The packed and corrected P to W subchannel data shall be transferred + Pack = 0x02, - /// Reserved for Address offset reserved area boot method technical report - AddressOffsetReserved = 0x09, + /// The raw P to W subchannel data shall be transferred + All = 0x03, - /// Enable CFA power mode 1 - EnableCfaPowerMode1 = 0x0A, + /// The raw P to W subchannel data, plus C2 error data shall be transferred + RawC2 = 0x08 +} - /// Enable Write-Read-Verify feature set - EnableWriteReadVerify = 0x0B, +public enum PlextorSubCommands : byte +{ + /// Gets Plextor mode + GetMode = 0x00, - /// Enable use of SATA feature - EnableSataFeature = 0x10, + /// Sets Plextor mode + SetMode = 0x10, - /// Disable Media Status Notification - DisableMediaStatusNotification = 0x31, + /// Plextor force single session or hide CD-R + SessionHide = 0x01, - /// Disable retry - DisableRetry = 0x33, + /// Plextor VariRec + VariRec = 0x02, - /// Enable Free-fall Control - EnableFreeFall = 0x41, + /// Plextor GigaRec + GigaRec = 0x04, - /// Enable Automatic Acoustic Management feature set - EnableAam = 0x42, + /// Plextor acoustic management (disc related) + SilentDisc = 0x06, - /// Set Maximum Host Interface Sector Times - SetMaximumHostInterfaceSectorTimes = 0x43, + /// Plextor acoustic management (tra related) + SilentTray = 0x07, - /// Vendor unique length of ECC on read long/write long commands - EnableReadLongVendorLength = 0x44, + /// Plextor acoustic management + Silent = 0x08, - /// Extended Power conditions - ExtendedPowerConditions = 0x4A, + /// Plextor test write DVD+ + TestWriteDvdPlus = 0x21, - /// Set cache segments to sector count register value - SetCacheSegments = 0x54, + /// Plextor book setting + BitSet = 0x22, - /// Disable read look-ahead feature - DisableReadLookAhead = 0x55, + /// Plextor SecuRec + SecuRec = 0xD5, - /// Enable release interrupt - EnableReleaseInterrupt = 0x5D, + /// Book setting for DVD+R + BitSetR = 0x0A, - /// Enable SERVICE interrupt - EnableServiceInterrupt = 0x5E, + /// Book setting for DVD+R DL + BitSetRdl = 0x0E, - /// Long Physical Sector Alignment Error Reporting Control - LongPhysicalSectorErrorControl = 0x62, + /// Plextor SpeedRead + SpeedRead = 0xBB +} - /// Enable/Disable the DSN feature set - DsnFeature = 0x63, +public enum SscLogicalIdTypes : byte +{ + /// Logical object identifier + ObjectId = 0, - /// Disable reverting to power on defaults - DisableRevertToDefaults = 0x66, + /// Logical file identifier + FileId = 1, - /// Disable ECC - DisableEcc = 0x77, + /// Logical set identifier + SetId = 2, - /// Disable 8-bit data transfers - Disable8Bit = 0x81, + /// Reserved + Reserved = 3 +} - /// Disable write cache - DisableWriteCache = 0x82, +public enum SscPositionForms : byte +{ + /// 20 bytes using logical block addresses + Short = 0, - /// Disable all automatic defect reassignment - DisableDefectReassignment = 0x84, + /// 20 bytes using vendor-specified values + VendorShort = 1, - /// Disable advanced power management - DisableApm = 0x85, + /// Equivalent to on SSC-1 + OldLong = 2, - /// Disable Power-Up In Standby feature set - DisablePowerUpInStandby = 0x86, + /// Invalid: Equivalent to LONG + BT on SSC-1 + OldLongVendor = 3, - /// Enable ECC - EnableEcc = 0x88, + /// Invalid: Equivalent to TCLP on SSC-1 + OldTclp = 4, - /// Reserved for Address offset reserved area boot method technical report - AddressOffsetReserved2 = 0x89, + /// Invalid: Equivalent to TCLP + BT on SSC-1 + OldTclpVendor = 5, - /// Disable CFA power mode 1 - DisableCfaPowerMode1 = 0x8A, + /// 32 bytes + Long = 6, - /// Disable Write-Read-Verify feature set - DisableWriteReadVerify = 0x8B, + /// Invalid: Equivalent to TCLP + LONG + BT on SSC-1 + OldLongTclpVendor = 7, - /// Disable use of SATA feature - DisableSataFeature = 0x90, + /// From 28 bytes to allocation length + Extended = 8 +} - /// Enable Media Status Notification - EnableMediaStatusNotification = 0x95, +public enum ScsiAttributeAction : byte +{ + /// Return attribute values + Values = 0, - /// Enable retries - EnableRetries = 0x99, + /// Return a list of available attributes + List = 1, - /// Set device maximum average current - SetMaximumAverageCurrent = 0x9A, + /// Returns a list of known logical volume numbers + VolumeList = 2, - /// Enable read look-ahead feature - EnableReadLookAhead = 0xAA, + /// Returns a list of known partition numbers + PartitionList = 3, - /// Set maximum prefetch using sector count register value - SetMaximumPrefetch = 0xAB, + /// Returns a list of elements containing volumes with MAM + ElementList = 4, - /// 4 bytes of ECC apply on read long/write long commands - DisableReadLongVendorLength = 0xBB, + /// Returns a list of supported attribute identifiers + Supported = 5 +} - /// Disable Free-fall Control - DisableFreeFall = 0xC1, +public enum FujitsuDisplayModes : byte +{ + /// Message is displayed until next tape operation starts + Idle = 0, - /// Disable Automatic Acoustic Management feature set - DisableAam = 0xC2, + /// Message is displayed only if a cartridge is inserted, until its removal + Cart = 1, - /// Enable/Disable the Sense Data Reporting feature set - SenseDataReporting = 0xC3, + /// Message is only displayed when drive is ready + Ready = 2, - /// Enable reverting to power on defaults - EnableRevertToDefaults = 0xCC, + /// Cancels current display + Cancel = 3, - /// Disable release interrupt - DisableReleaseInterrupt = 0xDD, + /// + /// Message is displayed only if a cartridge is inserted. When removed, only second half of the message is + /// displayed. + /// + Half = 7 +} - /// Disable SERVICE interrupt - DisableServiceInterrupt = 0xDE, VendorSpecific = 0xE0 - } +// TODO: Check obsoletes +public enum SscSpaceCodes : byte +{ + /// Logical blocks + LogicalBlock = 0, - public enum KreonLockStates : byte - { - Locked = 0, Xtreme = 1, Wxripper = 2 - } + /// Filemarks + Filemark = 1, - public enum RotationalControl : byte - { - ClvAndImpureCav = 0, PureCav = 1 - } + /// Sequential filemarks + SequentialFilemark = 2, - public enum TrackInformationType : byte - { - LogicalBlockAddress = 0, LogicalTrackNumber = 1, SessionNumber = 2 - } + /// End-of-data + EndOfData = 3, Obsolete1 = 4, Obsolete2 = 5 +} - public enum CssReportKeyFormat : byte - { - AgidForCssCppm = 0x00, ChallengeKey = 0x01, Key1 = 0x02, - TitleKey = 0x04, Asf = 0x05, RpcState = 0x08, - AgidForCprm = 0x11, InvalidateAgid = 0x3f - } +/// MMC / SecureDigital commands +public enum MmcCommands : byte +{ + #region Class 1 MMC Commands (Basic and read-stream) + /// Resets device to idle (BC) + GoIdle = 0, - public enum CssSendKeyFormat : byte - { - ChallengeKey = 0x01, Key2 = 0x03, RpcStructure = 0x06, - InvalidateAgid = 0x3f - } + /// Resets the device to pre-idle (BC) + GoPreIdleState = 0, + + /// Initiate alternative boot operation + BootInitiation = 0, + + /// Asks device in idle state to send their operation conditions in response (BCR, R3) + SendOpCond = 1, + + /// Asks device to send their CID numbers (BCR, R2) + AllSendCid = 2, + + /// Assigns a relative address to the device (AC, R1) + SetRelativeAddress = 3, + + /// Programs the DSR of the device (BC) + SetDsr = 4, + + /// Toggles the device between sleep and standby (AC, R1b) + SleepAwake = 5, + + /// Switches device mode of operation (AC, R1b) + Switch = 6, + + /// + /// Toggles a device between the stand-by and transfer stats or between the programming and disconnect states (AC, + /// R1b) + /// + SelectCard = 7, + + /// Asks device to send its extended card-specific data (ExtCSD) (ADTC, R1) + SendExtCsd = 8, + + /// Asks device to send its card-specific data (CSD) (AC, R2) + SendCsd = 9, + + /// Asks device to send its card identification (CID) (AC, R2) + SendCid = 10, + + /// + /// Reads data stream from device, starting at given address, until a follows + /// (ADTC, R1) + /// + [Obsolete] + ReadDatUntilStop = 11, + + /// Terminates a read/write stream/multiple block operation (AC, R1 / R1b) + StopTransmission = 12, + + /// Asks device to send its status register (AC, R1) + SendStatus = 13, + + /// The host reads the reversed bus testing data pattern from a device (ADTC, R1) + BusTestRead = 14, + + /// Sets the card to inactive state (AC) + GoInactiveState = 15, + + /// The host sends the bus testing data pattern to a device (ADTC, R1) + BusTestWrite = 19, SpiReadOcr = 58, SpicrcOnOff = 59, + #endregion Class 1 MMC Commands (Basic and read-stream) + + #region Class 2 MMC Commands (Block-oriented read) + /// Sets the block length in bytes (AC, R1) + SetBlocklen = 16, + + /// Reads a block (ADTC, R1) + ReadSingleBlock = 17, + + /// Transfers data blocks from card to host until interrupted (ADTC, R1) + ReadMultipleBlock = 18, + + /// 128 blocks of tuning pattern is sent for HS200 optimal sampling point detection (ADTC, R1) + SendTuningBlockHs200 = 21, + #endregion Class 2 MMC Commands (Block-oriented read) + + #region Class 3 MMC Commands (Stream write) + /// Writes data stream from host until a follows (ADTC, R1) + [Obsolete] + WriteDatUntilStop = 20, + #endregion Class 3 MMC Commands (Stream write) + + #region Class 4 MMC Commands (Block-oriented write) + /// + /// Defines the number of blocks which are going to be transferred in the immediately succeeding multiple block + /// command (AC, R1) + /// + SetBlockCount = 23, + + /// Writes a block (ADTC, R1) + WriteBlock = 24, + + /// Continuously writes blocks until interrupted (ADTC, R1) + WriteMultipleBlock = 25, + + /// Programs the Card Information register (ADTC, R1) + ProgramCid = 26, + + /// Programs the programmable bits of the CSD (ADTC, R1) + ProgramCsd = 27, + + /// Sets the real time clock according to information in block (ADTC, R1) + SetTime = 49, + #endregion Class 4 MMC Commands (Block-oriented write) + + #region Class 5 MMC Commands (Erase) + /// Sets the address of the first erase group (AC, R1) + EraseGroupStart = 35, + + /// Sets the address of the last erase group (AC, R1) + EraseGroupEnd = 36, + + /// Erases previously selected write blocks (AC, R1b) + Erase = 38, + #endregion Class 5 MMC Commands (Erase) + + #region Class 6 MMC Commands (Block-oriented write protection) + /// Sets the write protection bit (AC, R1b) + SetWriteProtect = 28, + + /// Clears the write protection bit (AC, R1b) + ClearWriteProtect = 29, + + /// Asks the device to send the status of the write protection bit (ADTC, R1) + SendWriteProtect = 30, + + /// Sends the type of write protection that is set for the different write protection groups (ADTC, R1) + SentWriteProtectType = 31, + #endregion Class 6 MMC Commands (Block-oriented write protection) + + #region Class 7 MMC Commands (Lock) + /// Used to set/reset the password or lock/unlock the card (ADTC, R1b) + LockUnlock = 42, + #endregion Class 7 MMC Commands (Lock) + + #region Class 8 MMC Commands (Application-specific) + /// Indicates the card that the next command is an application specific command (AC, R1) + ApplicationCommand = 55, + + /// Transfers a data block to/from the card for general purpose / application specific commands (ADTC, R1b) + GenericCommand = 56, + #endregion Class 8 MMC Commands (Application-specific) + + #region Class 9 MMC Commands (I/O mode) + /// + /// Used to write and read 8 bit data field, used to access application dependent registers not defined in MMC + /// standard (AC, R4) + /// + FastIo = 39, + + /// Sets the system into interrupt mode (BCR, R5) + GoIrqState = 40, + #endregion Class 9 MMC Commands (I/O mode) + + #region Class 10 MMC Commands (Security Protocols) + /// Reads data blocks (ADTC, R1) + ProtocolRead = 53, + + /// Writes data blocks (ADTC, R1) + ProtocolWrite = 54, + #endregion Class 10 MMC Commands (Security Protocols) + + #region Class 11 MMC Commands (Command Queue) + /// Defines data direction, priority, task ID and block count of queued task (AC, R1) + QueuedTaskParameters = 44, + + /// Defines the block address of queued task (AC, R1) + QueuedTaskAddress = 45, + + /// Executes the task queue for reading (ADTC, R1) + ExecuteTaskRead = 46, + + /// Executes the task queue for writing (ADTC, R1) + ExecuteTaskWrite = 47, + + /// Manages queues and tasks (AC, R1b) + CmdQTaskManagement = 48, + #endregion Class 11 MMC Commands (Command Queue) + + #region Class 1 SecureDigital Commands (Basic) + /// Sends SD interface condition (BCR, R7) + SendInterfaceCondition = 8, + + /// Switch to 1.8V bus signaling level (AC, R1) + VoltageSwitch = 11, + #endregion Class 1 SecureDigital Commands (Basic) + + #region Class 2 SecureDigital Commands (Block-oriented read) + /// 64 bytes of tuning pattern is sent for SDR50 and SDR104 optimal sampling point detection (ADTC, R1) + SendTuningBlock = 19, + + /// Speed class control command (AC, R1b) + SpeedClassControl = 20, + #endregion Class 2 SecureDigital Commands (Block-oriented read) + + #region Class 11 SecureDigital Commands (Function Extension) + /// Single block read type (ADTC, R1) + ReadExtraSingle = 48, + + /// Single block write type (ADTC, R1) + WriteExtraSingle = 49, + + /// Multiple block read type (ADTC, R1) + ReadExtraMulti = 58, + + /// Multiple block write type (ADTC, R1) + WriteExtraMulti = 59, + #endregion Class 11 SecureDigital Commands (Function Extension) +} + +/// SecureDigital application-specific commands +public enum SecureDigitalCommands : byte +{ + /// Defines the data bus width to be used for data transfer (AC, R1) + SetBusWidth = 6, + + /// Sends the SD status register (ADTC, R1) + SendStatus = 13, + + /// Send the number of the written write blocks (ADTC, R1) + SendNumWriteBlocks = 22, + + /// Set the number of write blocks to be pre-erased before writing (AC, R1) + SetWriteBlockEraseCount = 23, + + /// Sends host capacity support information and asks the card to send its operating condition register (BCR, R3) + SendOperatingCondition = 41, + + /// Connects/Disconnects the 50 kOhm pull-up resistor on CD/DAT3 pin of card (AC, R1) + SetClearCardDetect = 42, + + /// Reads the SD Configuration Register SCR (ADTC, R1) + SendScr = 51 +} + +[Flags, SuppressMessage("ReSharper", "ShiftExpressionZeroLeftOperand")] +public enum MmcFlags : uint +{ + ResponsePresent = 1 << 0, Response136 = 1 << 1, ResponseCrc = 1 << 2, + ResponseBusy = 1 << 3, ResponseOpcode = 1 << 4, CommandMask = 3 << 5, + CommandAc = 0 << 5, CommandAdtc = 1 << 5, CommandBc = 2 << 5, + CommandBcr = 3 << 5, ResponseSpiS1 = 1 << 7, ResponseSpiS2 = 1 << 8, + ResponseSpiB4 = 1 << 9, ResponseSpiBusy = 1 << 10, ResponseNone = 0, + ResponseR1 = ResponsePresent | ResponseCrc | ResponseOpcode, + ResponseR1B = ResponsePresent | ResponseCrc | ResponseOpcode | ResponseBusy, + ResponseR2 = ResponsePresent | Response136 | ResponseCrc, ResponseR3 = ResponsePresent, + ResponseR4 = ResponsePresent, ResponseR5 = ResponsePresent | ResponseCrc | ResponseOpcode, + ResponseR6 = ResponsePresent | ResponseCrc | ResponseOpcode, + ResponseR7 = ResponsePresent | ResponseCrc | ResponseOpcode, ResponseSpiR1 = ResponseSpiS1, + ResponseSpiR1B = ResponseSpiS1 | ResponseSpiBusy, ResponseSpiR2 = ResponseSpiS1 | ResponseSpiS2, + ResponseSpiR3 = ResponseSpiS1 | ResponseSpiB4, ResponseSpiR4 = ResponseSpiS1 | ResponseSpiB4, + ResponseSpiR5 = ResponseSpiS1 | ResponseSpiS2, ResponseSpiR7 = ResponseSpiS1 | ResponseSpiB4 +} + +[Flags] +public enum KreonFeatures +{ + /// Drive can set the xtreme unlock state with Xbox 360 discs + XtremeUnlock360, + + /// Drive can set the wxripper unlock state with Xbox 360 discs + WxripperUnlock360, + + /// Drive can read and decrypt the SS from Xbox 360 discs + DecryptSs360, + + /// Drive has full challenge response capabilities with Xbox 360 discs + ChallengeResponse360, + + /// Drive can set the xtreme unlock state with Xbox discs + XtremeUnlock, + + /// Drive can set the wxripper unlock state with Xbox discs + WxripperUnlock, + + /// Drive can read and decrypt the SS from Xbox discs + DecryptSs, + + /// Drive has full challenge response capabilities with Xbox discs + ChallengeResponse, + + /// Drive supports the locked state + Lock, + + /// Drive supports skipping read errors + ErrorSkipping +} + +public enum AtaFeatures : byte +{ + /// Enable 8-bit data transfers + Enable8Bit = 0x01, + + /// Enable write cache + EnableWriteCache = 0x02, + + /// Set transfer mode based on value in sector count register + SetTransferMode = 0x03, + + /// Enable all automatic defect reassignment + EnableDefectReassignment = 0x04, + + /// Enable advanced power management + EnableApm = 0x05, + + /// Enable Power-Up In Standby feature set + EnablePowerUpInStandby = 0x06, + + /// Power-Up In Standby feature set device spin-up + PowerUpInStandByFeature = 0x07, + + /// Reserved for Address offset reserved area boot method technical report + AddressOffsetReserved = 0x09, + + /// Enable CFA power mode 1 + EnableCfaPowerMode1 = 0x0A, + + /// Enable Write-Read-Verify feature set + EnableWriteReadVerify = 0x0B, + + /// Enable use of SATA feature + EnableSataFeature = 0x10, + + /// Disable Media Status Notification + DisableMediaStatusNotification = 0x31, + + /// Disable retry + DisableRetry = 0x33, + + /// Enable Free-fall Control + EnableFreeFall = 0x41, + + /// Enable Automatic Acoustic Management feature set + EnableAam = 0x42, + + /// Set Maximum Host Interface Sector Times + SetMaximumHostInterfaceSectorTimes = 0x43, + + /// Vendor unique length of ECC on read long/write long commands + EnableReadLongVendorLength = 0x44, + + /// Extended Power conditions + ExtendedPowerConditions = 0x4A, + + /// Set cache segments to sector count register value + SetCacheSegments = 0x54, + + /// Disable read look-ahead feature + DisableReadLookAhead = 0x55, + + /// Enable release interrupt + EnableReleaseInterrupt = 0x5D, + + /// Enable SERVICE interrupt + EnableServiceInterrupt = 0x5E, + + /// Long Physical Sector Alignment Error Reporting Control + LongPhysicalSectorErrorControl = 0x62, + + /// Enable/Disable the DSN feature set + DsnFeature = 0x63, + + /// Disable reverting to power on defaults + DisableRevertToDefaults = 0x66, + + /// Disable ECC + DisableEcc = 0x77, + + /// Disable 8-bit data transfers + Disable8Bit = 0x81, + + /// Disable write cache + DisableWriteCache = 0x82, + + /// Disable all automatic defect reassignment + DisableDefectReassignment = 0x84, + + /// Disable advanced power management + DisableApm = 0x85, + + /// Disable Power-Up In Standby feature set + DisablePowerUpInStandby = 0x86, + + /// Enable ECC + EnableEcc = 0x88, + + /// Reserved for Address offset reserved area boot method technical report + AddressOffsetReserved2 = 0x89, + + /// Disable CFA power mode 1 + DisableCfaPowerMode1 = 0x8A, + + /// Disable Write-Read-Verify feature set + DisableWriteReadVerify = 0x8B, + + /// Disable use of SATA feature + DisableSataFeature = 0x90, + + /// Enable Media Status Notification + EnableMediaStatusNotification = 0x95, + + /// Enable retries + EnableRetries = 0x99, + + /// Set device maximum average current + SetMaximumAverageCurrent = 0x9A, + + /// Enable read look-ahead feature + EnableReadLookAhead = 0xAA, + + /// Set maximum prefetch using sector count register value + SetMaximumPrefetch = 0xAB, + + /// 4 bytes of ECC apply on read long/write long commands + DisableReadLongVendorLength = 0xBB, + + /// Disable Free-fall Control + DisableFreeFall = 0xC1, + + /// Disable Automatic Acoustic Management feature set + DisableAam = 0xC2, + + /// Enable/Disable the Sense Data Reporting feature set + SenseDataReporting = 0xC3, + + /// Enable reverting to power on defaults + EnableRevertToDefaults = 0xCC, + + /// Disable release interrupt + DisableReleaseInterrupt = 0xDD, + + /// Disable SERVICE interrupt + DisableServiceInterrupt = 0xDE, VendorSpecific = 0xE0 +} + +public enum KreonLockStates : byte +{ + Locked = 0, Xtreme = 1, Wxripper = 2 +} + +public enum RotationalControl : byte +{ + ClvAndImpureCav = 0, PureCav = 1 +} + +public enum TrackInformationType : byte +{ + LogicalBlockAddress = 0, LogicalTrackNumber = 1, SessionNumber = 2 +} + +public enum CssReportKeyFormat : byte +{ + AgidForCssCppm = 0x00, ChallengeKey = 0x01, Key1 = 0x02, + TitleKey = 0x04, Asf = 0x05, RpcState = 0x08, + AgidForCprm = 0x11, InvalidateAgid = 0x3f +} + +public enum CssSendKeyFormat : byte +{ + ChallengeKey = 0x01, Key2 = 0x03, RpcStructure = 0x06, + InvalidateAgid = 0x3f } \ No newline at end of file diff --git a/Aaru.Devices/Linux/Command.cs b/Aaru.Devices/Linux/Command.cs index 0576d80a5..50fca0ceb 100644 --- a/Aaru.Devices/Linux/Command.cs +++ b/Aaru.Devices/Linux/Command.cs @@ -37,386 +37,450 @@ using System.Text; using Aaru.CommonTypes.Interop; using Aaru.Decoders.ATA; -namespace Aaru.Devices.Linux +namespace Aaru.Devices.Linux; + +internal static class Command { - internal static class Command + /// Sends a SCSI command + /// 0 if no error occurred, otherwise, errno + /// File handle + /// SCSI CDB + /// Buffer for SCSI command response + /// Buffer with the SCSI sense + /// Timeout in seconds + /// SCSI command transfer direction + /// Time it took to execute the command in milliseconds + /// + /// True if SCSI error returned non-OK status and contains SCSI + /// sense + /// + internal static int SendScsiCommand(int fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, + ScsiIoctlDirection direction, out double duration, out bool sense) { - /// Sends a SCSI command - /// 0 if no error occurred, otherwise, errno - /// File handle - /// SCSI CDB - /// Buffer for SCSI command response - /// Buffer with the SCSI sense - /// Timeout in seconds - /// SCSI command transfer direction - /// Time it took to execute the command in milliseconds - /// - /// True if SCSI error returned non-OK status and contains SCSI - /// sense - /// - internal static int SendScsiCommand(int fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, - ScsiIoctlDirection direction, out double duration, out bool sense) + senseBuffer = null; + duration = 0; + sense = false; + + if(buffer == null) + return -1; + + var ioHdr = new SgIoHdrT(); + + senseBuffer = new byte[64]; + + ioHdr.interface_id = 'S'; + ioHdr.cmd_len = (byte)cdb.Length; + ioHdr.mx_sb_len = (byte)senseBuffer.Length; + ioHdr.dxfer_direction = direction; + ioHdr.dxfer_len = (uint)buffer.Length; + ioHdr.dxferp = Marshal.AllocHGlobal(buffer.Length); + ioHdr.cmdp = Marshal.AllocHGlobal(cdb.Length); + ioHdr.sbp = Marshal.AllocHGlobal(senseBuffer.Length); + ioHdr.timeout = timeout * 1000; + ioHdr.flags = (uint)SgFlags.DirectIo; + + Marshal.Copy(buffer, 0, ioHdr.dxferp, buffer.Length); + Marshal.Copy(cdb, 0, ioHdr.cmdp, cdb.Length); + Marshal.Copy(senseBuffer, 0, ioHdr.sbp, senseBuffer.Length); + + DateTime start = DateTime.UtcNow; + int error = Extern.ioctlSg(fd, LinuxIoctl.SgIo, ref ioHdr); + DateTime end = DateTime.UtcNow; + + if(error < 0) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(ioHdr.dxferp, buffer, 0, buffer.Length); + Marshal.Copy(ioHdr.cmdp, cdb, 0, cdb.Length); + Marshal.Copy(ioHdr.sbp, senseBuffer, 0, senseBuffer.Length); + + sense |= (ioHdr.info & SgInfo.OkMask) != SgInfo.Ok; + + duration = ioHdr.duration > 0 ? ioHdr.duration : (end - start).TotalMilliseconds; + + Marshal.FreeHGlobal(ioHdr.dxferp); + Marshal.FreeHGlobal(ioHdr.cmdp); + Marshal.FreeHGlobal(ioHdr.sbp); + + return error; + } + + /// Converts ATA protocol to SG_IO direction + /// ATA protocol + /// SG_IO direction + static ScsiIoctlDirection AtaProtocolToScsiDirection(AtaProtocol protocol) + { + switch(protocol) { - senseBuffer = null; - duration = 0; - sense = false; - - if(buffer == null) - return -1; - - var ioHdr = new SgIoHdrT(); - - senseBuffer = new byte[64]; - - ioHdr.interface_id = 'S'; - ioHdr.cmd_len = (byte)cdb.Length; - ioHdr.mx_sb_len = (byte)senseBuffer.Length; - ioHdr.dxfer_direction = direction; - ioHdr.dxfer_len = (uint)buffer.Length; - ioHdr.dxferp = Marshal.AllocHGlobal(buffer.Length); - ioHdr.cmdp = Marshal.AllocHGlobal(cdb.Length); - ioHdr.sbp = Marshal.AllocHGlobal(senseBuffer.Length); - ioHdr.timeout = timeout * 1000; - ioHdr.flags = (uint)SgFlags.DirectIo; - - Marshal.Copy(buffer, 0, ioHdr.dxferp, buffer.Length); - Marshal.Copy(cdb, 0, ioHdr.cmdp, cdb.Length); - Marshal.Copy(senseBuffer, 0, ioHdr.sbp, senseBuffer.Length); - - DateTime start = DateTime.UtcNow; - int error = Extern.ioctlSg(fd, LinuxIoctl.SgIo, ref ioHdr); - DateTime end = DateTime.UtcNow; - - if(error < 0) - error = Marshal.GetLastWin32Error(); - - Marshal.Copy(ioHdr.dxferp, buffer, 0, buffer.Length); - Marshal.Copy(ioHdr.cmdp, cdb, 0, cdb.Length); - Marshal.Copy(ioHdr.sbp, senseBuffer, 0, senseBuffer.Length); - - sense |= (ioHdr.info & SgInfo.OkMask) != SgInfo.Ok; - - duration = ioHdr.duration > 0 ? ioHdr.duration : (end - start).TotalMilliseconds; - - Marshal.FreeHGlobal(ioHdr.dxferp); - Marshal.FreeHGlobal(ioHdr.cmdp); - Marshal.FreeHGlobal(ioHdr.sbp); - - return error; + case AtaProtocol.DeviceDiagnostic: + case AtaProtocol.DeviceReset: + case AtaProtocol.HardReset: + case AtaProtocol.NonData: + case AtaProtocol.SoftReset: + case AtaProtocol.ReturnResponse: return ScsiIoctlDirection.None; + case AtaProtocol.PioIn: + case AtaProtocol.UDmaIn: return ScsiIoctlDirection.In; + case AtaProtocol.PioOut: + case AtaProtocol.UDmaOut: return ScsiIoctlDirection.Out; + default: return ScsiIoctlDirection.Unspecified; } + } - /// Converts ATA protocol to SG_IO direction - /// ATA protocol - /// SG_IO direction - static ScsiIoctlDirection AtaProtocolToScsiDirection(AtaProtocol protocol) + /// Sends an ATA command in CHS mode + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA error returned non-OK status + /// Registers to send to drive + /// Registers returned by drive + /// ATA protocol to use + /// Which register contains the transfer count + /// Set to true if the transfer count is in blocks, otherwise it is in bytes + internal static int SendAtaCommand(int fd, AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, + AtaProtocol protocol, AtaTransferRegister transferRegister, + ref byte[] buffer, uint timeout, bool transferBlocks, out double duration, + out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new AtaErrorRegistersChs(); + + if(buffer == null) + return -1; + + byte[] cdb = new byte[16]; + cdb[0] = (byte)ScsiCommands.AtaPassThrough16; + cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); + + if(transferRegister != AtaTransferRegister.NoTransfer && + protocol != AtaProtocol.NonData) { switch(protocol) { - case AtaProtocol.DeviceDiagnostic: - case AtaProtocol.DeviceReset: - case AtaProtocol.HardReset: - case AtaProtocol.NonData: - case AtaProtocol.SoftReset: - case AtaProtocol.ReturnResponse: return ScsiIoctlDirection.None; case AtaProtocol.PioIn: - case AtaProtocol.UDmaIn: return ScsiIoctlDirection.In; - case AtaProtocol.PioOut: - case AtaProtocol.UDmaOut: return ScsiIoctlDirection.Out; - default: return ScsiIoctlDirection.Unspecified; + case AtaProtocol.UDmaIn: + cdb[2] = 0x08; + + break; + default: + cdb[2] = 0x00; + + break; } + + if(transferBlocks) + cdb[2] |= 0x04; + + cdb[2] |= (byte)((int)transferRegister & 0x03); } - /// Sends an ATA command in CHS mode - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA error returned non-OK status - /// Registers to send to drive - /// Registers returned by drive - /// ATA protocol to use - /// Which register contains the transfer count - /// Set to true if the transfer count is in blocks, otherwise it is in bytes - internal static int SendAtaCommand(int fd, AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, - AtaProtocol protocol, AtaTransferRegister transferRegister, - ref byte[] buffer, uint timeout, bool transferBlocks, out double duration, - out bool sense) - { - duration = 0; - sense = false; - errorRegisters = new AtaErrorRegistersChs(); + //cdb[2] |= 0x20; - if(buffer == null) - return -1; + cdb[4] = registers.Feature; + cdb[6] = registers.SectorCount; + cdb[8] = registers.Sector; + cdb[10] = registers.CylinderLow; + cdb[12] = registers.CylinderHigh; + cdb[13] = registers.DeviceHead; + cdb[14] = registers.Command; - byte[] cdb = new byte[16]; - cdb[0] = (byte)ScsiCommands.AtaPassThrough16; - cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); - - if(transferRegister != AtaTransferRegister.NoTransfer && - protocol != AtaProtocol.NonData) - { - switch(protocol) - { - case AtaProtocol.PioIn: - case AtaProtocol.UDmaIn: - cdb[2] = 0x08; - - break; - default: - cdb[2] = 0x00; - - break; - } - - if(transferBlocks) - cdb[2] |= 0x04; - - cdb[2] |= (byte)((int)transferRegister & 0x03); - } - - //cdb[2] |= 0x20; - - cdb[4] = registers.Feature; - cdb[6] = registers.SectorCount; - cdb[8] = registers.Sector; - cdb[10] = registers.CylinderLow; - cdb[12] = registers.CylinderHigh; - cdb[13] = registers.DeviceHead; - cdb[14] = registers.Command; - - int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, - AtaProtocolToScsiDirection(protocol), out duration, out sense); - - if(senseBuffer.Length < 22 || - (senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C)) - return error; - - errorRegisters.Error = senseBuffer[11]; - - errorRegisters.SectorCount = senseBuffer[13]; - errorRegisters.Sector = senseBuffer[15]; - errorRegisters.CylinderLow = senseBuffer[17]; - errorRegisters.CylinderHigh = senseBuffer[19]; - errorRegisters.DeviceHead = senseBuffer[20]; - errorRegisters.Status = senseBuffer[21]; - - sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; + int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, + AtaProtocolToScsiDirection(protocol), out duration, out sense); + if(senseBuffer.Length < 22 || + (senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C)) return error; - } - /// Sends an ATA command in 28-bit LBA mode - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA error returned non-OK status - /// Registers to send to drive - /// Registers returned by drive - /// ATA protocol to use - /// Which register contains the transfer count - /// Set to true if the transfer count is in blocks, otherwise it is in bytes - internal static int SendAtaCommand(int fd, AtaRegistersLba28 registers, - out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) + errorRegisters.Error = senseBuffer[11]; + + errorRegisters.SectorCount = senseBuffer[13]; + errorRegisters.Sector = senseBuffer[15]; + errorRegisters.CylinderLow = senseBuffer[17]; + errorRegisters.CylinderHigh = senseBuffer[19]; + errorRegisters.DeviceHead = senseBuffer[20]; + errorRegisters.Status = senseBuffer[21]; + + sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; + + return error; + } + + /// Sends an ATA command in 28-bit LBA mode + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA error returned non-OK status + /// Registers to send to drive + /// Registers returned by drive + /// ATA protocol to use + /// Which register contains the transfer count + /// Set to true if the transfer count is in blocks, otherwise it is in bytes + internal static int SendAtaCommand(int fd, AtaRegistersLba28 registers, + out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new AtaErrorRegistersLba28(); + + if(buffer == null) + return -1; + + byte[] cdb = new byte[16]; + cdb[0] = (byte)ScsiCommands.AtaPassThrough16; + cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); + + if(transferRegister != AtaTransferRegister.NoTransfer && + protocol != AtaProtocol.NonData) { - duration = 0; - sense = false; - errorRegisters = new AtaErrorRegistersLba28(); - - if(buffer == null) - return -1; - - byte[] cdb = new byte[16]; - cdb[0] = (byte)ScsiCommands.AtaPassThrough16; - cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); - - if(transferRegister != AtaTransferRegister.NoTransfer && - protocol != AtaProtocol.NonData) + switch(protocol) { - switch(protocol) - { - case AtaProtocol.PioIn: - case AtaProtocol.UDmaIn: - cdb[2] = 0x08; + case AtaProtocol.PioIn: + case AtaProtocol.UDmaIn: + cdb[2] = 0x08; - break; - default: - cdb[2] = 0x00; + break; + default: + cdb[2] = 0x00; - break; - } - - if(transferBlocks) - cdb[2] |= 0x04; - - cdb[2] |= (byte)((int)transferRegister & 0x03); + break; } - cdb[2] |= 0x20; + if(transferBlocks) + cdb[2] |= 0x04; - cdb[4] = registers.Feature; - cdb[6] = registers.SectorCount; - cdb[8] = registers.LbaLow; - cdb[10] = registers.LbaMid; - cdb[12] = registers.LbaHigh; - cdb[13] = registers.DeviceHead; - cdb[14] = registers.Command; - - int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, - AtaProtocolToScsiDirection(protocol), out duration, out sense); - - if(senseBuffer.Length < 22 || - (senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C)) - return error; - - errorRegisters.Error = senseBuffer[11]; - - errorRegisters.SectorCount = senseBuffer[13]; - errorRegisters.LbaLow = senseBuffer[15]; - errorRegisters.LbaMid = senseBuffer[17]; - errorRegisters.LbaHigh = senseBuffer[19]; - errorRegisters.DeviceHead = senseBuffer[20]; - errorRegisters.Status = senseBuffer[21]; - - sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; - - return error; + cdb[2] |= (byte)((int)transferRegister & 0x03); } - /// Sends an ATA command in 48-bit LBA mode - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA error returned non-OK status - /// Registers to send to drive - /// Registers returned by drive - /// ATA protocol to use - /// Which register contains the transfer count - /// Set to true if the transfer count is in blocks, otherwise it is in bytes - internal static int SendAtaCommand(int fd, AtaRegistersLba48 registers, - out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, - AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, - bool transferBlocks, out double duration, out bool sense) + cdb[2] |= 0x20; + + cdb[4] = registers.Feature; + cdb[6] = registers.SectorCount; + cdb[8] = registers.LbaLow; + cdb[10] = registers.LbaMid; + cdb[12] = registers.LbaHigh; + cdb[13] = registers.DeviceHead; + cdb[14] = registers.Command; + + int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, + AtaProtocolToScsiDirection(protocol), out duration, out sense); + + if(senseBuffer.Length < 22 || + (senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C)) + return error; + + errorRegisters.Error = senseBuffer[11]; + + errorRegisters.SectorCount = senseBuffer[13]; + errorRegisters.LbaLow = senseBuffer[15]; + errorRegisters.LbaMid = senseBuffer[17]; + errorRegisters.LbaHigh = senseBuffer[19]; + errorRegisters.DeviceHead = senseBuffer[20]; + errorRegisters.Status = senseBuffer[21]; + + sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; + + return error; + } + + /// Sends an ATA command in 48-bit LBA mode + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA error returned non-OK status + /// Registers to send to drive + /// Registers returned by drive + /// ATA protocol to use + /// Which register contains the transfer count + /// Set to true if the transfer count is in blocks, otherwise it is in bytes + internal static int SendAtaCommand(int fd, AtaRegistersLba48 registers, + out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, + AtaTransferRegister transferRegister, ref byte[] buffer, uint timeout, + bool transferBlocks, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new AtaErrorRegistersLba48(); + + if(buffer == null) + return -1; + + byte[] cdb = new byte[16]; + cdb[0] = (byte)ScsiCommands.AtaPassThrough16; + cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); + cdb[1] |= 0x01; + + if(transferRegister != AtaTransferRegister.NoTransfer && + protocol != AtaProtocol.NonData) { - duration = 0; - sense = false; - errorRegisters = new AtaErrorRegistersLba48(); - - if(buffer == null) - return -1; - - byte[] cdb = new byte[16]; - cdb[0] = (byte)ScsiCommands.AtaPassThrough16; - cdb[1] = (byte)(((byte)protocol << 1) & 0x1E); - cdb[1] |= 0x01; - - if(transferRegister != AtaTransferRegister.NoTransfer && - protocol != AtaProtocol.NonData) + switch(protocol) { - switch(protocol) - { - case AtaProtocol.PioIn: - case AtaProtocol.UDmaIn: - cdb[2] = 0x08; + case AtaProtocol.PioIn: + case AtaProtocol.UDmaIn: + cdb[2] = 0x08; - break; - default: - cdb[2] = 0x00; + break; + default: + cdb[2] = 0x00; - break; - } - - if(transferBlocks) - cdb[2] |= 0x04; - - cdb[2] |= (byte)((int)transferRegister & 0x03); + break; } - cdb[2] |= 0x20; + if(transferBlocks) + cdb[2] |= 0x04; - cdb[3] = (byte)((registers.Feature & 0xFF00) >> 8); - cdb[4] = (byte)(registers.Feature & 0xFF); - cdb[5] = (byte)((registers.SectorCount & 0xFF00) >> 8); - cdb[6] = (byte)(registers.SectorCount & 0xFF); - cdb[7] = registers.LbaLowPrevious; - cdb[8] = registers.LbaLowCurrent; - cdb[9] = registers.LbaMidPrevious; - cdb[10] = registers.LbaMidCurrent; - cdb[11] = registers.LbaHighPrevious; - cdb[12] = registers.LbaHighCurrent; - cdb[13] = registers.DeviceHead; - cdb[14] = registers.Command; - - int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, - AtaProtocolToScsiDirection(protocol), out duration, out sense); - - if(senseBuffer.Length < 22 || - (senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C)) - return error; - - errorRegisters.Error = senseBuffer[11]; - - errorRegisters.SectorCount = (ushort)((senseBuffer[12] << 8) + senseBuffer[13]); - errorRegisters.LbaLowPrevious = senseBuffer[14]; - errorRegisters.LbaLowCurrent = senseBuffer[15]; - errorRegisters.LbaMidPrevious = senseBuffer[16]; - errorRegisters.LbaMidCurrent = senseBuffer[17]; - errorRegisters.LbaHighPrevious = senseBuffer[18]; - errorRegisters.LbaHighCurrent = senseBuffer[19]; - errorRegisters.DeviceHead = senseBuffer[20]; - errorRegisters.Status = senseBuffer[21]; - - sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; - - sense |= error != 0; - - return error; + cdb[2] |= (byte)((int)transferRegister & 0x03); } - /// Sends a MMC/SD command - /// The result of the command. - /// File handle - /// MMC/SD opcode - /// Buffer for MMC/SD command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if MMC/SD returned non-OK status - /// True if data is sent from host to card - /// True if command should be preceded with CMD55 - /// Flags indicating kind and place of response - /// How many blocks to transfer - /// Command argument - /// Response registers - /// Size of block in bytes - internal static int SendMmcCommand(int fd, MmcCommands command, bool write, bool isApplication, MmcFlags flags, - uint argument, uint blockSize, uint blocks, ref byte[] buffer, - out uint[] response, out double duration, out bool sense, uint timeout = 0) + cdb[2] |= 0x20; + + cdb[3] = (byte)((registers.Feature & 0xFF00) >> 8); + cdb[4] = (byte)(registers.Feature & 0xFF); + cdb[5] = (byte)((registers.SectorCount & 0xFF00) >> 8); + cdb[6] = (byte)(registers.SectorCount & 0xFF); + cdb[7] = registers.LbaLowPrevious; + cdb[8] = registers.LbaLowCurrent; + cdb[9] = registers.LbaMidPrevious; + cdb[10] = registers.LbaMidCurrent; + cdb[11] = registers.LbaHighPrevious; + cdb[12] = registers.LbaHighCurrent; + cdb[13] = registers.DeviceHead; + cdb[14] = registers.Command; + + int error = SendScsiCommand(fd, cdb, ref buffer, out byte[] senseBuffer, timeout, + AtaProtocolToScsiDirection(protocol), out duration, out sense); + + if(senseBuffer.Length < 22 || + (senseBuffer[8] != 0x09 && senseBuffer[9] != 0x0C)) + return error; + + errorRegisters.Error = senseBuffer[11]; + + errorRegisters.SectorCount = (ushort)((senseBuffer[12] << 8) + senseBuffer[13]); + errorRegisters.LbaLowPrevious = senseBuffer[14]; + errorRegisters.LbaLowCurrent = senseBuffer[15]; + errorRegisters.LbaMidPrevious = senseBuffer[16]; + errorRegisters.LbaMidCurrent = senseBuffer[17]; + errorRegisters.LbaHighPrevious = senseBuffer[18]; + errorRegisters.LbaHighCurrent = senseBuffer[19]; + errorRegisters.DeviceHead = senseBuffer[20]; + errorRegisters.Status = senseBuffer[21]; + + sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; + + sense |= error != 0; + + return error; + } + + /// Sends a MMC/SD command + /// The result of the command. + /// File handle + /// MMC/SD opcode + /// Buffer for MMC/SD command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if MMC/SD returned non-OK status + /// True if data is sent from host to card + /// True if command should be preceded with CMD55 + /// Flags indicating kind and place of response + /// How many blocks to transfer + /// Command argument + /// Response registers + /// Size of block in bytes + internal static int SendMmcCommand(int fd, MmcCommands command, bool write, bool isApplication, MmcFlags flags, + uint argument, uint blockSize, uint blocks, ref byte[] buffer, + out uint[] response, out double duration, out bool sense, uint timeout = 0) + { + response = null; + duration = 0; + sense = false; + + if(buffer == null) + return -1; + + var ioCmd = new MmcIocCmd(); + + IntPtr bufPtr = Marshal.AllocHGlobal(buffer.Length); + + ioCmd.write_flag = write; + ioCmd.is_ascmd = isApplication; + ioCmd.opcode = (uint)command; + ioCmd.arg = argument; + ioCmd.flags = flags; + ioCmd.blksz = blockSize; + ioCmd.blocks = blocks; + + if(timeout > 0) { - response = null; - duration = 0; - sense = false; + ioCmd.data_timeout_ns = timeout * 1000000000; + ioCmd.cmd_timeout_ms = timeout * 1000; + } - if(buffer == null) - return -1; + ioCmd.data_ptr = (ulong)bufPtr; + Marshal.Copy(buffer, 0, bufPtr, buffer.Length); + + DateTime start = DateTime.UtcNow; + int error = Extern.ioctlMmc(fd, LinuxIoctl.MmcIocCmd, ref ioCmd); + DateTime end = DateTime.UtcNow; + + sense |= error < 0; + + if(error < 0) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(bufPtr, buffer, 0, buffer.Length); + + response = ioCmd.response; + duration = (end - start).TotalMilliseconds; + + Marshal.FreeHGlobal(bufPtr); + + return error; + } + + internal static int SendMultipleMmcCommands(int fd, Device.MmcSingleCommand[] commands, out double duration, + out bool sense, uint timeout = 0) + { + duration = 0; + sense = false; + int off; + + // Create array for buffers + IntPtr[] bufferPointers = new IntPtr[commands.Length]; + + // Allocate memory for the array for commands + byte[] ioMultiCmd = new byte[sizeof(ulong) + (Marshal.SizeOf() * commands.Length)]; + + // First value of array is uint64 with count of commands + Array.Copy(BitConverter.GetBytes((ulong)commands.Length), 0, ioMultiCmd, 0, sizeof(ulong)); + + off = sizeof(ulong); + + for(int i = 0; i < commands.Length; i++) + { + // Create command var ioCmd = new MmcIocCmd(); - IntPtr bufPtr = Marshal.AllocHGlobal(buffer.Length); + // Allocate buffer + bufferPointers[i] = Marshal.AllocHGlobal(commands[i].buffer.Length); - ioCmd.write_flag = write; - ioCmd.is_ascmd = isApplication; - ioCmd.opcode = (uint)command; - ioCmd.arg = argument; - ioCmd.flags = flags; - ioCmd.blksz = blockSize; - ioCmd.blocks = blocks; + // Define command + ioCmd.write_flag = commands[i].write; + ioCmd.is_ascmd = commands[i].isApplication; + ioCmd.opcode = (uint)commands[i].command; + ioCmd.arg = commands[i].argument; + ioCmd.flags = commands[i].flags; + ioCmd.blksz = commands[i].blockSize; + ioCmd.blocks = commands[i].blocks; if(timeout > 0) { @@ -424,223 +488,158 @@ namespace Aaru.Devices.Linux ioCmd.cmd_timeout_ms = timeout * 1000; } - ioCmd.data_ptr = (ulong)bufPtr; + ioCmd.data_ptr = (ulong)bufferPointers[i]; - Marshal.Copy(buffer, 0, bufPtr, buffer.Length); + // Copy buffer to unmanaged space + Marshal.Copy(commands[i].buffer, 0, bufferPointers[i], commands[i].buffer.Length); - DateTime start = DateTime.UtcNow; - int error = Extern.ioctlMmc(fd, LinuxIoctl.MmcIocCmd, ref ioCmd); - DateTime end = DateTime.UtcNow; + // Copy command to array + byte[] ioCmdBytes = Helpers.Marshal.StructureToByteArrayLittleEndian(ioCmd); + Array.Copy(ioCmdBytes, 0, ioMultiCmd, off, Marshal.SizeOf()); - sense |= error < 0; + // Advance pointer + off += Marshal.SizeOf(); + } - if(error < 0) - error = Marshal.GetLastWin32Error(); + // Allocate unmanaged memory for array of commands + IntPtr ioMultiCmdPtr = Marshal.AllocHGlobal(ioMultiCmd.Length); - Marshal.Copy(bufPtr, buffer, 0, buffer.Length); + // Copy array of commands to unmanaged memory + Marshal.Copy(ioMultiCmd, 0, ioMultiCmdPtr, ioMultiCmd.Length); - response = ioCmd.response; + // Send command + DateTime start = DateTime.UtcNow; + int error = Extern.ioctlMmcMulti(fd, LinuxIoctl.MmcIocMultiCmd, ioMultiCmdPtr); + DateTime end = DateTime.UtcNow; + + sense |= error < 0; + + if(error < 0) + error = Marshal.GetLastWin32Error(); + + duration = (end - start).TotalMilliseconds; + + off = sizeof(ulong); + + // Copy array from unmanaged memory + Marshal.Copy(ioMultiCmdPtr, ioMultiCmd, 0, ioMultiCmd.Length); + + // TODO: Use real pointers this is too slow + for(int i = 0; i < commands.Length; i++) + { + byte[] tmp = new byte[Marshal.SizeOf()]; + + // Copy command to managed space + Array.Copy(ioMultiCmd, off, tmp, 0, tmp.Length); + MmcIocCmd command = Helpers.Marshal.ByteArrayToStructureLittleEndian(tmp); + + // Copy response + commands[i].response = command.response; + + // Copy buffer to managed space + Marshal.Copy(bufferPointers[i], commands[i].buffer, 0, commands[i].buffer.Length); + + // Free buffer + Marshal.FreeHGlobal(bufferPointers[i]); + + // Advance pointer + off += Marshal.SizeOf(); + } + + // Free unmanaged memory + Marshal.FreeHGlobal(ioMultiCmdPtr); + + return error; + } + + internal static int ReOpen(string devicePath, int fd, out object newFd) + { + newFd = -1; + + int ret = Extern.close(fd); + + if(ret < 0) + return Marshal.GetLastWin32Error(); + + newFd = Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); + + if((int)newFd >= 0) + return 0; + + int error = Marshal.GetLastWin32Error(); + + if(error != 13 && + error != 30) + return Marshal.GetLastWin32Error(); + + newFd = Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); + + return (int)newFd < 0 ? Marshal.GetLastWin32Error() : 0; + } + + /// Reads the contents of a symbolic link + /// Path to the symbolic link + /// Contents of the symbolic link + internal static string ReadLink(string path) + { + IntPtr buf = Marshal.AllocHGlobal(4096); + int resultSize; + + if(DetectOS.Is64Bit) + { + long result64 = Extern.readlink64(path, buf, 4096); + + if(result64 <= 0) + return null; + + resultSize = (int)result64; + } + else + { + int result = Extern.readlink(path, buf, 4096); + + if(result <= 0) + return null; + + resultSize = result; + } + + byte[] resultString = new byte[resultSize]; + Marshal.Copy(buf, resultString, 0, resultSize); + Marshal.FreeHGlobal(buf); + + return Encoding.ASCII.GetString(resultString); + } + + internal static int BufferedOsRead(int fd, out byte[] buffer, long offset, uint length, out double duration) + { + buffer = new byte[length]; + + DateTime start = DateTime.Now; + + long sense = Extern.lseek(fd, offset, SeekWhence.Begin); + + DateTime end = DateTime.Now; + + if(sense < 0) + { duration = (end - start).TotalMilliseconds; - Marshal.FreeHGlobal(bufPtr); - - return error; + return Marshal.GetLastWin32Error(); } - internal static int SendMultipleMmcCommands(int fd, Device.MmcSingleCommand[] commands, out double duration, - out bool sense, uint timeout = 0) - { - duration = 0; - sense = false; - int off; + sense = DetectOS.Is64Bit ? Extern.read64(fd, buffer, length) : Extern.read(fd, buffer, (int)length); - // Create array for buffers - IntPtr[] bufferPointers = new IntPtr[commands.Length]; + end = DateTime.Now; + duration = (end - start).TotalMilliseconds; - // Allocate memory for the array for commands - byte[] ioMultiCmd = new byte[sizeof(ulong) + (Marshal.SizeOf() * commands.Length)]; + int errno = Marshal.GetLastWin32Error(); - // First value of array is uint64 with count of commands - Array.Copy(BitConverter.GetBytes((ulong)commands.Length), 0, ioMultiCmd, 0, sizeof(ulong)); + if(sense == length) + errno = 0; + else if(errno == 0) + errno = -22; - off = sizeof(ulong); - - for(int i = 0; i < commands.Length; i++) - { - // Create command - var ioCmd = new MmcIocCmd(); - - // Allocate buffer - bufferPointers[i] = Marshal.AllocHGlobal(commands[i].buffer.Length); - - // Define command - ioCmd.write_flag = commands[i].write; - ioCmd.is_ascmd = commands[i].isApplication; - ioCmd.opcode = (uint)commands[i].command; - ioCmd.arg = commands[i].argument; - ioCmd.flags = commands[i].flags; - ioCmd.blksz = commands[i].blockSize; - ioCmd.blocks = commands[i].blocks; - - if(timeout > 0) - { - ioCmd.data_timeout_ns = timeout * 1000000000; - ioCmd.cmd_timeout_ms = timeout * 1000; - } - - ioCmd.data_ptr = (ulong)bufferPointers[i]; - - // Copy buffer to unmanaged space - Marshal.Copy(commands[i].buffer, 0, bufferPointers[i], commands[i].buffer.Length); - - // Copy command to array - byte[] ioCmdBytes = Helpers.Marshal.StructureToByteArrayLittleEndian(ioCmd); - Array.Copy(ioCmdBytes, 0, ioMultiCmd, off, Marshal.SizeOf()); - - // Advance pointer - off += Marshal.SizeOf(); - } - - // Allocate unmanaged memory for array of commands - IntPtr ioMultiCmdPtr = Marshal.AllocHGlobal(ioMultiCmd.Length); - - // Copy array of commands to unmanaged memory - Marshal.Copy(ioMultiCmd, 0, ioMultiCmdPtr, ioMultiCmd.Length); - - // Send command - DateTime start = DateTime.UtcNow; - int error = Extern.ioctlMmcMulti(fd, LinuxIoctl.MmcIocMultiCmd, ioMultiCmdPtr); - DateTime end = DateTime.UtcNow; - - sense |= error < 0; - - if(error < 0) - error = Marshal.GetLastWin32Error(); - - duration = (end - start).TotalMilliseconds; - - off = sizeof(ulong); - - // Copy array from unmanaged memory - Marshal.Copy(ioMultiCmdPtr, ioMultiCmd, 0, ioMultiCmd.Length); - - // TODO: Use real pointers this is too slow - for(int i = 0; i < commands.Length; i++) - { - byte[] tmp = new byte[Marshal.SizeOf()]; - - // Copy command to managed space - Array.Copy(ioMultiCmd, off, tmp, 0, tmp.Length); - MmcIocCmd command = Helpers.Marshal.ByteArrayToStructureLittleEndian(tmp); - - // Copy response - commands[i].response = command.response; - - // Copy buffer to managed space - Marshal.Copy(bufferPointers[i], commands[i].buffer, 0, commands[i].buffer.Length); - - // Free buffer - Marshal.FreeHGlobal(bufferPointers[i]); - - // Advance pointer - off += Marshal.SizeOf(); - } - - // Free unmanaged memory - Marshal.FreeHGlobal(ioMultiCmdPtr); - - return error; - } - - internal static int ReOpen(string devicePath, int fd, out object newFd) - { - newFd = -1; - - int ret = Extern.close(fd); - - if(ret < 0) - return Marshal.GetLastWin32Error(); - - newFd = Extern.open(devicePath, FileFlags.ReadWrite | FileFlags.NonBlocking | FileFlags.CreateNew); - - if((int)newFd >= 0) - return 0; - - int error = Marshal.GetLastWin32Error(); - - if(error != 13 && - error != 30) - return Marshal.GetLastWin32Error(); - - newFd = Extern.open(devicePath, FileFlags.Readonly | FileFlags.NonBlocking); - - return (int)newFd < 0 ? Marshal.GetLastWin32Error() : 0; - } - - /// Reads the contents of a symbolic link - /// Path to the symbolic link - /// Contents of the symbolic link - internal static string ReadLink(string path) - { - IntPtr buf = Marshal.AllocHGlobal(4096); - int resultSize; - - if(DetectOS.Is64Bit) - { - long result64 = Extern.readlink64(path, buf, 4096); - - if(result64 <= 0) - return null; - - resultSize = (int)result64; - } - else - { - int result = Extern.readlink(path, buf, 4096); - - if(result <= 0) - return null; - - resultSize = result; - } - - byte[] resultString = new byte[resultSize]; - Marshal.Copy(buf, resultString, 0, resultSize); - Marshal.FreeHGlobal(buf); - - return Encoding.ASCII.GetString(resultString); - } - - internal static int BufferedOsRead(int fd, out byte[] buffer, long offset, uint length, out double duration) - { - buffer = new byte[length]; - - DateTime start = DateTime.Now; - - long sense = Extern.lseek(fd, offset, SeekWhence.Begin); - - DateTime end = DateTime.Now; - - if(sense < 0) - { - duration = (end - start).TotalMilliseconds; - - return Marshal.GetLastWin32Error(); - } - - sense = DetectOS.Is64Bit ? Extern.read64(fd, buffer, length) : Extern.read(fd, buffer, (int)length); - - end = DateTime.Now; - duration = (end - start).TotalMilliseconds; - - int errno = Marshal.GetLastWin32Error(); - - if(sense == length) - errno = 0; - else if(errno == 0) - errno = -22; - - return errno; - } + return errno; } } \ No newline at end of file diff --git a/Aaru.Devices/Linux/Enums.cs b/Aaru.Devices/Linux/Enums.cs index 9522a6de1..2d71df4f3 100644 --- a/Aaru.Devices/Linux/Enums.cs +++ b/Aaru.Devices/Linux/Enums.cs @@ -33,101 +33,100 @@ using System; -namespace Aaru.Devices.Linux +namespace Aaru.Devices.Linux; + +[Flags] +internal enum FileFlags { - [Flags] - internal enum FileFlags - { - /// O_RDONLY - Readonly = 0x0, - /// O_WRONLY - Writeonly = 0x1, - /// O_RDWR - ReadWrite = 0x2, - /// O_CREAT - OpenOrCreate = 0x40, - /// O_EXCL - CreateNew = 0x80, - /// O_NOCTTY - NoControlTty = 0x100, - /// O_TRUNC - Truncate = 0x200, - /// O_APPEND - Append = 0x400, - /// O_NONBLOCK - NonBlocking = 0x800, - /// O_DSYNC - Synchronous = 0x1000, - /// O_ASYNC - Async = 0x2000, - /// O_DIRECT - Direct = 0x4000, - /// O_LARGEFILE - LargeFile = 0x8000, - /// O_DIRECTORY - Directory = 0x10000, - /// O_NOFOLLOW - NoFollowSymlink = 0x20000, - /// O_NOATIME - NoAccessTime = 0x40000, - /// O_CLOEXEC - CloseOnExec = 0x80000 - } + /// O_RDONLY + Readonly = 0x0, + /// O_WRONLY + Writeonly = 0x1, + /// O_RDWR + ReadWrite = 0x2, + /// O_CREAT + OpenOrCreate = 0x40, + /// O_EXCL + CreateNew = 0x80, + /// O_NOCTTY + NoControlTty = 0x100, + /// O_TRUNC + Truncate = 0x200, + /// O_APPEND + Append = 0x400, + /// O_NONBLOCK + NonBlocking = 0x800, + /// O_DSYNC + Synchronous = 0x1000, + /// O_ASYNC + Async = 0x2000, + /// O_DIRECT + Direct = 0x4000, + /// O_LARGEFILE + LargeFile = 0x8000, + /// O_DIRECTORY + Directory = 0x10000, + /// O_NOFOLLOW + NoFollowSymlink = 0x20000, + /// O_NOATIME + NoAccessTime = 0x40000, + /// O_CLOEXEC + CloseOnExec = 0x80000 +} - /// Direction of SCSI transfer - internal enum ScsiIoctlDirection - { - /// No data transfer happens SG_DXFER_NONE - None = -1, - /// From host to device SG_DXFER_TO_DEV - Out = -2, - /// From device to host SG_DXFER_FROM_DEV - In = -3, - /// Bidirectional device/host SG_DXFER_TO_FROM_DEV - Unspecified = -4, - /// Unspecified SG_DXFER_UNKNOWN - Unknown = -5 - } +/// Direction of SCSI transfer +internal enum ScsiIoctlDirection +{ + /// No data transfer happens SG_DXFER_NONE + None = -1, + /// From host to device SG_DXFER_TO_DEV + Out = -2, + /// From device to host SG_DXFER_FROM_DEV + In = -3, + /// Bidirectional device/host SG_DXFER_TO_FROM_DEV + Unspecified = -4, + /// Unspecified SG_DXFER_UNKNOWN + Unknown = -5 +} - internal enum LinuxIoctl : uint - { - // SCSI IOCtls - SgGetVersionNum = 0x2282, SgIo = 0x2285, +internal enum LinuxIoctl : uint +{ + // SCSI IOCtls + SgGetVersionNum = 0x2282, SgIo = 0x2285, - // MMC IOCtl - MmcIocCmd = 0xC048B300, MmcIocMultiCmd = 0xC008B301 - } + // MMC IOCtl + MmcIocCmd = 0xC048B300, MmcIocMultiCmd = 0xC008B301 +} - [Flags] - internal enum SgInfo : uint - { - /// Mask to check OK - OkMask = 0x01, - /// No sense or driver noise - Ok = 0x00, - /// Check Condition - CheckCondition = 0x01, +[Flags] +internal enum SgInfo : uint +{ + /// Mask to check OK + OkMask = 0x01, + /// No sense or driver noise + Ok = 0x00, + /// Check Condition + CheckCondition = 0x01, - /// Direct I/O mask - DirectIoMask = 0x06, - /// Transfer via kernel buffers (or no transfer) - IndirectIo = 0x00, - /// Direct I/O performed - DirectIo = 0x02, - /// Partial direct and partial indirect I/O - MixedIo = 0x04 - } + /// Direct I/O mask + DirectIoMask = 0x06, + /// Transfer via kernel buffers (or no transfer) + IndirectIo = 0x00, + /// Direct I/O performed + DirectIo = 0x02, + /// Partial direct and partial indirect I/O + MixedIo = 0x04 +} - [Flags] - internal enum SgFlags : uint - { - DirectIo = 1, UnusedLunInhibit = 2, MmapIo = 4, - NoDxfer = 0x10000, QAtTail = 0x10, QAtHead = 0x20 - } +[Flags] +internal enum SgFlags : uint +{ + DirectIo = 1, UnusedLunInhibit = 2, MmapIo = 4, + NoDxfer = 0x10000, QAtTail = 0x10, QAtHead = 0x20 +} - internal enum SeekWhence - { - Begin = 0, Current = 1, End = 2, - Data = 3, Hole = 4 - } +internal enum SeekWhence +{ + Begin = 0, Current = 1, End = 2, + Data = 3, Hole = 4 } \ No newline at end of file diff --git a/Aaru.Devices/Linux/Extern.cs b/Aaru.Devices/Linux/Extern.cs index 4751488f8..6ab3994e4 100644 --- a/Aaru.Devices/Linux/Extern.cs +++ b/Aaru.Devices/Linux/Extern.cs @@ -34,51 +34,50 @@ using System; using System.Runtime.InteropServices; -namespace Aaru.Devices.Linux +namespace Aaru.Devices.Linux; + +internal static class Extern { - internal static class Extern - { - [DllImport("libc", CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern int open(string pathname, [MarshalAs(UnmanagedType.U4)] FileFlags flags); + [DllImport("libc", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern int open(string pathname, [MarshalAs(UnmanagedType.U4)] FileFlags flags); - [DllImport("libc")] - internal static extern int close(int fd); + [DllImport("libc")] + internal static extern int close(int fd); - [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] - internal static extern int ioctlInt(int fd, LinuxIoctl request, out int value); + [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] + internal static extern int ioctlInt(int fd, LinuxIoctl request, out int value); - [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] - internal static extern int ioctlSg(int fd, LinuxIoctl request, ref SgIoHdrT value); + [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] + internal static extern int ioctlSg(int fd, LinuxIoctl request, ref SgIoHdrT value); - [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] - internal static extern int ioctlMmc(int fd, LinuxIoctl request, ref MmcIocCmd value); + [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] + internal static extern int ioctlMmc(int fd, LinuxIoctl request, ref MmcIocCmd value); - [DllImport("libc", CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern int readlink(string path, IntPtr buf, int bufsize); + [DllImport("libc", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern int readlink(string path, IntPtr buf, int bufsize); - [DllImport("libc", CharSet = CharSet.Ansi, EntryPoint = "readlink", SetLastError = true)] - internal static extern long readlink64(string path, IntPtr buf, long bufsize); + [DllImport("libc", CharSet = CharSet.Ansi, EntryPoint = "readlink", SetLastError = true)] + internal static extern long readlink64(string path, IntPtr buf, long bufsize); - [DllImport("libudev", CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern IntPtr udev_new(); + [DllImport("libudev", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern IntPtr udev_new(); - [DllImport("libudev", CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern IntPtr udev_device_new_from_subsystem_sysname( - IntPtr udev, string subsystem, string sysname); + [DllImport("libudev", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern IntPtr udev_device_new_from_subsystem_sysname( + IntPtr udev, string subsystem, string sysname); - [DllImport("libudev", CharSet = CharSet.Ansi, SetLastError = true)] - internal static extern string udev_device_get_property_value(IntPtr udevDevice, string key); + [DllImport("libudev", CharSet = CharSet.Ansi, SetLastError = true)] + internal static extern string udev_device_get_property_value(IntPtr udevDevice, string key); - [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] - internal static extern int ioctlMmcMulti(int fd, LinuxIoctl request, IntPtr value); + [DllImport("libc", EntryPoint = "ioctl", SetLastError = true)] + internal static extern int ioctlMmcMulti(int fd, LinuxIoctl request, IntPtr value); - [DllImport("libc", SetLastError = true)] - internal static extern long lseek(int fd, long offset, SeekWhence whence); + [DllImport("libc", SetLastError = true)] + internal static extern long lseek(int fd, long offset, SeekWhence whence); - [DllImport("libc", SetLastError = true)] - internal static extern int read(int fd, byte[] buf, int count); + [DllImport("libc", SetLastError = true)] + internal static extern int read(int fd, byte[] buf, int count); - [DllImport("libc", EntryPoint = "read", SetLastError = true)] - internal static extern long read64(int fd, byte[] buf, long count); - } + [DllImport("libc", EntryPoint = "read", SetLastError = true)] + internal static extern long read64(int fd, byte[] buf, long count); } \ No newline at end of file diff --git a/Aaru.Devices/Linux/ListDevices.cs b/Aaru.Devices/Linux/ListDevices.cs index efdce1575..9dc914174 100644 --- a/Aaru.Devices/Linux/ListDevices.cs +++ b/Aaru.Devices/Linux/ListDevices.cs @@ -34,128 +34,127 @@ using System; using System.IO; using System.Text; -namespace Aaru.Devices.Linux +namespace Aaru.Devices.Linux; + +internal static class ListDevices { - internal static class ListDevices + const string PATH_SYS_DEVBLOCK = "/sys/block/"; + + /// Gets a list of all known storage devices on Linux + /// List of devices + internal static DeviceInfo[] GetList() { - const string PATH_SYS_DEVBLOCK = "/sys/block/"; + string[] sysdevs = Directory.GetFileSystemEntries(PATH_SYS_DEVBLOCK, "*", SearchOption.TopDirectoryOnly); - /// Gets a list of all known storage devices on Linux - /// List of devices - internal static DeviceInfo[] GetList() + DeviceInfo[] devices = new DeviceInfo[sysdevs.Length]; + bool hasUdev; + + IntPtr udev = IntPtr.Zero; + + try { - string[] sysdevs = Directory.GetFileSystemEntries(PATH_SYS_DEVBLOCK, "*", SearchOption.TopDirectoryOnly); - - DeviceInfo[] devices = new DeviceInfo[sysdevs.Length]; - bool hasUdev; - - IntPtr udev = IntPtr.Zero; - - try - { - udev = Extern.udev_new(); - hasUdev = udev != IntPtr.Zero; - } - catch - { - hasUdev = false; - } - - for(int i = 0; i < sysdevs.Length; i++) - { - devices[i] = new DeviceInfo - { - Path = "/dev/" + Path.GetFileName(sysdevs[i]) - }; - - if(hasUdev) - { - IntPtr udevDev = - Extern.udev_device_new_from_subsystem_sysname(udev, "block", Path.GetFileName(sysdevs[i])); - - devices[i].Vendor = Extern.udev_device_get_property_value(udevDev, "ID_VENDOR"); - devices[i].Model = Extern.udev_device_get_property_value(udevDev, "ID_MODEL"); - - if(!string.IsNullOrEmpty(devices[i].Model)) - devices[i].Model = devices[i].Model.Replace('_', ' '); - - devices[i].Serial = Extern.udev_device_get_property_value(udevDev, "ID_SCSI_SERIAL"); - - if(string.IsNullOrEmpty(devices[i].Serial)) - devices[i].Serial = Extern.udev_device_get_property_value(udevDev, "ID_SERIAL_SHORT"); - - devices[i].Bus = Extern.udev_device_get_property_value(udevDev, "ID_BUS"); - } - - StreamReader sr; - - if(File.Exists(Path.Combine(sysdevs[i], "device/vendor")) && - string.IsNullOrEmpty(devices[i].Vendor)) - { - sr = new StreamReader(Path.Combine(sysdevs[i], "device/vendor"), Encoding.ASCII); - devices[i].Vendor = sr.ReadLine()?.Trim(); - } - else if(devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) - devices[i].Vendor = "Linux"; - - if(File.Exists(Path.Combine(sysdevs[i], "device/model")) && - (string.IsNullOrEmpty(devices[i].Model) || devices[i].Bus == "ata")) - { - sr = new StreamReader(Path.Combine(sysdevs[i], "device/model"), Encoding.ASCII); - devices[i].Model = sr.ReadLine()?.Trim(); - } - else if(devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) - devices[i].Model = "Linux"; - - if(File.Exists(Path.Combine(sysdevs[i], "device/serial")) && - string.IsNullOrEmpty(devices[i].Serial)) - { - sr = new StreamReader(Path.Combine(sysdevs[i], "device/serial"), Encoding.ASCII); - devices[i].Serial = sr.ReadLine()?.Trim(); - } - - if(string.IsNullOrEmpty(devices[i].Vendor) || - devices[i].Vendor == "ATA") - if(devices[i].Model != null) - { - string[] pieces = devices[i].Model.Split(' '); - - if(pieces.Length > 1) - { - devices[i].Vendor = pieces[0]; - devices[i].Model = devices[i].Model.Substring(pieces[0].Length + 1); - } - } - - // TODO: Get better device type from sysfs paths - if(string.IsNullOrEmpty(devices[i].Bus)) - { - if(devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) - devices[i].Bus = "loop"; - else if(devices[i].Path.StartsWith("/dev/nvme", StringComparison.CurrentCulture)) - devices[i].Bus = "NVMe"; - else if(devices[i].Path.StartsWith("/dev/mmc", StringComparison.CurrentCulture)) - devices[i].Bus = "MMC/SD"; - } - else - devices[i].Bus = devices[i].Bus.ToUpper(); - - switch(devices[i].Bus) - { - case "ATA": - case "ATAPI": - case "SCSI": - case "USB": - case "PCMCIA": - case "FireWire": - case "MMC/SD": - devices[i].Supported = true; - - break; - } - } - - return devices; + udev = Extern.udev_new(); + hasUdev = udev != IntPtr.Zero; } + catch + { + hasUdev = false; + } + + for(int i = 0; i < sysdevs.Length; i++) + { + devices[i] = new DeviceInfo + { + Path = "/dev/" + Path.GetFileName(sysdevs[i]) + }; + + if(hasUdev) + { + IntPtr udevDev = + Extern.udev_device_new_from_subsystem_sysname(udev, "block", Path.GetFileName(sysdevs[i])); + + devices[i].Vendor = Extern.udev_device_get_property_value(udevDev, "ID_VENDOR"); + devices[i].Model = Extern.udev_device_get_property_value(udevDev, "ID_MODEL"); + + if(!string.IsNullOrEmpty(devices[i].Model)) + devices[i].Model = devices[i].Model.Replace('_', ' '); + + devices[i].Serial = Extern.udev_device_get_property_value(udevDev, "ID_SCSI_SERIAL"); + + if(string.IsNullOrEmpty(devices[i].Serial)) + devices[i].Serial = Extern.udev_device_get_property_value(udevDev, "ID_SERIAL_SHORT"); + + devices[i].Bus = Extern.udev_device_get_property_value(udevDev, "ID_BUS"); + } + + StreamReader sr; + + if(File.Exists(Path.Combine(sysdevs[i], "device/vendor")) && + string.IsNullOrEmpty(devices[i].Vendor)) + { + sr = new StreamReader(Path.Combine(sysdevs[i], "device/vendor"), Encoding.ASCII); + devices[i].Vendor = sr.ReadLine()?.Trim(); + } + else if(devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) + devices[i].Vendor = "Linux"; + + if(File.Exists(Path.Combine(sysdevs[i], "device/model")) && + (string.IsNullOrEmpty(devices[i].Model) || devices[i].Bus == "ata")) + { + sr = new StreamReader(Path.Combine(sysdevs[i], "device/model"), Encoding.ASCII); + devices[i].Model = sr.ReadLine()?.Trim(); + } + else if(devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) + devices[i].Model = "Linux"; + + if(File.Exists(Path.Combine(sysdevs[i], "device/serial")) && + string.IsNullOrEmpty(devices[i].Serial)) + { + sr = new StreamReader(Path.Combine(sysdevs[i], "device/serial"), Encoding.ASCII); + devices[i].Serial = sr.ReadLine()?.Trim(); + } + + if(string.IsNullOrEmpty(devices[i].Vendor) || + devices[i].Vendor == "ATA") + if(devices[i].Model != null) + { + string[] pieces = devices[i].Model.Split(' '); + + if(pieces.Length > 1) + { + devices[i].Vendor = pieces[0]; + devices[i].Model = devices[i].Model.Substring(pieces[0].Length + 1); + } + } + + // TODO: Get better device type from sysfs paths + if(string.IsNullOrEmpty(devices[i].Bus)) + { + if(devices[i].Path.StartsWith("/dev/loop", StringComparison.CurrentCulture)) + devices[i].Bus = "loop"; + else if(devices[i].Path.StartsWith("/dev/nvme", StringComparison.CurrentCulture)) + devices[i].Bus = "NVMe"; + else if(devices[i].Path.StartsWith("/dev/mmc", StringComparison.CurrentCulture)) + devices[i].Bus = "MMC/SD"; + } + else + devices[i].Bus = devices[i].Bus.ToUpper(); + + switch(devices[i].Bus) + { + case "ATA": + case "ATAPI": + case "SCSI": + case "USB": + case "PCMCIA": + case "FireWire": + case "MMC/SD": + devices[i].Supported = true; + + break; + } + } + + return devices; } } \ No newline at end of file diff --git a/Aaru.Devices/Linux/Structs.cs b/Aaru.Devices/Linux/Structs.cs index 0087018b1..8a533e088 100644 --- a/Aaru.Devices/Linux/Structs.cs +++ b/Aaru.Devices/Linux/Structs.cs @@ -35,73 +35,72 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace Aaru.Devices.Linux -{ - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct SgIoHdrT - { - /// Always 'S' for SG v3 - public int interface_id; /* [i] 'S' (required) */ - public ScsiIoctlDirection dxfer_direction; /* [i] */ - public byte cmd_len; /* [i] */ - public byte mx_sb_len; /* [i] */ - public ushort iovec_count; /* [i] */ - public uint dxfer_len; /* [i] */ - public IntPtr dxferp; /* [i], [*io] */ - public IntPtr cmdp; /* [i], [*i] */ - public IntPtr sbp; /* [i], [*o] */ - public uint timeout; /* [i] unit: millisecs */ - public uint flags; /* [i] */ - public int pack_id; /* [i->o] */ - public IntPtr usr_ptr; /* [i->o] */ - public byte status; /* [o] */ - public byte masked_status; /* [o] */ - public byte msg_status; /* [o] */ - public byte sb_len_wr; /* [o] */ - public ushort host_status; /* [o] */ - public ushort driver_status; /* [o] */ - public int resid; /* [o] */ - public uint duration; /* [o] */ - public SgInfo info; /* [o] */ - } +namespace Aaru.Devices.Linux; - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct MmcIocCmd - { - /// Implies direction of data. true = write, false = read - public bool write_flag; - /// Application-specific command. true = precede with CMD55 - public bool is_ascmd; - public uint opcode; - public uint arg; - /// CMD response - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public uint[] response; - public MmcFlags flags; - public uint blksz; - public uint blocks; - /// - /// Sleep at least useconds, and at most useconds - /// *after* issuing command.Needed for some read commands for which cards have no other way of indicating they're ready - /// for the next command (i.e. there is no equivalent of a "busy" indicator for read operations). - /// - public uint postsleep_min_us; - /// - /// Sleep at least useconds, and at most useconds - /// *after* issuing command.Needed for some read commands for which cards have no other way of indicating they're ready - /// for the next command (i.e. there is no equivalent of a "busy" indicator for read operations). - /// - public uint postsleep_max_us; - /// Override driver-computed timeouts. - public uint data_timeout_ns; - /// Override driver-computed timeouts. - public uint cmd_timeout_ms; - /// - /// For 64-bit machines , wants to be 8-byte aligned.Make sure this struct is the same - /// size when built for 32-bit. - /// - public uint __pad; - /// DAT buffer - public ulong data_ptr; - } +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct SgIoHdrT +{ + /// Always 'S' for SG v3 + public int interface_id; /* [i] 'S' (required) */ + public ScsiIoctlDirection dxfer_direction; /* [i] */ + public byte cmd_len; /* [i] */ + public byte mx_sb_len; /* [i] */ + public ushort iovec_count; /* [i] */ + public uint dxfer_len; /* [i] */ + public IntPtr dxferp; /* [i], [*io] */ + public IntPtr cmdp; /* [i], [*i] */ + public IntPtr sbp; /* [i], [*o] */ + public uint timeout; /* [i] unit: millisecs */ + public uint flags; /* [i] */ + public int pack_id; /* [i->o] */ + public IntPtr usr_ptr; /* [i->o] */ + public byte status; /* [o] */ + public byte masked_status; /* [o] */ + public byte msg_status; /* [o] */ + public byte sb_len_wr; /* [o] */ + public ushort host_status; /* [o] */ + public ushort driver_status; /* [o] */ + public int resid; /* [o] */ + public uint duration; /* [o] */ + public SgInfo info; /* [o] */ +} + +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct MmcIocCmd +{ + /// Implies direction of data. true = write, false = read + public bool write_flag; + /// Application-specific command. true = precede with CMD55 + public bool is_ascmd; + public uint opcode; + public uint arg; + /// CMD response + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public uint[] response; + public MmcFlags flags; + public uint blksz; + public uint blocks; + /// + /// Sleep at least useconds, and at most useconds + /// *after* issuing command.Needed for some read commands for which cards have no other way of indicating they're ready + /// for the next command (i.e. there is no equivalent of a "busy" indicator for read operations). + /// + public uint postsleep_min_us; + /// + /// Sleep at least useconds, and at most useconds + /// *after* issuing command.Needed for some read commands for which cards have no other way of indicating they're ready + /// for the next command (i.e. there is no equivalent of a "busy" indicator for read operations). + /// + public uint postsleep_max_us; + /// Override driver-computed timeouts. + public uint data_timeout_ns; + /// Override driver-computed timeouts. + public uint cmd_timeout_ms; + /// + /// For 64-bit machines , wants to be 8-byte aligned.Make sure this struct is the same + /// size when built for 32-bit. + /// + public uint __pad; + /// DAT buffer + public ulong data_ptr; } \ No newline at end of file diff --git a/Aaru.Devices/Remote/Consts.cs b/Aaru.Devices/Remote/Consts.cs index 35ff4d463..a1f43bac4 100644 --- a/Aaru.Devices/Remote/Consts.cs +++ b/Aaru.Devices/Remote/Consts.cs @@ -30,18 +30,17 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Devices.Remote +namespace Aaru.Devices.Remote; + +/// AaruRemote protocol constants +public class Consts { - /// AaruRemote protocol constants - public class Consts - { - /// Primary unique packet identifier - public const uint REMOTE_ID = 0x52434944; // "DICR" - /// Secondary unique packet identifier - public const uint PACKET_ID = 0x544B4350; // "PCKT" - /// Default packet version - public const int PACKET_VERSION = 1; - /// Maximum supported protocol version - public const int MAX_PROTOCOL = 2; - } + /// Primary unique packet identifier + public const uint REMOTE_ID = 0x52434944; // "DICR" + /// Secondary unique packet identifier + public const uint PACKET_ID = 0x544B4350; // "PCKT" + /// Default packet version + public const int PACKET_VERSION = 1; + /// Maximum supported protocol version + public const int MAX_PROTOCOL = 2; } \ No newline at end of file diff --git a/Aaru.Devices/Remote/Enums.cs b/Aaru.Devices/Remote/Enums.cs index 9077d91a9..26442ce56 100644 --- a/Aaru.Devices/Remote/Enums.cs +++ b/Aaru.Devices/Remote/Enums.cs @@ -30,44 +30,43 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Devices.Remote -{ - /// Packet type enumeration - public enum AaruPacketType : sbyte - { - #pragma warning disable 1591 - Nop = -1, Hello = 1, CommandListDevices = 2, - ResponseListDevices = 3, CommandOpen = 4, CommandScsi = 5, - ResponseScsi = 6, CommandAtaChs = 7, ResponseAtaChs = 8, - CommandAtaLba28 = 9, ResponseAtaLba28 = 10, CommandAtaLba48 = 11, - ResponseAtaLba48 = 12, CommandSdhci = 13, ResponseSdhci = 14, - CommandGetType = 15, ResponseGetType = 16, CommandGetSdhciRegisters = 17, - ResponseGetSdhciRegisters = 18, CommandGetUsbData = 19, ResponseGetUsbData = 20, - CommandGetFireWireData = 21, ResponseGetFireWireData = 22, CommandGetPcmciaData = 23, - ResponseGetPcmciaData = 24, CommandCloseDevice = 25, CommandAmIRoot = 26, - ResponseAmIRoot = 27, MultiCommandSdhci = 28, ResponseMultiSdhci = 29, - CommandReOpenDevice = 30, CommandOsRead = 31, ResponseOsRead = 32 - #pragma warning restore 1591 - } +namespace Aaru.Devices.Remote; - /// Reasons for non-data request or response - public enum AaruNopReason : byte - { - /// Request or response has arrived unexpectedly - OutOfOrder = 0, - /// Packet or version of packet is not implemented - NotImplemented = 1, - /// Unknown or non-recognized packet - NotRecognized = 2, - /// Error trying to get list of devices - ErrorListDevices = 3, - /// Device opened correctly - OpenOk = 4, - /// An error occurred opening the device - OpenError = 5, - /// Device re-opened correctly - ReOpenOk = 6, - /// An error occurred closing the device - CloseError = 7 - } +/// Packet type enumeration +public enum AaruPacketType : sbyte +{ + #pragma warning disable 1591 + Nop = -1, Hello = 1, CommandListDevices = 2, + ResponseListDevices = 3, CommandOpen = 4, CommandScsi = 5, + ResponseScsi = 6, CommandAtaChs = 7, ResponseAtaChs = 8, + CommandAtaLba28 = 9, ResponseAtaLba28 = 10, CommandAtaLba48 = 11, + ResponseAtaLba48 = 12, CommandSdhci = 13, ResponseSdhci = 14, + CommandGetType = 15, ResponseGetType = 16, CommandGetSdhciRegisters = 17, + ResponseGetSdhciRegisters = 18, CommandGetUsbData = 19, ResponseGetUsbData = 20, + CommandGetFireWireData = 21, ResponseGetFireWireData = 22, CommandGetPcmciaData = 23, + ResponseGetPcmciaData = 24, CommandCloseDevice = 25, CommandAmIRoot = 26, + ResponseAmIRoot = 27, MultiCommandSdhci = 28, ResponseMultiSdhci = 29, + CommandReOpenDevice = 30, CommandOsRead = 31, ResponseOsRead = 32 + #pragma warning restore 1591 +} + +/// Reasons for non-data request or response +public enum AaruNopReason : byte +{ + /// Request or response has arrived unexpectedly + OutOfOrder = 0, + /// Packet or version of packet is not implemented + NotImplemented = 1, + /// Unknown or non-recognized packet + NotRecognized = 2, + /// Error trying to get list of devices + ErrorListDevices = 3, + /// Device opened correctly + OpenOk = 4, + /// An error occurred opening the device + OpenError = 5, + /// Device re-opened correctly + ReOpenOk = 6, + /// An error occurred closing the device + CloseError = 7 } \ No newline at end of file diff --git a/Aaru.Devices/Remote/Remote.cs b/Aaru.Devices/Remote/Remote.cs index ddfcecbc0..6a2ba86a3 100644 --- a/Aaru.Devices/Remote/Remote.cs +++ b/Aaru.Devices/Remote/Remote.cs @@ -46,337 +46,79 @@ using Version = Aaru.CommonTypes.Interop.Version; // ReSharper disable MemberCanBeInternal -namespace Aaru.Devices.Remote +namespace Aaru.Devices.Remote; + +/// +/// Handles communication with a remote device that's connected using the AaruRemote protocol +public class Remote : IDisposable { - /// - /// Handles communication with a remote device that's connected using the AaruRemote protocol - public class Remote : IDisposable + readonly string _host; + readonly Socket _socket; + + /// Connects using TCP/IP to the specified remote + /// URI of the remote + /// Unsupported or invalid remote protocol. + /// Host not found. + /// Network error. + public Remote(Uri uri) { - readonly string _host; - readonly Socket _socket; + if(uri.Scheme != "aaru" && + uri.Scheme != "dic") + throw new ArgumentException("Invalid remote protocol.", nameof(uri.Scheme)); - /// Connects using TCP/IP to the specified remote - /// URI of the remote - /// Unsupported or invalid remote protocol. - /// Host not found. - /// Network error. - public Remote(Uri uri) + _host = uri.DnsSafeHost; + + if(!IPAddress.TryParse(_host, out IPAddress ipAddress)) { - if(uri.Scheme != "aaru" && - uri.Scheme != "dic") - throw new ArgumentException("Invalid remote protocol.", nameof(uri.Scheme)); + IPHostEntry ipHostEntry = Dns.GetHostEntry(_host); - _host = uri.DnsSafeHost; + ipAddress = ipHostEntry.AddressList.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork); + } - if(!IPAddress.TryParse(_host, out IPAddress ipAddress)) - { - IPHostEntry ipHostEntry = Dns.GetHostEntry(_host); + if(ipAddress is null) + { + AaruConsole.ErrorWriteLine("Host not found"); - ipAddress = ipHostEntry.AddressList.FirstOrDefault(a => a.AddressFamily == AddressFamily.InterNetwork); - } + throw new SocketException(11001); + } - if(ipAddress is null) - { - AaruConsole.ErrorWriteLine("Host not found"); + var ipEndPoint = new IPEndPoint(ipAddress, uri.Port > 0 ? uri.Port : 6666); + _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - throw new SocketException(11001); - } + _socket.Connect(ipEndPoint); - var ipEndPoint = new IPEndPoint(ipAddress, uri.Port > 0 ? uri.Port : 6666); - _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + AaruConsole.WriteLine("Connected to {0}", uri.Host); - _socket.Connect(ipEndPoint); + byte[] hdrBuf = new byte[Marshal.SizeOf()]; - AaruConsole.WriteLine("Connected to {0}", uri.Host); + int len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - int len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - throw new IOException(); - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - throw new ArgumentException(); - } - - byte[] buf; - - if(hdr.packetType != AaruPacketType.Hello) - { - if(hdr.packetType != AaruPacketType.Nop) - { - AaruConsole.ErrorWriteLine("Expected Hello Packet, got packet type {0}...", hdr.packetType); - - throw new ArgumentException(); - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - throw new IOException(); - } - - AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); - - AaruConsole.ErrorWriteLine($"{nop.reason}"); - - throw new ArgumentException(); - } - - if(hdr.version != Consts.PACKET_VERSION) - { - AaruConsole.ErrorWriteLine("Unrecognized packet version..."); - - throw new ArgumentException(); - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - throw new IOException(); - } - - AaruPacketHello serverHello = Marshal.ByteArrayToStructureLittleEndian(buf); - - ServerApplication = serverHello.application; - ServerVersion = serverHello.version; - ServerOperatingSystem = serverHello.sysname; - ServerOperatingSystemVersion = serverHello.release; - ServerArchitecture = serverHello.machine; - ServerProtocolVersion = serverHello.maxProtocol; - - var clientHello = new AaruPacketHello - { - application = "Aaru", - version = Version.GetVersion(), - maxProtocol = Consts.MAX_PROTOCOL, - sysname = DetectOS.GetPlatformName(DetectOS.GetRealPlatformID(), DetectOS.GetVersion()), - release = DetectOS.GetVersion(), - machine = RuntimeInformation.ProcessArchitecture.ToString(), - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.Hello - } - }; - - buf = Marshal.StructureToByteArrayLittleEndian(clientHello); - - len = _socket.Send(buf, SocketFlags.None); - - if(len >= buf.Length) - return; - - AaruConsole.ErrorWriteLine("Could not write to the network..."); + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); throw new IOException(); } - /// Remote server application - public string ServerApplication { get; } - /// Remote server application version - public string ServerVersion { get; } - /// Remote server operating system - public string ServerOperatingSystem { get; } - /// Remote server operating system version - public string ServerOperatingSystemVersion { get; } - /// Remote server architecture - public string ServerArchitecture { get; } - /// Remote server protocol version - public int ServerProtocolVersion { get; } + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - /// Is remote running with administrative (aka root) privileges? - public bool IsRoot + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) { - get - { - var cmdPkt = new AaruPacketCmdAmIRoot - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandAmIRoot - } - }; + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return false; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return false; - } - - if(hdr.packetType != AaruPacketType.ResponseAmIRoot) - { - AaruConsole.ErrorWriteLine("Expected Am I Root? Response Packet, got packet type {0}...", - hdr.packetType); - - return false; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketResAmIRoot res = Marshal.ByteArrayToStructureLittleEndian(buf); - - return res.am_i_root != 0; - } + throw new ArgumentException(); } - /// - public void Dispose() => Disconnect(); + byte[] buf; - /// Disconnects from remote - public void Disconnect() + if(hdr.packetType != AaruPacketType.Hello) { - try + if(hdr.packetType != AaruPacketType.Nop) { - _socket.Shutdown(SocketShutdown.Both); - _socket.Close(); - } - catch(ObjectDisposedException) - { - // Ignore if already disposed - } - } + AaruConsole.ErrorWriteLine("Expected Hello Packet, got packet type {0}...", hdr.packetType); - /// Lists devices attached to remote - /// List of devices - public DeviceInfo[] ListDevices() - { - var cmdPkt = new AaruPacketCommandListDevices - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandListDevices - } - }; - - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return Array.Empty(); - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return Array.Empty(); - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return Array.Empty(); - } - - if(hdr.packetType != AaruPacketType.ResponseListDevices) - { - if(hdr.packetType != AaruPacketType.Nop) - { - AaruConsole.ErrorWriteLine("Expected List Devices Response Packet, got packet type {0}...", - hdr.packetType); - - return Array.Empty(); - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return Array.Empty(); - } - - AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); - - AaruConsole.ErrorWriteLine($"{nop.reason}"); - - return Array.Empty(); - } - - if(hdr.version != Consts.PACKET_VERSION) - { - AaruConsole.ErrorWriteLine("Unrecognized packet version..."); - - return Array.Empty(); + throw new ArgumentException(); } buf = new byte[hdr.len]; @@ -386,50 +128,100 @@ namespace Aaru.Devices.Remote { AaruConsole.ErrorWriteLine("Could not read from the network..."); - return Array.Empty(); + throw new IOException(); } - AaruPacketResponseListDevices response = - Marshal.ByteArrayToStructureLittleEndian(buf); + AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); - List devices = new List(); - int offset = Marshal.SizeOf(); - int devInfoLen = Marshal.SizeOf(); + AaruConsole.ErrorWriteLine($"{nop.reason}"); - for(ushort i = 0; i < response.devices; i++) - { - DeviceInfo dev = Marshal.ByteArrayToStructureLittleEndian(buf, offset, devInfoLen); - dev.Path = dev.Path[0] == '/' ? $"aaru://{_host}{dev.Path}" : $"aaru://{_host}/{dev.Path}"; - devices.Add(dev); - offset += devInfoLen; - } - - return devices.ToArray(); + throw new ArgumentException(); } - /// Opens the specified device path on the remote - /// Device path - /// Returned error - /// true if opened correctly, falseotherwise - /// - /// Support for the specified device has not yet been implemented in the remote - /// application. - /// - public bool Open(string devicePath, out int lastError) + if(hdr.version != Consts.PACKET_VERSION) { - lastError = 0; + AaruConsole.ErrorWriteLine("Unrecognized packet version..."); - var cmdPkt = new AaruPacketCommandOpenDevice + throw new ArgumentException(); + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + throw new IOException(); + } + + AaruPacketHello serverHello = Marshal.ByteArrayToStructureLittleEndian(buf); + + ServerApplication = serverHello.application; + ServerVersion = serverHello.version; + ServerOperatingSystem = serverHello.sysname; + ServerOperatingSystemVersion = serverHello.release; + ServerArchitecture = serverHello.machine; + ServerProtocolVersion = serverHello.maxProtocol; + + var clientHello = new AaruPacketHello + { + application = "Aaru", + version = Version.GetVersion(), + maxProtocol = Consts.MAX_PROTOCOL, + sysname = DetectOS.GetPlatformName(DetectOS.GetRealPlatformID(), DetectOS.GetVersion()), + release = DetectOS.GetVersion(), + machine = RuntimeInformation.ProcessArchitecture.ToString(), + hdr = new AaruPacketHeader + { + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.Hello + } + }; + + buf = Marshal.StructureToByteArrayLittleEndian(clientHello); + + len = _socket.Send(buf, SocketFlags.None); + + if(len >= buf.Length) + return; + + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + throw new IOException(); + } + + /// Remote server application + public string ServerApplication { get; } + /// Remote server application version + public string ServerVersion { get; } + /// Remote server operating system + public string ServerOperatingSystem { get; } + /// Remote server operating system version + public string ServerOperatingSystemVersion { get; } + /// Remote server architecture + public string ServerArchitecture { get; } + /// Remote server protocol version + public int ServerProtocolVersion { get; } + + /// Is remote running with administrative (aka root) privileges? + public bool IsRoot + { + get + { + var cmdPkt = new AaruPacketCmdAmIRoot { hdr = new AaruPacketHeader { remote_id = Consts.REMOTE_ID, packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), + len = (uint)Marshal.SizeOf(), version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandOpen - }, - device_path = devicePath + packetType = AaruPacketType.CommandAmIRoot + } }; byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); @@ -439,7 +231,6 @@ namespace Aaru.Devices.Remote if(len != buf.Length) { AaruConsole.ErrorWriteLine("Could not write to the network..."); - lastError = -1; return false; } @@ -451,7 +242,6 @@ namespace Aaru.Devices.Remote if(len < hdrBuf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); - lastError = -1; return false; } @@ -462,19 +252,107 @@ namespace Aaru.Devices.Remote hdr.packet_id != Consts.PACKET_ID) { AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - lastError = -1; return false; } + if(hdr.packetType != AaruPacketType.ResponseAmIRoot) + { + AaruConsole.ErrorWriteLine("Expected Am I Root? Response Packet, got packet type {0}...", + hdr.packetType); + + return false; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketResAmIRoot res = Marshal.ByteArrayToStructureLittleEndian(buf); + + return res.am_i_root != 0; + } + } + + /// + public void Dispose() => Disconnect(); + + /// Disconnects from remote + public void Disconnect() + { + try + { + _socket.Shutdown(SocketShutdown.Both); + _socket.Close(); + } + catch(ObjectDisposedException) + { + // Ignore if already disposed + } + } + + /// Lists devices attached to remote + /// List of devices + public DeviceInfo[] ListDevices() + { + var cmdPkt = new AaruPacketCommandListDevices + { + hdr = new AaruPacketHeader + { + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandListDevices + } + }; + + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return Array.Empty(); + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return Array.Empty(); + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return Array.Empty(); + } + + if(hdr.packetType != AaruPacketType.ResponseListDevices) + { if(hdr.packetType != AaruPacketType.Nop) { AaruConsole.ErrorWriteLine("Expected List Devices Response Packet, got packet type {0}...", hdr.packetType); - lastError = -1; - - return false; + return Array.Empty(); } buf = new byte[hdr.len]; @@ -483,1481 +361,1602 @@ namespace Aaru.Devices.Remote if(len < buf.Length) { AaruConsole.ErrorWriteLine("Could not read from the network..."); - lastError = -1; - return false; + return Array.Empty(); } AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); - switch(nop.reasonCode) - { - case AaruNopReason.OpenOk: return true; - case AaruNopReason.NotImplemented: throw new NotImplementedException($"{nop.reason}"); - } - AaruConsole.ErrorWriteLine($"{nop.reason}"); - lastError = nop.errno; + + return Array.Empty(); + } + + if(hdr.version != Consts.PACKET_VERSION) + { + AaruConsole.ErrorWriteLine("Unrecognized packet version..."); + + return Array.Empty(); + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return Array.Empty(); + } + + AaruPacketResponseListDevices response = + Marshal.ByteArrayToStructureLittleEndian(buf); + + List devices = new List(); + int offset = Marshal.SizeOf(); + int devInfoLen = Marshal.SizeOf(); + + for(ushort i = 0; i < response.devices; i++) + { + DeviceInfo dev = Marshal.ByteArrayToStructureLittleEndian(buf, offset, devInfoLen); + dev.Path = dev.Path[0] == '/' ? $"aaru://{_host}{dev.Path}" : $"aaru://{_host}/{dev.Path}"; + devices.Add(dev); + offset += devInfoLen; + } + + return devices.ToArray(); + } + + /// Opens the specified device path on the remote + /// Device path + /// Returned error + /// true if opened correctly, falseotherwise + /// + /// Support for the specified device has not yet been implemented in the remote + /// application. + /// + public bool Open(string devicePath, out int lastError) + { + lastError = 0; + + var cmdPkt = new AaruPacketCommandOpenDevice + { + hdr = new AaruPacketHeader + { + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandOpen + }, + device_path = devicePath + }; + + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + lastError = -1; return false; } - /// Sends a SCSI command to the remote device - /// 0 if no error occurred, otherwise, errno - /// SCSI CDB - /// Buffer for SCSI command response - /// Buffer with the SCSI sense - /// Timeout in seconds - /// SCSI command transfer direction - /// Time it took to execute the command in milliseconds - /// - /// True if SCSI command returned non-OK status and contains - /// SCSI sense - /// - public int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, - ScsiDirection direction, out double duration, out bool sense) + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) { - senseBuffer = null; - duration = 0; - sense = true; - - var cmdPkt = new AaruPacketCmdScsi - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandScsi - }, - direction = (int)direction, - timeout = timeout * 1000 - }; - - if(cdb != null) - cmdPkt.cdb_len = (uint)cdb.Length; - - if(buffer != null) - cmdPkt.buf_len = (uint)buffer.Length; - - cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.cdb_len + cmdPkt.buf_len); - - byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - byte[] buf = new byte[cmdPkt.hdr.len]; - - Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); - - if(cdb != null) - Array.Copy(cdb, 0, buf, Marshal.SizeOf(), cmdPkt.cdb_len); - - if(buffer != null) - Array.Copy(buffer, 0, buf, Marshal.SizeOf() + cmdPkt.cdb_len, cmdPkt.buf_len); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return -1; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return -1; - } - - if(hdr.packetType != AaruPacketType.ResponseScsi) - { - AaruConsole.ErrorWriteLine("Expected SCSI Response Packet, got packet type {0}...", hdr.packetType); - - return -1; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketResScsi res = Marshal.ByteArrayToStructureLittleEndian(buf); - - senseBuffer = new byte[res.sense_len]; - Array.Copy(buf, Marshal.SizeOf(), senseBuffer, 0, res.sense_len); - buffer = new byte[res.buf_len]; - Array.Copy(buf, Marshal.SizeOf() + res.sense_len, buffer, 0, res.buf_len); - duration = res.duration; - sense = res.sense != 0; - - return (int)res.error_no; - } - - /// Sends an ATA/ATAPI command to the remote device using CHS addressing - /// 0 if no error occurred, otherwise, errno - /// ATA registers. - /// Status/error registers. - /// ATA Protocol. - /// Indicates which register indicates the transfer length - /// Buffer for ATA/ATAPI command response - /// Timeout in seconds - /// - /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in - /// bytes. - /// - /// Time it took to execute the command in milliseconds - /// True if ATA/ATAPI command returned non-OK status - public int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, - AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, - uint timeout, bool transferBlocks, out double duration, out bool sense) - { - duration = 0; - sense = true; - errorRegisters = new AtaErrorRegistersChs(); - - var cmdPkt = new AaruPacketCmdAtaChs - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandAtaChs - }, - registers = registers, - protocol = (byte)protocol, - transferRegister = (byte)transferRegister, - transferBlocks = transferBlocks, - timeout = timeout * 1000 - }; - - if(buffer != null) - cmdPkt.buf_len = (uint)buffer.Length; - - cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); - - byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - byte[] buf = new byte[cmdPkt.hdr.len]; - - Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); - - if(buffer != null) - Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return -1; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return -1; - } - - if(hdr.packetType != AaruPacketType.ResponseAtaChs) - { - AaruConsole.ErrorWriteLine("Expected ATA CHS Response Packet, got packet type {0}...", hdr.packetType); - - return -1; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketResAtaChs res = Marshal.ByteArrayToStructureLittleEndian(buf); - - buffer = new byte[res.buf_len]; - Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); - duration = res.duration; - sense = res.sense != 0; - errorRegisters = res.registers; - - return (int)res.error_no; - } - - /// Sends an ATA/ATAPI command to the remote device using 28-bit LBA addressing - /// 0 if no error occurred, otherwise, errno - /// ATA registers. - /// Status/error registers. - /// ATA Protocol. - /// Indicates which register indicates the transfer length - /// Buffer for ATA/ATAPI command response - /// Timeout in seconds - /// - /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in - /// bytes. - /// - /// Time it took to execute the command in milliseconds - /// True if ATA/ATAPI command returned non-OK status - public int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters, - AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, - uint timeout, bool transferBlocks, out double duration, out bool sense) - { - duration = 0; - sense = true; - errorRegisters = new AtaErrorRegistersLba28(); - - var cmdPkt = new AaruPacketCmdAtaLba28 - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandAtaLba28 - }, - registers = registers, - protocol = (byte)protocol, - transferRegister = (byte)transferRegister, - transferBlocks = transferBlocks, - timeout = timeout * 1000 - }; - - if(buffer != null) - cmdPkt.buf_len = (uint)buffer.Length; - - cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); - - byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - byte[] buf = new byte[cmdPkt.hdr.len]; - - Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); - - if(buffer != null) - Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return -1; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return -1; - } - - if(hdr.packetType != AaruPacketType.ResponseAtaLba28) - { - AaruConsole.ErrorWriteLine("Expected ATA LBA28 Response Packet, got packet type {0}...", - hdr.packetType); - - return -1; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketResAtaLba28 res = Marshal.ByteArrayToStructureLittleEndian(buf); - - buffer = new byte[res.buf_len]; - Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); - duration = res.duration; - sense = res.sense != 0; - errorRegisters = res.registers; - - return (int)res.error_no; - } - - /// Sends an ATA/ATAPI command to the remote device using 48-bit LBA addressing - /// 0 if no error occurred, otherwise, errno - /// ATA registers. - /// Status/error registers. - /// ATA Protocol. - /// Indicates which register indicates the transfer length - /// Buffer for ATA/ATAPI command response - /// Timeout in seconds - /// - /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in - /// bytes. - /// - /// Time it took to execute the command in milliseconds - /// True if ATA/ATAPI command returned non-OK status - public int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters, - AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, - uint timeout, bool transferBlocks, out double duration, out bool sense) - { - duration = 0; - sense = true; - errorRegisters = new AtaErrorRegistersLba48(); - - var cmdPkt = new AaruPacketCmdAtaLba48 - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandAtaLba48 - }, - registers = registers, - protocol = (byte)protocol, - transferRegister = (byte)transferRegister, - transferBlocks = transferBlocks, - timeout = timeout * 1000 - }; - - if(buffer != null) - cmdPkt.buf_len = (uint)buffer.Length; - - cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); - - byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - byte[] buf = new byte[cmdPkt.hdr.len]; - - Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); - - if(buffer != null) - Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return -1; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return -1; - } - - if(hdr.packetType != AaruPacketType.ResponseAtaLba48) - { - AaruConsole.ErrorWriteLine("Expected ATA LBA48 Response Packet, got packet type {0}...", - hdr.packetType); - - return -1; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketResAtaLba48 res = Marshal.ByteArrayToStructureLittleEndian(buf); - - buffer = new byte[res.buf_len]; - Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); - duration = res.duration; - sense = res.sense != 0; - errorRegisters = res.registers; - - return (int)res.error_no; - } - - /// Sends a MMC/SD command to the remote device - /// The result of the command. - /// MMC/SD opcode - /// Buffer for MMC/SD command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if MMC/SD returned non-OK status - /// True if data is sent from host to card - /// True if command should be preceded with CMD55 - /// Flags indicating kind and place of response - /// How many blocks to transfer - /// Command argument - /// Response registers - /// Size of block in bytes - public int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, - uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, - out double duration, out bool sense, uint timeout = 0) - { - duration = 0; - sense = true; - response = null; - - var cmdPkt = new AaruPacketCmdSdhci - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandSdhci - }, - command = new AaruCmdSdhci - { - command = command, - write = write, - application = isApplication, - flags = flags, - argument = argument, - block_size = blockSize, - blocks = blocks, - timeout = timeout * 1000 - } - }; - - if(buffer != null) - cmdPkt.command.buf_len = (uint)buffer.Length; - - cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.command.buf_len); - - byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - byte[] buf = new byte[cmdPkt.hdr.len]; - - Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); - - if(buffer != null) - Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.command.buf_len); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return -1; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return -1; - } - - if(hdr.packetType != AaruPacketType.ResponseSdhci) - { - AaruConsole.ErrorWriteLine("Expected SDHCI Response Packet, got packet type {0}...", hdr.packetType); - - return -1; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketResSdhci res = Marshal.ByteArrayToStructureLittleEndian(buf); - - buffer = new byte[res.res.buf_len]; - Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.res.buf_len); - duration = res.res.duration; - sense = res.res.sense != 0; - response = new uint[4]; - response[0] = res.res.response[0]; - response[1] = res.res.response[1]; - response[2] = res.res.response[2]; - response[3] = res.res.response[3]; - - return (int)res.res.error_no; - } - - /// Gets the for the remote device - /// - /// - /// - public DeviceType GetDeviceType() - { - var cmdPkt = new AaruPacketCmdGetDeviceType - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandGetType - } - }; - - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return DeviceType.Unknown; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return DeviceType.Unknown; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return DeviceType.Unknown; - } - - if(hdr.packetType != AaruPacketType.ResponseGetType) - { - AaruConsole.ErrorWriteLine("Expected Device Type Response Packet, got packet type {0}...", - hdr.packetType); - - return DeviceType.Unknown; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return DeviceType.Unknown; - } - - AaruPacketResGetDeviceType res = Marshal.ByteArrayToStructureLittleEndian(buf); - - return res.device_type; - } - - /// Retrieves the SDHCI registers from the remote device - /// CSD register - /// CID register - /// OCR register - /// SCR register - /// true if the device is attached to an SDHCI controller, false otherwise - public bool GetSdhciRegisters(out byte[] csd, out byte[] cid, out byte[] ocr, out byte[] scr) - { - csd = null; - cid = null; - ocr = null; - scr = null; - - var cmdPkt = new AaruPacketCmdGetSdhciRegisters - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandGetSdhciRegisters - } - }; - - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return false; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return false; - } - - if(hdr.packetType != AaruPacketType.ResponseGetSdhciRegisters) - { - AaruConsole.ErrorWriteLine("Expected Device Type Response Packet, got packet type {0}...", - hdr.packetType); - - return false; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketResGetSdhciRegisters res = - Marshal.ByteArrayToStructureLittleEndian(buf); - - if(res.csd_len > 0) - { - if(res.csd_len > 16) - res.csd_len = 16; - - csd = new byte[res.csd_len]; - - Array.Copy(res.csd, 0, csd, 0, res.csd_len); - } - - if(res.cid_len > 0) - { - if(res.cid_len > 16) - res.cid_len = 16; - - cid = new byte[res.cid_len]; - - Array.Copy(res.cid, 0, cid, 0, res.cid_len); - } - - if(res.ocr_len > 0) - { - if(res.ocr_len > 16) - res.ocr_len = 16; - - ocr = new byte[res.ocr_len]; - - Array.Copy(res.ocr, 0, ocr, 0, res.ocr_len); - } - - if(res.scr_len > 0) - { - if(res.scr_len > 16) - res.scr_len = 16; - - scr = new byte[res.scr_len]; - - Array.Copy(res.scr, 0, scr, 0, res.scr_len); - } - - return res.isSdhci; - } - - /// Gets the USB data from the remote device - /// USB descriptors - /// USB vendor ID - /// USB product ID - /// USB manufacturer string - /// USB product string - /// USB serial number string - /// true if the device is attached via USB, false otherwise - public bool GetUsbData(out byte[] descriptors, out ushort idVendor, out ushort idProduct, - out string manufacturer, out string product, out string serial) - { - descriptors = null; - idVendor = 0; - idProduct = 0; - manufacturer = null; - product = null; - serial = null; - - var cmdPkt = new AaruPacketCmdGetUsbData - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandGetUsbData - } - }; - - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return false; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return false; - } - - if(hdr.packetType != AaruPacketType.ResponseGetUsbData) - { - AaruConsole.ErrorWriteLine("Expected USB Data Response Packet, got packet type {0}...", hdr.packetType); - - return false; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketResGetUsbData res = Marshal.ByteArrayToStructureLittleEndian(buf); - - if(!res.isUsb) - return false; - - descriptors = new byte[res.descLen]; - Array.Copy(res.descriptors, 0, descriptors, 0, res.descLen); - idVendor = res.idVendor; - idProduct = res.idProduct; - manufacturer = res.manufacturer; - product = res.product; - serial = res.serial; - - return true; - } - - /// Gets the FireWire data from the remote device - /// FireWire vendor ID - /// FireWire product ID - /// FireWire vendor string - /// FireWire model string - /// FireWire GUID - /// true if the device is attached via FireWire, false otherwise - public bool GetFireWireData(out uint idVendor, out uint idProduct, out ulong guid, out string vendor, - out string model) - { - idVendor = 0; - idProduct = 0; - guid = 0; - vendor = null; - model = null; - - var cmdPkt = new AaruPacketCmdGetFireWireData - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandGetFireWireData - } - }; - - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return false; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return false; - } - - if(hdr.packetType != AaruPacketType.ResponseGetFireWireData) - { - AaruConsole.ErrorWriteLine("Expected FireWire Data Response Packet, got packet type {0}...", - hdr.packetType); - - return false; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketResGetFireWireData res = - Marshal.ByteArrayToStructureLittleEndian(buf); - - if(!res.isFireWire) - return false; - - idVendor = res.idVendor; - idProduct = res.idModel; - guid = res.guid; - vendor = res.vendor; - model = res.model; - - return true; - } - - /// Gets the PCMCIA/CardBus data from the remote device - /// Card Information Structure - /// true if the device is attached via PCMCIA or CardBus, false otherwise - public bool GetPcmciaData(out byte[] cis) - { - cis = null; - - var cmdPkt = new AaruPacketCmdGetPcmciaData - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandGetPcmciaData - } - }; - - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return false; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return false; - } - - if(hdr.packetType != AaruPacketType.ResponseGetPcmciaData) - { - AaruConsole.ErrorWriteLine("Expected PCMCIA Data Response Packet, got packet type {0}...", - hdr.packetType); - - return false; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketResGetPcmciaData res = Marshal.ByteArrayToStructureLittleEndian(buf); - - if(!res.isPcmcia) - return false; - - cis = res.cis; - - return true; - } - - /// Receives data from a socket into a buffer - /// Socket - /// Data buffer - /// Expected total size in bytes - /// Socket flags - /// Retrieved number of bytes - static int Receive(Socket socket, byte[] buffer, int size, SocketFlags socketFlags) - { - int offset = 0; - - while(size > 0) - { - int got = socket.Receive(buffer, offset, size, socketFlags); - - if(got <= 0) - break; - - offset += got; - size -= got; - } - - return offset; - } - - /// Closes the remote device, without closing the network connection - public void Close() - { - var cmdPkt = new AaruPacketCmdClose - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandCloseDevice - } - }; - - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - try - { - _socket.Send(buf, SocketFlags.None); - } - catch(ObjectDisposedException) - { - // Ignore if already disposed - } - } - - /// - /// Concatenates a queue of commands to be send to a remote SecureDigital or MultiMediaCard attached to an SDHCI - /// controller - /// - /// List of commands - /// Duration to execute all commands, in milliseconds - /// Set to true if any of the commands returned an error status, false otherwise - /// Maximum allowed time to execute a single command - /// 0 if no error occurred, otherwise, errno - public int SendMultipleMmcCommands(Device.MmcSingleCommand[] commands, out double duration, out bool sense, - uint timeout = 0) - { - if(ServerProtocolVersion < 2) - return SendMultipleMmcCommandsV1(commands, out duration, out sense, timeout); - - sense = false; - duration = 0; - - long packetSize = Marshal.SizeOf() + - (Marshal.SizeOf() * commands.LongLength); - - foreach(Device.MmcSingleCommand command in commands) - packetSize += command.buffer?.Length ?? 0; - - var packet = new AaruPacketMultiCmdSdhci - { - cmd_count = (ulong)commands.LongLength, - hdr = new AaruPacketHeader - { - len = (uint)packetSize, - packetType = AaruPacketType.MultiCommandSdhci, - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - version = Consts.PACKET_VERSION - } - }; - - byte[] buf = new byte[packetSize]; - byte[] tmp = Marshal.StructureToByteArrayLittleEndian(packet); - - Array.Copy(tmp, 0, buf, 0, tmp.Length); - - int off = tmp.Length; - - foreach(AaruCmdSdhci cmd in commands.Select(command => new AaruCmdSdhci - { - application = command.isApplication, - argument = command.argument, - block_size = command.blockSize, - blocks = command.blocks, - buf_len = (uint)(command.buffer?.Length ?? 0), - command = command.command, - flags = command.flags, - timeout = timeout, - write = command.write - })) - { - tmp = Marshal.StructureToByteArrayLittleEndian(cmd); - Array.Copy(tmp, 0, buf, off, tmp.Length); - - off += tmp.Length; - } - - foreach(Device.MmcSingleCommand command in commands.Where(command => (command.buffer?.Length ?? 0) != 0)) - { - Array.Copy(command.buffer, 0, buf, off, command.buffer.Length); - - off += command.buffer.Length; - } - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return -1; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return -1; - } - - if(hdr.packetType != AaruPacketType.ResponseMultiSdhci) - { - AaruConsole.ErrorWriteLine("Expected multi MMC/SD command Response Packet, got packet type {0}...", - hdr.packetType); - - return -1; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return -1; - } - - AaruPacketMultiCmdSdhci res = Marshal.ByteArrayToStructureLittleEndian(buf); - - if(res.cmd_count != (ulong)commands.Length) - { - AaruConsole.ErrorWriteLine("Expected the response to {0} SD/MMC commands, but got {1} responses...", - commands.Length, res.cmd_count); - - return -1; - } - - off = Marshal.SizeOf(); - - int error = 0; - - foreach(Device.MmcSingleCommand command in commands) - { - AaruResSdhci cmdRes = - Marshal.ByteArrayToStructureLittleEndian(buf, off, Marshal.SizeOf()); - - command.response = cmdRes.response; - duration += cmdRes.duration; - - if(cmdRes.error_no != 0 && - error == 0) - error = (int)cmdRes.error_no; - - if(cmdRes.sense != 0) - sense = true; - - if(cmdRes.buf_len > 0) - command.buffer = new byte[cmdRes.buf_len]; - - off += Marshal.SizeOf(); - } - - foreach(Device.MmcSingleCommand command in commands) - { - Array.Copy(buf, off, command.buffer, 0, command.buffer.Length); - off += command.buffer.Length; - } - - return error; - } - - /// - /// Concatenates a queue of commands to be send to a remote SecureDigital or MultiMediaCard attached to an SDHCI - /// controller, using protocol version 1 without specific support for such a queueing - /// - /// List of commands - /// Duration to execute all commands, in milliseconds - /// Set to true if any of the commands returned an error status, false otherwise - /// Maximum allowed time to execute a single command - /// 0 if no error occurred, otherwise, errno - int SendMultipleMmcCommandsV1(Device.MmcSingleCommand[] commands, out double duration, out bool sense, - uint timeout) - { - sense = false; - duration = 0; - int error = 0; - - foreach(Device.MmcSingleCommand command in commands) - { - error = SendMmcCommand(command.command, command.write, command.isApplication, command.flags, - command.argument, command.blockSize, command.blocks, ref command.buffer, - out command.response, out double cmdDuration, out bool cmdSense, timeout); - - if(cmdSense) - sense = true; - - duration += cmdDuration; - } - - return error; - } - - /// Closes then immediately reopens a remote device - /// Returned error number if any - public bool ReOpen() - { - if(ServerProtocolVersion < 2) - return false; - - var cmdPkt = new AaruPacketCmdReOpen - { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandReOpenDevice - } - }; - - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - - int len = _socket.Send(buf, SocketFlags.None); - - if(len != buf.Length) - { - AaruConsole.ErrorWriteLine("Could not write to the network..."); - - return false; - } - - byte[] hdrBuf = new byte[Marshal.SizeOf()]; - - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); - - if(len < hdrBuf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); - - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) - { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return false; - } - - if(hdr.packetType != AaruPacketType.Nop) - { - AaruConsole.ErrorWriteLine("Expected NOP Packet, got packet type {0}...", hdr.packetType); - - return false; - } - - if(hdr.version != Consts.PACKET_VERSION) - { - AaruConsole.ErrorWriteLine("Unrecognized packet version..."); - - return false; - } - - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); - - if(len < buf.Length) - { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; - } - - AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); - - switch(nop.reasonCode) - { - case AaruNopReason.ReOpenOk: return true; - case AaruNopReason.CloseError: - case AaruNopReason.OpenError: - AaruConsole.ErrorWriteLine("ReOpen error closing device..."); - - break; - default: - AaruConsole.ErrorWriteLine("ReOpen error {0} with reason: {1}...", nop.errno, nop.reason); - - break; - } + AaruConsole.ErrorWriteLine("Could not read from the network..."); + lastError = -1; return false; } - /// Reads data using operating system buffers. - /// Data buffer - /// Offset in remote device to start reading, in bytes - /// Number of bytes to read - /// Total time in milliseconds the reading took - /// true if there was an error, false otherwise - public bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration) + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) { - duration = 0; - buffer = null; + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + lastError = -1; - if(ServerProtocolVersion < 2) - return false; + return false; + } - var cmdPkt = new AaruPacketCmdOsRead + if(hdr.packetType != AaruPacketType.Nop) + { + AaruConsole.ErrorWriteLine("Expected List Devices Response Packet, got packet type {0}...", + hdr.packetType); + + lastError = -1; + + return false; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + lastError = -1; + + return false; + } + + AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); + + switch(nop.reasonCode) + { + case AaruNopReason.OpenOk: return true; + case AaruNopReason.NotImplemented: throw new NotImplementedException($"{nop.reason}"); + } + + AaruConsole.ErrorWriteLine($"{nop.reason}"); + lastError = nop.errno; + + return false; + } + + /// Sends a SCSI command to the remote device + /// 0 if no error occurred, otherwise, errno + /// SCSI CDB + /// Buffer for SCSI command response + /// Buffer with the SCSI sense + /// Timeout in seconds + /// SCSI command transfer direction + /// Time it took to execute the command in milliseconds + /// + /// True if SCSI command returned non-OK status and contains + /// SCSI sense + /// + public int SendScsiCommand(byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, uint timeout, + ScsiDirection direction, out double duration, out bool sense) + { + senseBuffer = null; + duration = 0; + sense = true; + + var cmdPkt = new AaruPacketCmdScsi + { + hdr = new AaruPacketHeader { - hdr = new AaruPacketHeader - { - remote_id = Consts.REMOTE_ID, - packet_id = Consts.PACKET_ID, - len = (uint)Marshal.SizeOf(), - version = Consts.PACKET_VERSION, - packetType = AaruPacketType.CommandOsRead - }, - length = length, - offset = (ulong)offset - }; + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandScsi + }, + direction = (int)direction, + timeout = timeout * 1000 + }; - byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + if(cdb != null) + cmdPkt.cdb_len = (uint)cdb.Length; - int len = _socket.Send(buf, SocketFlags.None); + if(buffer != null) + cmdPkt.buf_len = (uint)buffer.Length; - if(len != buf.Length) + cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.cdb_len + cmdPkt.buf_len); + + byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + byte[] buf = new byte[cmdPkt.hdr.len]; + + Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); + + if(cdb != null) + Array.Copy(cdb, 0, buf, Marshal.SizeOf(), cmdPkt.cdb_len); + + if(buffer != null) + Array.Copy(buffer, 0, buf, Marshal.SizeOf() + cmdPkt.cdb_len, cmdPkt.buf_len); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return -1; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return -1; + } + + if(hdr.packetType != AaruPacketType.ResponseScsi) + { + AaruConsole.ErrorWriteLine("Expected SCSI Response Packet, got packet type {0}...", hdr.packetType); + + return -1; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketResScsi res = Marshal.ByteArrayToStructureLittleEndian(buf); + + senseBuffer = new byte[res.sense_len]; + Array.Copy(buf, Marshal.SizeOf(), senseBuffer, 0, res.sense_len); + buffer = new byte[res.buf_len]; + Array.Copy(buf, Marshal.SizeOf() + res.sense_len, buffer, 0, res.buf_len); + duration = res.duration; + sense = res.sense != 0; + + return (int)res.error_no; + } + + /// Sends an ATA/ATAPI command to the remote device using CHS addressing + /// 0 if no error occurred, otherwise, errno + /// ATA registers. + /// Status/error registers. + /// ATA Protocol. + /// Indicates which register indicates the transfer length + /// Buffer for ATA/ATAPI command response + /// Timeout in seconds + /// + /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in + /// bytes. + /// + /// Time it took to execute the command in milliseconds + /// True if ATA/ATAPI command returned non-OK status + public int SendAtaCommand(AtaRegistersChs registers, out AtaErrorRegistersChs errorRegisters, + AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, + uint timeout, bool transferBlocks, out double duration, out bool sense) + { + duration = 0; + sense = true; + errorRegisters = new AtaErrorRegistersChs(); + + var cmdPkt = new AaruPacketCmdAtaChs + { + hdr = new AaruPacketHeader { - AaruConsole.ErrorWriteLine("Could not write to the network..."); + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandAtaChs + }, + registers = registers, + protocol = (byte)protocol, + transferRegister = (byte)transferRegister, + transferBlocks = transferBlocks, + timeout = timeout * 1000 + }; - return false; + if(buffer != null) + cmdPkt.buf_len = (uint)buffer.Length; + + cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); + + byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + byte[] buf = new byte[cmdPkt.hdr.len]; + + Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); + + if(buffer != null) + Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return -1; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return -1; + } + + if(hdr.packetType != AaruPacketType.ResponseAtaChs) + { + AaruConsole.ErrorWriteLine("Expected ATA CHS Response Packet, got packet type {0}...", hdr.packetType); + + return -1; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketResAtaChs res = Marshal.ByteArrayToStructureLittleEndian(buf); + + buffer = new byte[res.buf_len]; + Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); + duration = res.duration; + sense = res.sense != 0; + errorRegisters = res.registers; + + return (int)res.error_no; + } + + /// Sends an ATA/ATAPI command to the remote device using 28-bit LBA addressing + /// 0 if no error occurred, otherwise, errno + /// ATA registers. + /// Status/error registers. + /// ATA Protocol. + /// Indicates which register indicates the transfer length + /// Buffer for ATA/ATAPI command response + /// Timeout in seconds + /// + /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in + /// bytes. + /// + /// Time it took to execute the command in milliseconds + /// True if ATA/ATAPI command returned non-OK status + public int SendAtaCommand(AtaRegistersLba28 registers, out AtaErrorRegistersLba28 errorRegisters, + AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, + uint timeout, bool transferBlocks, out double duration, out bool sense) + { + duration = 0; + sense = true; + errorRegisters = new AtaErrorRegistersLba28(); + + var cmdPkt = new AaruPacketCmdAtaLba28 + { + hdr = new AaruPacketHeader + { + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandAtaLba28 + }, + registers = registers, + protocol = (byte)protocol, + transferRegister = (byte)transferRegister, + transferBlocks = transferBlocks, + timeout = timeout * 1000 + }; + + if(buffer != null) + cmdPkt.buf_len = (uint)buffer.Length; + + cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); + + byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + byte[] buf = new byte[cmdPkt.hdr.len]; + + Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); + + if(buffer != null) + Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return -1; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return -1; + } + + if(hdr.packetType != AaruPacketType.ResponseAtaLba28) + { + AaruConsole.ErrorWriteLine("Expected ATA LBA28 Response Packet, got packet type {0}...", + hdr.packetType); + + return -1; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketResAtaLba28 res = Marshal.ByteArrayToStructureLittleEndian(buf); + + buffer = new byte[res.buf_len]; + Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); + duration = res.duration; + sense = res.sense != 0; + errorRegisters = res.registers; + + return (int)res.error_no; + } + + /// Sends an ATA/ATAPI command to the remote device using 48-bit LBA addressing + /// 0 if no error occurred, otherwise, errno + /// ATA registers. + /// Status/error registers. + /// ATA Protocol. + /// Indicates which register indicates the transfer length + /// Buffer for ATA/ATAPI command response + /// Timeout in seconds + /// + /// If set to true, transfer is indicated in blocks, otherwise, it is indicated in + /// bytes. + /// + /// Time it took to execute the command in milliseconds + /// True if ATA/ATAPI command returned non-OK status + public int SendAtaCommand(AtaRegistersLba48 registers, out AtaErrorRegistersLba48 errorRegisters, + AtaProtocol protocol, AtaTransferRegister transferRegister, ref byte[] buffer, + uint timeout, bool transferBlocks, out double duration, out bool sense) + { + duration = 0; + sense = true; + errorRegisters = new AtaErrorRegistersLba48(); + + var cmdPkt = new AaruPacketCmdAtaLba48 + { + hdr = new AaruPacketHeader + { + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandAtaLba48 + }, + registers = registers, + protocol = (byte)protocol, + transferRegister = (byte)transferRegister, + transferBlocks = transferBlocks, + timeout = timeout * 1000 + }; + + if(buffer != null) + cmdPkt.buf_len = (uint)buffer.Length; + + cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.buf_len); + + byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + byte[] buf = new byte[cmdPkt.hdr.len]; + + Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); + + if(buffer != null) + Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.buf_len); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return -1; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return -1; + } + + if(hdr.packetType != AaruPacketType.ResponseAtaLba48) + { + AaruConsole.ErrorWriteLine("Expected ATA LBA48 Response Packet, got packet type {0}...", + hdr.packetType); + + return -1; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketResAtaLba48 res = Marshal.ByteArrayToStructureLittleEndian(buf); + + buffer = new byte[res.buf_len]; + Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.buf_len); + duration = res.duration; + sense = res.sense != 0; + errorRegisters = res.registers; + + return (int)res.error_no; + } + + /// Sends a MMC/SD command to the remote device + /// The result of the command. + /// MMC/SD opcode + /// Buffer for MMC/SD command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if MMC/SD returned non-OK status + /// True if data is sent from host to card + /// True if command should be preceded with CMD55 + /// Flags indicating kind and place of response + /// How many blocks to transfer + /// Command argument + /// Response registers + /// Size of block in bytes + public int SendMmcCommand(MmcCommands command, bool write, bool isApplication, MmcFlags flags, uint argument, + uint blockSize, uint blocks, ref byte[] buffer, out uint[] response, + out double duration, out bool sense, uint timeout = 0) + { + duration = 0; + sense = true; + response = null; + + var cmdPkt = new AaruPacketCmdSdhci + { + hdr = new AaruPacketHeader + { + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandSdhci + }, + command = new AaruCmdSdhci + { + command = command, + write = write, + application = isApplication, + flags = flags, + argument = argument, + block_size = blockSize, + blocks = blocks, + timeout = timeout * 1000 } + }; - byte[] hdrBuf = new byte[Marshal.SizeOf()]; + if(buffer != null) + cmdPkt.command.buf_len = (uint)buffer.Length; - len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + cmdPkt.hdr.len = (uint)(Marshal.SizeOf() + cmdPkt.command.buf_len); - if(len < hdrBuf.Length) + byte[] pktBuf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + byte[] buf = new byte[cmdPkt.hdr.len]; + + Array.Copy(pktBuf, 0, buf, 0, Marshal.SizeOf()); + + if(buffer != null) + Array.Copy(buffer, 0, buf, Marshal.SizeOf(), cmdPkt.command.buf_len); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return -1; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return -1; + } + + if(hdr.packetType != AaruPacketType.ResponseSdhci) + { + AaruConsole.ErrorWriteLine("Expected SDHCI Response Packet, got packet type {0}...", hdr.packetType); + + return -1; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketResSdhci res = Marshal.ByteArrayToStructureLittleEndian(buf); + + buffer = new byte[res.res.buf_len]; + Array.Copy(buf, Marshal.SizeOf(), buffer, 0, res.res.buf_len); + duration = res.res.duration; + sense = res.res.sense != 0; + response = new uint[4]; + response[0] = res.res.response[0]; + response[1] = res.res.response[1]; + response[2] = res.res.response[2]; + response[3] = res.res.response[3]; + + return (int)res.res.error_no; + } + + /// Gets the for the remote device + /// + /// + /// + public DeviceType GetDeviceType() + { + var cmdPkt = new AaruPacketCmdGetDeviceType + { + hdr = new AaruPacketHeader { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandGetType } + }; - AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - if(hdr.remote_id != Consts.REMOTE_ID || - hdr.packet_id != Consts.PACKET_ID) + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return DeviceType.Unknown; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return DeviceType.Unknown; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return DeviceType.Unknown; + } + + if(hdr.packetType != AaruPacketType.ResponseGetType) + { + AaruConsole.ErrorWriteLine("Expected Device Type Response Packet, got packet type {0}...", + hdr.packetType); + + return DeviceType.Unknown; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return DeviceType.Unknown; + } + + AaruPacketResGetDeviceType res = Marshal.ByteArrayToStructureLittleEndian(buf); + + return res.device_type; + } + + /// Retrieves the SDHCI registers from the remote device + /// CSD register + /// CID register + /// OCR register + /// SCR register + /// true if the device is attached to an SDHCI controller, false otherwise + public bool GetSdhciRegisters(out byte[] csd, out byte[] cid, out byte[] ocr, out byte[] scr) + { + csd = null; + cid = null; + ocr = null; + scr = null; + + var cmdPkt = new AaruPacketCmdGetSdhciRegisters + { + hdr = new AaruPacketHeader { - AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); - - return false; + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandGetSdhciRegisters } + }; - if(hdr.packetType != AaruPacketType.ResponseOsRead) + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return false; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return false; + } + + if(hdr.packetType != AaruPacketType.ResponseGetSdhciRegisters) + { + AaruConsole.ErrorWriteLine("Expected Device Type Response Packet, got packet type {0}...", + hdr.packetType); + + return false; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketResGetSdhciRegisters res = + Marshal.ByteArrayToStructureLittleEndian(buf); + + if(res.csd_len > 0) + { + if(res.csd_len > 16) + res.csd_len = 16; + + csd = new byte[res.csd_len]; + + Array.Copy(res.csd, 0, csd, 0, res.csd_len); + } + + if(res.cid_len > 0) + { + if(res.cid_len > 16) + res.cid_len = 16; + + cid = new byte[res.cid_len]; + + Array.Copy(res.cid, 0, cid, 0, res.cid_len); + } + + if(res.ocr_len > 0) + { + if(res.ocr_len > 16) + res.ocr_len = 16; + + ocr = new byte[res.ocr_len]; + + Array.Copy(res.ocr, 0, ocr, 0, res.ocr_len); + } + + if(res.scr_len > 0) + { + if(res.scr_len > 16) + res.scr_len = 16; + + scr = new byte[res.scr_len]; + + Array.Copy(res.scr, 0, scr, 0, res.scr_len); + } + + return res.isSdhci; + } + + /// Gets the USB data from the remote device + /// USB descriptors + /// USB vendor ID + /// USB product ID + /// USB manufacturer string + /// USB product string + /// USB serial number string + /// true if the device is attached via USB, false otherwise + public bool GetUsbData(out byte[] descriptors, out ushort idVendor, out ushort idProduct, + out string manufacturer, out string product, out string serial) + { + descriptors = null; + idVendor = 0; + idProduct = 0; + manufacturer = null; + product = null; + serial = null; + + var cmdPkt = new AaruPacketCmdGetUsbData + { + hdr = new AaruPacketHeader { - AaruConsole.ErrorWriteLine("Expected OS Read Response Packet, got packet type {0}...", hdr.packetType); - - return false; + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandGetUsbData } + }; - if(hdr.version != Consts.PACKET_VERSION) + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return false; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return false; + } + + if(hdr.packetType != AaruPacketType.ResponseGetUsbData) + { + AaruConsole.ErrorWriteLine("Expected USB Data Response Packet, got packet type {0}...", hdr.packetType); + + return false; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketResGetUsbData res = Marshal.ByteArrayToStructureLittleEndian(buf); + + if(!res.isUsb) + return false; + + descriptors = new byte[res.descLen]; + Array.Copy(res.descriptors, 0, descriptors, 0, res.descLen); + idVendor = res.idVendor; + idProduct = res.idProduct; + manufacturer = res.manufacturer; + product = res.product; + serial = res.serial; + + return true; + } + + /// Gets the FireWire data from the remote device + /// FireWire vendor ID + /// FireWire product ID + /// FireWire vendor string + /// FireWire model string + /// FireWire GUID + /// true if the device is attached via FireWire, false otherwise + public bool GetFireWireData(out uint idVendor, out uint idProduct, out ulong guid, out string vendor, + out string model) + { + idVendor = 0; + idProduct = 0; + guid = 0; + vendor = null; + model = null; + + var cmdPkt = new AaruPacketCmdGetFireWireData + { + hdr = new AaruPacketHeader { - AaruConsole.ErrorWriteLine("Unrecognized packet version..."); - - return false; + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandGetFireWireData } + }; - buf = new byte[hdr.len]; - len = Receive(_socket, buf, buf.Length, SocketFlags.None); + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - if(len < buf.Length) + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return false; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return false; + } + + if(hdr.packetType != AaruPacketType.ResponseGetFireWireData) + { + AaruConsole.ErrorWriteLine("Expected FireWire Data Response Packet, got packet type {0}...", + hdr.packetType); + + return false; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketResGetFireWireData res = + Marshal.ByteArrayToStructureLittleEndian(buf); + + if(!res.isFireWire) + return false; + + idVendor = res.idVendor; + idProduct = res.idModel; + guid = res.guid; + vendor = res.vendor; + model = res.model; + + return true; + } + + /// Gets the PCMCIA/CardBus data from the remote device + /// Card Information Structure + /// true if the device is attached via PCMCIA or CardBus, false otherwise + public bool GetPcmciaData(out byte[] cis) + { + cis = null; + + var cmdPkt = new AaruPacketCmdGetPcmciaData + { + hdr = new AaruPacketHeader { - AaruConsole.ErrorWriteLine("Could not read from the network..."); - - return false; + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandGetPcmciaData } + }; - AaruPacketResOsRead osRead = Marshal.ByteArrayToStructureLittleEndian(buf); + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - duration = osRead.duration; + int len = _socket.Send(buf, SocketFlags.None); - if(osRead.errno != 0) + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return false; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return false; + } + + if(hdr.packetType != AaruPacketType.ResponseGetPcmciaData) + { + AaruConsole.ErrorWriteLine("Expected PCMCIA Data Response Packet, got packet type {0}...", + hdr.packetType); + + return false; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketResGetPcmciaData res = Marshal.ByteArrayToStructureLittleEndian(buf); + + if(!res.isPcmcia) + return false; + + cis = res.cis; + + return true; + } + + /// Receives data from a socket into a buffer + /// Socket + /// Data buffer + /// Expected total size in bytes + /// Socket flags + /// Retrieved number of bytes + static int Receive(Socket socket, byte[] buffer, int size, SocketFlags socketFlags) + { + int offset = 0; + + while(size > 0) + { + int got = socket.Receive(buffer, offset, size, socketFlags); + + if(got <= 0) + break; + + offset += got; + size -= got; + } + + return offset; + } + + /// Closes the remote device, without closing the network connection + public void Close() + { + var cmdPkt = new AaruPacketCmdClose + { + hdr = new AaruPacketHeader { - AaruConsole.ErrorWriteLine("Remote error {0} in OS Read...", osRead.errno); - - return false; + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandCloseDevice } + }; - buffer = new byte[length]; - Array.Copy(buf, Marshal.SizeOf(), buffer, 0, length); + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); - return true; + try + { + _socket.Send(buf, SocketFlags.None); + } + catch(ObjectDisposedException) + { + // Ignore if already disposed } } + + /// + /// Concatenates a queue of commands to be send to a remote SecureDigital or MultiMediaCard attached to an SDHCI + /// controller + /// + /// List of commands + /// Duration to execute all commands, in milliseconds + /// Set to true if any of the commands returned an error status, false otherwise + /// Maximum allowed time to execute a single command + /// 0 if no error occurred, otherwise, errno + public int SendMultipleMmcCommands(Device.MmcSingleCommand[] commands, out double duration, out bool sense, + uint timeout = 0) + { + if(ServerProtocolVersion < 2) + return SendMultipleMmcCommandsV1(commands, out duration, out sense, timeout); + + sense = false; + duration = 0; + + long packetSize = Marshal.SizeOf() + + (Marshal.SizeOf() * commands.LongLength); + + foreach(Device.MmcSingleCommand command in commands) + packetSize += command.buffer?.Length ?? 0; + + var packet = new AaruPacketMultiCmdSdhci + { + cmd_count = (ulong)commands.LongLength, + hdr = new AaruPacketHeader + { + len = (uint)packetSize, + packetType = AaruPacketType.MultiCommandSdhci, + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + version = Consts.PACKET_VERSION + } + }; + + byte[] buf = new byte[packetSize]; + byte[] tmp = Marshal.StructureToByteArrayLittleEndian(packet); + + Array.Copy(tmp, 0, buf, 0, tmp.Length); + + int off = tmp.Length; + + foreach(AaruCmdSdhci cmd in commands.Select(command => new AaruCmdSdhci + { + application = command.isApplication, + argument = command.argument, + block_size = command.blockSize, + blocks = command.blocks, + buf_len = (uint)(command.buffer?.Length ?? 0), + command = command.command, + flags = command.flags, + timeout = timeout, + write = command.write + })) + { + tmp = Marshal.StructureToByteArrayLittleEndian(cmd); + Array.Copy(tmp, 0, buf, off, tmp.Length); + + off += tmp.Length; + } + + foreach(Device.MmcSingleCommand command in commands.Where(command => (command.buffer?.Length ?? 0) != 0)) + { + Array.Copy(command.buffer, 0, buf, off, command.buffer.Length); + + off += command.buffer.Length; + } + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return -1; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return -1; + } + + if(hdr.packetType != AaruPacketType.ResponseMultiSdhci) + { + AaruConsole.ErrorWriteLine("Expected multi MMC/SD command Response Packet, got packet type {0}...", + hdr.packetType); + + return -1; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return -1; + } + + AaruPacketMultiCmdSdhci res = Marshal.ByteArrayToStructureLittleEndian(buf); + + if(res.cmd_count != (ulong)commands.Length) + { + AaruConsole.ErrorWriteLine("Expected the response to {0} SD/MMC commands, but got {1} responses...", + commands.Length, res.cmd_count); + + return -1; + } + + off = Marshal.SizeOf(); + + int error = 0; + + foreach(Device.MmcSingleCommand command in commands) + { + AaruResSdhci cmdRes = + Marshal.ByteArrayToStructureLittleEndian(buf, off, Marshal.SizeOf()); + + command.response = cmdRes.response; + duration += cmdRes.duration; + + if(cmdRes.error_no != 0 && + error == 0) + error = (int)cmdRes.error_no; + + if(cmdRes.sense != 0) + sense = true; + + if(cmdRes.buf_len > 0) + command.buffer = new byte[cmdRes.buf_len]; + + off += Marshal.SizeOf(); + } + + foreach(Device.MmcSingleCommand command in commands) + { + Array.Copy(buf, off, command.buffer, 0, command.buffer.Length); + off += command.buffer.Length; + } + + return error; + } + + /// + /// Concatenates a queue of commands to be send to a remote SecureDigital or MultiMediaCard attached to an SDHCI + /// controller, using protocol version 1 without specific support for such a queueing + /// + /// List of commands + /// Duration to execute all commands, in milliseconds + /// Set to true if any of the commands returned an error status, false otherwise + /// Maximum allowed time to execute a single command + /// 0 if no error occurred, otherwise, errno + int SendMultipleMmcCommandsV1(Device.MmcSingleCommand[] commands, out double duration, out bool sense, + uint timeout) + { + sense = false; + duration = 0; + int error = 0; + + foreach(Device.MmcSingleCommand command in commands) + { + error = SendMmcCommand(command.command, command.write, command.isApplication, command.flags, + command.argument, command.blockSize, command.blocks, ref command.buffer, + out command.response, out double cmdDuration, out bool cmdSense, timeout); + + if(cmdSense) + sense = true; + + duration += cmdDuration; + } + + return error; + } + + /// Closes then immediately reopens a remote device + /// Returned error number if any + public bool ReOpen() + { + if(ServerProtocolVersion < 2) + return false; + + var cmdPkt = new AaruPacketCmdReOpen + { + hdr = new AaruPacketHeader + { + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandReOpenDevice + } + }; + + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return false; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return false; + } + + if(hdr.packetType != AaruPacketType.Nop) + { + AaruConsole.ErrorWriteLine("Expected NOP Packet, got packet type {0}...", hdr.packetType); + + return false; + } + + if(hdr.version != Consts.PACKET_VERSION) + { + AaruConsole.ErrorWriteLine("Unrecognized packet version..."); + + return false; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketNop nop = Marshal.ByteArrayToStructureLittleEndian(buf); + + switch(nop.reasonCode) + { + case AaruNopReason.ReOpenOk: return true; + case AaruNopReason.CloseError: + case AaruNopReason.OpenError: + AaruConsole.ErrorWriteLine("ReOpen error closing device..."); + + break; + default: + AaruConsole.ErrorWriteLine("ReOpen error {0} with reason: {1}...", nop.errno, nop.reason); + + break; + } + + return false; + } + + /// Reads data using operating system buffers. + /// Data buffer + /// Offset in remote device to start reading, in bytes + /// Number of bytes to read + /// Total time in milliseconds the reading took + /// true if there was an error, false otherwise + public bool BufferedOsRead(out byte[] buffer, long offset, uint length, out double duration) + { + duration = 0; + buffer = null; + + if(ServerProtocolVersion < 2) + return false; + + var cmdPkt = new AaruPacketCmdOsRead + { + hdr = new AaruPacketHeader + { + remote_id = Consts.REMOTE_ID, + packet_id = Consts.PACKET_ID, + len = (uint)Marshal.SizeOf(), + version = Consts.PACKET_VERSION, + packetType = AaruPacketType.CommandOsRead + }, + length = length, + offset = (ulong)offset + }; + + byte[] buf = Marshal.StructureToByteArrayLittleEndian(cmdPkt); + + int len = _socket.Send(buf, SocketFlags.None); + + if(len != buf.Length) + { + AaruConsole.ErrorWriteLine("Could not write to the network..."); + + return false; + } + + byte[] hdrBuf = new byte[Marshal.SizeOf()]; + + len = Receive(_socket, hdrBuf, hdrBuf.Length, SocketFlags.Peek); + + if(len < hdrBuf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketHeader hdr = Marshal.ByteArrayToStructureLittleEndian(hdrBuf); + + if(hdr.remote_id != Consts.REMOTE_ID || + hdr.packet_id != Consts.PACKET_ID) + { + AaruConsole.ErrorWriteLine("Received data is not an Aaru Remote Packet..."); + + return false; + } + + if(hdr.packetType != AaruPacketType.ResponseOsRead) + { + AaruConsole.ErrorWriteLine("Expected OS Read Response Packet, got packet type {0}...", hdr.packetType); + + return false; + } + + if(hdr.version != Consts.PACKET_VERSION) + { + AaruConsole.ErrorWriteLine("Unrecognized packet version..."); + + return false; + } + + buf = new byte[hdr.len]; + len = Receive(_socket, buf, buf.Length, SocketFlags.None); + + if(len < buf.Length) + { + AaruConsole.ErrorWriteLine("Could not read from the network..."); + + return false; + } + + AaruPacketResOsRead osRead = Marshal.ByteArrayToStructureLittleEndian(buf); + + duration = osRead.duration; + + if(osRead.errno != 0) + { + AaruConsole.ErrorWriteLine("Remote error {0} in OS Read...", osRead.errno); + + return false; + } + + buffer = new byte[length]; + Array.Copy(buf, Marshal.SizeOf(), buffer, 0, length); + + return true; + } } \ No newline at end of file diff --git a/Aaru.Devices/Remote/Structs.cs b/Aaru.Devices/Remote/Structs.cs index 5276d448a..249d19a48 100644 --- a/Aaru.Devices/Remote/Structs.cs +++ b/Aaru.Devices/Remote/Structs.cs @@ -39,583 +39,582 @@ using Aaru.Decoders.ATA; // ReSharper disable FieldCanBeMadeReadOnly.Global // ReSharper disable IdentifierTypo -namespace Aaru.Devices.Remote +namespace Aaru.Devices.Remote; + +/// Header for any Aaru remote packet +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketHeader { - /// Header for any Aaru remote packet - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketHeader - { - /// Unique Aaru packet identifier (primary) - public uint remote_id; - /// Unique Aaru packet identifier (secondary) - public uint packet_id; - /// Packet length - public uint len; - /// Packet version - public byte version; - /// Unique Aaru packet type identifier - public AaruPacketType packetType; - /// Spare for expansion (or alignment) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] spare; - } + /// Unique Aaru packet identifier (primary) + public uint remote_id; + /// Unique Aaru packet identifier (secondary) + public uint packet_id; + /// Packet length + public uint len; + /// Packet version + public byte version; + /// Unique Aaru packet type identifier + public AaruPacketType packetType; + /// Spare for expansion (or alignment) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] spare; +} - /// Hello packet, identifies a remote initiator with a responder - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketHello - { - /// Packet header - public AaruPacketHeader hdr; - /// Application name - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] - public string application; - /// Application version - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] - public string version; - /// Maximum supported protocol version - public byte maxProtocol; - /// Spare for expansion (or alignment) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] spare; - /// Operating system name - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string sysname; - /// Operating system version / release - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string release; - /// Operating system machine / architecture - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string machine; - } +/// Hello packet, identifies a remote initiator with a responder +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketHello +{ + /// Packet header + public AaruPacketHeader hdr; + /// Application name + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] + public string application; + /// Application version + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] + public string version; + /// Maximum supported protocol version + public byte maxProtocol; + /// Spare for expansion (or alignment) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] spare; + /// Operating system name + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string sysname; + /// Operating system version / release + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string release; + /// Operating system machine / architecture + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string machine; +} - /// Request a list of device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCommandListDevices - { - /// Packet header - public AaruPacketHeader hdr; - } +/// Request a list of device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCommandListDevices +{ + /// Packet header + public AaruPacketHeader hdr; +} - /// Returns the requested list of devices - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public readonly struct AaruPacketResponseListDevices - { - /// Packet header - public readonly AaruPacketHeader hdr; - /// How many device descriptors follows this structure in the packet - public readonly ushort devices; - } +/// Returns the requested list of devices +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public readonly struct AaruPacketResponseListDevices +{ + /// Packet header + public readonly AaruPacketHeader hdr; + /// How many device descriptors follows this structure in the packet + public readonly ushort devices; +} - /// Sends a request or returns a response that requires no intervention or further processing - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketNop - { - /// Packet header - public AaruPacketHeader hdr; - /// Reason code - public AaruNopReason reasonCode; - /// Spare for expansion (or alignment) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] spare; - /// Reason name - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string reason; - /// Operating system error number - public int errno; - } +/// Sends a request or returns a response that requires no intervention or further processing +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketNop +{ + /// Packet header + public AaruPacketHeader hdr; + /// Reason code + public AaruNopReason reasonCode; + /// Spare for expansion (or alignment) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] spare; + /// Reason name + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string reason; + /// Operating system error number + public int errno; +} - /// Requests to open a device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCommandOpenDevice - { - /// Packet header - public AaruPacketHeader hdr; - /// Device path - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] - public string device_path; - } +/// Requests to open a device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCommandOpenDevice +{ + /// Packet header + public AaruPacketHeader hdr; + /// Device path + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 1024)] + public string device_path; +} - /// - /// Requests remote to send a command to a SCSI device. This header is followed by the CDB and after it comes the - /// buffer. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdScsi - { - /// Packet header - public AaruPacketHeader hdr; - /// Length in bytes of the CDB that follows this structure - public uint cdb_len; - /// Length in bytes of the buffer that follows the CDB - public uint buf_len; - /// Direction of SCSI data transfer - public int direction; - /// Timeout waiting for device to respond to command - public uint timeout; - } +/// +/// Requests remote to send a command to a SCSI device. This header is followed by the CDB and after it comes the +/// buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdScsi +{ + /// Packet header + public AaruPacketHeader hdr; + /// Length in bytes of the CDB that follows this structure + public uint cdb_len; + /// Length in bytes of the buffer that follows the CDB + public uint buf_len; + /// Direction of SCSI data transfer + public int direction; + /// Timeout waiting for device to respond to command + public uint timeout; +} - /// - /// Returns the response from a command sent to a SCSI device. This structure is followed by the buffer containing - /// the REQUEST SENSE response and this is followed by the data buffer. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResScsi - { - /// Packet header - public AaruPacketHeader hdr; - /// Size the REQUEST SENSE buffer that follows this structure - public uint sense_len; - /// Length in bytes of the data buffer that follows the sense buffer - public uint buf_len; - /// Time in milliseconds it took for the device to execute the command - public uint duration; - /// Set to anything different of zero if there was a SENSE returned - public uint sense; - /// Set to the remote operating system error number - public uint error_no; - } +/// +/// Returns the response from a command sent to a SCSI device. This structure is followed by the buffer containing +/// the REQUEST SENSE response and this is followed by the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResScsi +{ + /// Packet header + public AaruPacketHeader hdr; + /// Size the REQUEST SENSE buffer that follows this structure + public uint sense_len; + /// Length in bytes of the data buffer that follows the sense buffer + public uint buf_len; + /// Time in milliseconds it took for the device to execute the command + public uint duration; + /// Set to anything different of zero if there was a SENSE returned + public uint sense; + /// Set to the remote operating system error number + public uint error_no; +} - /// - /// Requests remote to send a command to an ATA device using the CHS command set. This header is followed by the - /// data buffer. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdAtaChs - { - /// Packet header - public AaruPacketHeader hdr; - /// Length in bytes of the data buffer - public uint buf_len; - /// Registers to set in the ATA device - public AtaRegistersChs registers; - /// ATA protocol code - public byte protocol; - /// ATA transfer register indicator - public byte transferRegister; - /// Set to true to transfer blocks, false to transfer bytes - [MarshalAs(UnmanagedType.U1)] - public bool transferBlocks; - /// Spare for expansion (or alignment) - public byte spare; - /// Timeout waiting for device to respond to command - public uint timeout; - } +/// +/// Requests remote to send a command to an ATA device using the CHS command set. This header is followed by the +/// data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdAtaChs +{ + /// Packet header + public AaruPacketHeader hdr; + /// Length in bytes of the data buffer + public uint buf_len; + /// Registers to set in the ATA device + public AtaRegistersChs registers; + /// ATA protocol code + public byte protocol; + /// ATA transfer register indicator + public byte transferRegister; + /// Set to true to transfer blocks, false to transfer bytes + [MarshalAs(UnmanagedType.U1)] + public bool transferBlocks; + /// Spare for expansion (or alignment) + public byte spare; + /// Timeout waiting for device to respond to command + public uint timeout; +} - /// - /// Returns the response from a command sent to an ATA device using the CHS command set. This structure is - /// followed by the data buffer. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResAtaChs - { - /// Packet header - public AaruPacketHeader hdr; - /// Length in bytes of the data buffer - public uint buf_len; - /// Registers as set back by the ATA device - public AtaErrorRegistersChs registers; - /// Time in milliseconds it took for the device to execute the command - public uint duration; - /// Set to anything different of zero if the device set an error condition - public uint sense; - /// Set to the remote operating system error number - public uint error_no; - } +/// +/// Returns the response from a command sent to an ATA device using the CHS command set. This structure is +/// followed by the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResAtaChs +{ + /// Packet header + public AaruPacketHeader hdr; + /// Length in bytes of the data buffer + public uint buf_len; + /// Registers as set back by the ATA device + public AtaErrorRegistersChs registers; + /// Time in milliseconds it took for the device to execute the command + public uint duration; + /// Set to anything different of zero if the device set an error condition + public uint sense; + /// Set to the remote operating system error number + public uint error_no; +} - /// - /// Requests remote to send a command to an ATA device using the 28-bit command set. This header is followed by - /// the data buffer. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdAtaLba28 - { - /// Packet header - public AaruPacketHeader hdr; - /// Length in bytes of the data buffer - public uint buf_len; - /// Registers to set in the ATA device - public AtaRegistersLba28 registers; - /// ATA protocol code - public byte protocol; - /// ATA transfer register indicator - public byte transferRegister; - /// Set to true to transfer blocks, false to transfer bytes - [MarshalAs(UnmanagedType.U1)] - public bool transferBlocks; - /// Spare for expansion (or alignment) - public byte spare; - /// Timeout waiting for device to respond to command - public uint timeout; - } +/// +/// Requests remote to send a command to an ATA device using the 28-bit command set. This header is followed by +/// the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdAtaLba28 +{ + /// Packet header + public AaruPacketHeader hdr; + /// Length in bytes of the data buffer + public uint buf_len; + /// Registers to set in the ATA device + public AtaRegistersLba28 registers; + /// ATA protocol code + public byte protocol; + /// ATA transfer register indicator + public byte transferRegister; + /// Set to true to transfer blocks, false to transfer bytes + [MarshalAs(UnmanagedType.U1)] + public bool transferBlocks; + /// Spare for expansion (or alignment) + public byte spare; + /// Timeout waiting for device to respond to command + public uint timeout; +} - /// - /// Returns the response from a command sent to an ATA device using the 28-bit LBA command set. This structure is - /// followed by the data buffer. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResAtaLba28 - { - /// Packet header - public AaruPacketHeader hdr; - /// Length in bytes of the data buffer - public uint buf_len; - /// Registers as set back by the ATA device - public AtaErrorRegistersLba28 registers; - /// Time in milliseconds it took for the device to execute the command - public uint duration; - /// Set to anything different of zero if the device set an error condition - public uint sense; - /// Set to the remote operating system error number - public uint error_no; - } +/// +/// Returns the response from a command sent to an ATA device using the 28-bit LBA command set. This structure is +/// followed by the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResAtaLba28 +{ + /// Packet header + public AaruPacketHeader hdr; + /// Length in bytes of the data buffer + public uint buf_len; + /// Registers as set back by the ATA device + public AtaErrorRegistersLba28 registers; + /// Time in milliseconds it took for the device to execute the command + public uint duration; + /// Set to anything different of zero if the device set an error condition + public uint sense; + /// Set to the remote operating system error number + public uint error_no; +} - /// - /// Requests remote to send a command to an ATA device using the 48-bit command set. This header is followed by - /// the data buffer. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdAtaLba48 - { - /// Packet header - public AaruPacketHeader hdr; - /// Length in bytes of the data buffer - public uint buf_len; - /// Registers to set in the ATA device - public AtaRegistersLba48 registers; - /// ATA protocol code - public byte protocol; - /// ATA transfer register indicator - public byte transferRegister; - /// Set to true to transfer blocks, false to transfer bytes - [MarshalAs(UnmanagedType.U1)] - public bool transferBlocks; - /// Spare for expansion (or alignment) - public byte spare; - /// Timeout waiting for device to respond to command - public uint timeout; - } +/// +/// Requests remote to send a command to an ATA device using the 48-bit command set. This header is followed by +/// the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdAtaLba48 +{ + /// Packet header + public AaruPacketHeader hdr; + /// Length in bytes of the data buffer + public uint buf_len; + /// Registers to set in the ATA device + public AtaRegistersLba48 registers; + /// ATA protocol code + public byte protocol; + /// ATA transfer register indicator + public byte transferRegister; + /// Set to true to transfer blocks, false to transfer bytes + [MarshalAs(UnmanagedType.U1)] + public bool transferBlocks; + /// Spare for expansion (or alignment) + public byte spare; + /// Timeout waiting for device to respond to command + public uint timeout; +} - /// - /// Returns the response from a command sent to an ATA device using the 48-bit LBA command set. This structure is - /// followed by the data buffer. - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResAtaLba48 - { - /// Packet header - public AaruPacketHeader hdr; - /// Length in bytes of the data buffer - public uint buf_len; - /// Registers as set back by the ATA device - public AtaErrorRegistersLba48 registers; - /// Time in milliseconds it took for the device to execute the command - public uint duration; - /// Set to anything different of zero if the device set an error condition - public uint sense; - /// Set to the remote operating system error number - public uint error_no; - } +/// +/// Returns the response from a command sent to an ATA device using the 48-bit LBA command set. This structure is +/// followed by the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResAtaLba48 +{ + /// Packet header + public AaruPacketHeader hdr; + /// Length in bytes of the data buffer + public uint buf_len; + /// Registers as set back by the ATA device + public AtaErrorRegistersLba48 registers; + /// Time in milliseconds it took for the device to execute the command + public uint duration; + /// Set to anything different of zero if the device set an error condition + public uint sense; + /// Set to the remote operating system error number + public uint error_no; +} +/// SecureDigital or MultiMediaCard command description +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruCmdSdhci +{ + /// Command + public MmcCommands command; + /// Set to true if the command writes to the device, false otherwise + [MarshalAs(UnmanagedType.U1)] + public bool write; + /// Set to true if it is an application command, false otherwise + [MarshalAs(UnmanagedType.U1)] + public bool application; + /// Flags + public MmcFlags flags; + /// Argument + public uint argument; + /// Block size + public uint block_size; + /// Number of blocks to transfer + public uint blocks; + /// Length in bytes of the data buffer + public uint buf_len; + /// Timeout waiting for device to respond to command + public uint timeout; +} + +/// +/// Requests remote to send a command to a SecureDigital or MultiMediaCard device attached using a SDHCI +/// controller. This structure is followed by the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdSdhci +{ + /// Packet header + public AaruPacketHeader hdr; /// SecureDigital or MultiMediaCard command description - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruCmdSdhci - { - /// Command - public MmcCommands command; - /// Set to true if the command writes to the device, false otherwise - [MarshalAs(UnmanagedType.U1)] - public bool write; - /// Set to true if it is an application command, false otherwise - [MarshalAs(UnmanagedType.U1)] - public bool application; - /// Flags - public MmcFlags flags; - /// Argument - public uint argument; - /// Block size - public uint block_size; - /// Number of blocks to transfer - public uint blocks; - /// Length in bytes of the data buffer - public uint buf_len; - /// Timeout waiting for device to respond to command - public uint timeout; - } + public AaruCmdSdhci command; +} +/// +/// Returns the response from a command sent to a SecureDigital or MultiMediaCard device attached to a SDHCI +/// controller. This structure is followed by the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruResSdhci +{ + /// Length in bytes of the data buffer + public uint buf_len; + + /// Response registers + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public uint[] response; + + /// Time in milliseconds it took for the device to execute the command + public uint duration; + /// Set to anything different of zero if the device set an error condition + public uint sense; + /// Set to the remote operating system error number + public uint error_no; +} + +/// +/// Returns the response from a command sent to a SecureDigital or MultiMediaCard device attached to a SDHCI +/// controller. This structure is followed by the data buffer. +/// +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResSdhci +{ + /// Packet header + public AaruPacketHeader hdr; + /// Response + public AaruResSdhci res; +} + +/// Requests the Aaru device type for the opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdGetDeviceType +{ + /// Packet header + public AaruPacketHeader hdr; +} + +/// Returns the Aaru device type for the opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResGetDeviceType +{ + /// Packet header + public AaruPacketHeader hdr; + /// Aaru's device type + public DeviceType device_type; +} + +/// Requests the registers of a SecureDigital or MultiMediaCard attached to an SDHCI controller +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdGetSdhciRegisters +{ + /// Packet header + public AaruPacketHeader hdr; +} + +/// Returns the registers of a SecureDigital or MultiMediaCard attached to an SDHCI controller +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResGetSdhciRegisters +{ + /// Packet header + public AaruPacketHeader hdr; /// - /// Requests remote to send a command to a SecureDigital or MultiMediaCard device attached using a SDHCI - /// controller. This structure is followed by the data buffer. + /// true if the device is attached to an SDHCI controller and the rest of the fields on this packet are + /// valid, false otherwise /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdSdhci - { - /// Packet header - public AaruPacketHeader hdr; - /// SecureDigital or MultiMediaCard command description - public AaruCmdSdhci command; - } + [MarshalAs(UnmanagedType.U1)] + public bool isSdhci; + /// CSD registers + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] csd; + /// CID registers + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] cid; + /// OCR registers + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public byte[] ocr; + /// SCR registers + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] scr; + /// Length of the CSD registers + public uint csd_len; + /// Length of the CID registers + public uint cid_len; + /// Length of the OCR registers + public uint ocr_len; + /// Length of the SCR registers + public uint scr_len; +} +/// Requests information about the USB connection of the opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdGetUsbData +{ + /// Packet header + public AaruPacketHeader hdr; +} + +/// Returns information about the USB connection of the opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResGetUsbData +{ + /// Packet header + public AaruPacketHeader hdr; /// - /// Returns the response from a command sent to a SecureDigital or MultiMediaCard device attached to a SDHCI - /// controller. This structure is followed by the data buffer. + /// true if the device is attached using USB and the rest of the fields on this packet are valid, + /// false otherwise /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruResSdhci - { - /// Length in bytes of the data buffer - public uint buf_len; + [MarshalAs(UnmanagedType.U1)] + public bool isUsb; + /// Length of the descriptors + public ushort descLen; + /// Raw USB descriptors + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 65536)] + public byte[] descriptors; + /// USB vendor ID + public ushort idVendor; + /// USB product ID + public ushort idProduct; + /// USB manufacturer string + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string manufacturer; + /// USB product string + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string product; + /// USB serial number string + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string serial; +} - /// Response registers - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public uint[] response; - - /// Time in milliseconds it took for the device to execute the command - public uint duration; - /// Set to anything different of zero if the device set an error condition - public uint sense; - /// Set to the remote operating system error number - public uint error_no; - } +/// Requests information about the FireWire connection of the opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdGetFireWireData +{ + /// Packet header + public AaruPacketHeader hdr; +} +/// Returns information about the FireWire connection of the opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResGetFireWireData +{ + /// Packet header + public AaruPacketHeader hdr; /// - /// Returns the response from a command sent to a SecureDigital or MultiMediaCard device attached to a SDHCI - /// controller. This structure is followed by the data buffer. + /// true if the device is attached using FireWire and the rest of the fields on this packet are valid, + /// false otherwise /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResSdhci - { - /// Packet header - public AaruPacketHeader hdr; - /// Response - public AaruResSdhci res; - } + [MarshalAs(UnmanagedType.U1)] + public bool isFireWire; + /// FireWire model ID + public uint idModel; + /// FireWire vendor ID + public uint idVendor; + /// FireWire's device GUID + public ulong guid; + /// FireWire vendor string + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string vendor; + /// FireWire model string + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] + public string model; +} - /// Requests the Aaru device type for the opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdGetDeviceType - { - /// Packet header - public AaruPacketHeader hdr; - } +/// Requests information about the PCMCIA or CardBus connection of the opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdGetPcmciaData +{ + /// Packet header + public AaruPacketHeader hdr; +} - /// Returns the Aaru device type for the opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResGetDeviceType - { - /// Packet header - public AaruPacketHeader hdr; - /// Aaru's device type - public DeviceType device_type; - } +/// Returns information about the PCMCIA or CardBus connection of the opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResGetPcmciaData +{ + /// Packet header + public AaruPacketHeader hdr; + /// + /// true if the device is a PCMCIA or CardBus device and the rest of the fields on this packet are valid, + /// false otherwise + /// + [MarshalAs(UnmanagedType.U1)] + public bool isPcmcia; + /// CIS buffer length + public ushort cis_len; + /// CIS buffer + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 65536)] + public byte[] cis; +} - /// Requests the registers of a SecureDigital or MultiMediaCard attached to an SDHCI controller - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdGetSdhciRegisters - { - /// Packet header - public AaruPacketHeader hdr; - } +/// Requests to close the currently opened device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdClose +{ + /// Packet header + public AaruPacketHeader hdr; +} - /// Returns the registers of a SecureDigital or MultiMediaCard attached to an SDHCI controller - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResGetSdhciRegisters - { - /// Packet header - public AaruPacketHeader hdr; - /// - /// true if the device is attached to an SDHCI controller and the rest of the fields on this packet are - /// valid, false otherwise - /// - [MarshalAs(UnmanagedType.U1)] - public bool isSdhci; - /// CSD registers - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public byte[] csd; - /// CID registers - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public byte[] cid; - /// OCR registers - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public byte[] ocr; - /// SCR registers - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public byte[] scr; - /// Length of the CSD registers - public uint csd_len; - /// Length of the CID registers - public uint cid_len; - /// Length of the OCR registers - public uint ocr_len; - /// Length of the SCR registers - public uint scr_len; - } +/// Requests to know if the remote is running with administrative (aka root) privileges +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdAmIRoot +{ + /// Packet header + public AaruPacketHeader hdr; +} - /// Requests information about the USB connection of the opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdGetUsbData - { - /// Packet header - public AaruPacketHeader hdr; - } +/// Returns if the remote is running with administrative (aka root) privileges +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResAmIRoot +{ + /// Packet header + public AaruPacketHeader hdr; + /// Set to any value different of 0 to indicate the remote is running with administrative (aka root) privileges + public uint am_i_root; +} - /// Returns information about the USB connection of the opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResGetUsbData - { - /// Packet header - public AaruPacketHeader hdr; - /// - /// true if the device is attached using USB and the rest of the fields on this packet are valid, - /// false otherwise - /// - [MarshalAs(UnmanagedType.U1)] - public bool isUsb; - /// Length of the descriptors - public ushort descLen; - /// Raw USB descriptors - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 65536)] - public byte[] descriptors; - /// USB vendor ID - public ushort idVendor; - /// USB product ID - public ushort idProduct; - /// USB manufacturer string - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string manufacturer; - /// USB product string - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string product; - /// USB serial number string - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string serial; - } +/// Initiates a multiple command block with the SDHCI controller the SecureDigital or MultiMediaCard is attached +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketMultiCmdSdhci +{ + /// Packet header + public AaruPacketHeader hdr; + /// How many commands to queue + public ulong cmd_count; +} - /// Requests information about the FireWire connection of the opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdGetFireWireData - { - /// Packet header - public AaruPacketHeader hdr; - } +/// Closes and then re-opens the same device +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdReOpen +{ + /// Packet header + public AaruPacketHeader hdr; +} - /// Returns information about the FireWire connection of the opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResGetFireWireData - { - /// Packet header - public AaruPacketHeader hdr; - /// - /// true if the device is attached using FireWire and the rest of the fields on this packet are valid, - /// false otherwise - /// - [MarshalAs(UnmanagedType.U1)] - public bool isFireWire; - /// FireWire model ID - public uint idModel; - /// FireWire vendor ID - public uint idVendor; - /// FireWire's device GUID - public ulong guid; - /// FireWire vendor string - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string vendor; - /// FireWire model string - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)] - public string model; - } +/// Reads data using operating system buffers +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketCmdOsRead +{ + /// Packet header + public AaruPacketHeader hdr; + /// Device offset where to read + public ulong offset; + /// Number of bytes to read + public uint length; +} - /// Requests information about the PCMCIA or CardBus connection of the opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdGetPcmciaData - { - /// Packet header - public AaruPacketHeader hdr; - } - - /// Returns information about the PCMCIA or CardBus connection of the opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResGetPcmciaData - { - /// Packet header - public AaruPacketHeader hdr; - /// - /// true if the device is a PCMCIA or CardBus device and the rest of the fields on this packet are valid, - /// false otherwise - /// - [MarshalAs(UnmanagedType.U1)] - public bool isPcmcia; - /// CIS buffer length - public ushort cis_len; - /// CIS buffer - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 65536)] - public byte[] cis; - } - - /// Requests to close the currently opened device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdClose - { - /// Packet header - public AaruPacketHeader hdr; - } - - /// Requests to know if the remote is running with administrative (aka root) privileges - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdAmIRoot - { - /// Packet header - public AaruPacketHeader hdr; - } - - /// Returns if the remote is running with administrative (aka root) privileges - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResAmIRoot - { - /// Packet header - public AaruPacketHeader hdr; - /// Set to any value different of 0 to indicate the remote is running with administrative (aka root) privileges - public uint am_i_root; - } - - /// Initiates a multiple command block with the SDHCI controller the SecureDigital or MultiMediaCard is attached - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketMultiCmdSdhci - { - /// Packet header - public AaruPacketHeader hdr; - /// How many commands to queue - public ulong cmd_count; - } - - /// Closes and then re-opens the same device - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdReOpen - { - /// Packet header - public AaruPacketHeader hdr; - } - - /// Reads data using operating system buffers - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketCmdOsRead - { - /// Packet header - public AaruPacketHeader hdr; - /// Device offset where to read - public ulong offset; - /// Number of bytes to read - public uint length; - } - - /// Returns data read using operating system buffers. This structure is followed by the data buffer. - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] - public struct AaruPacketResOsRead - { - /// Packet header - public AaruPacketHeader hdr; - /// Set to the remote operating system error number - public int errno; - /// Time in milliseconds it took for the device to execute the command - public uint duration; - } +/// Returns data read using operating system buffers. This structure is followed by the data buffer. +[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)] +public struct AaruPacketResOsRead +{ + /// Packet header + public AaruPacketHeader hdr; + /// Set to the remote operating system error number + public int errno; + /// Time in milliseconds it took for the device to execute the command + public uint duration; } \ No newline at end of file diff --git a/Aaru.Devices/Windows/Command.cs b/Aaru.Devices/Windows/Command.cs index 9c1e395ba..8ffe796b0 100644 --- a/Aaru.Devices/Windows/Command.cs +++ b/Aaru.Devices/Windows/Command.cs @@ -37,594 +37,593 @@ using System.Runtime.InteropServices; using Aaru.Decoders.ATA; using Microsoft.Win32.SafeHandles; -namespace Aaru.Devices.Windows +namespace Aaru.Devices.Windows; + +[SuppressMessage("ReSharper", "UnusedParameter.Global")] +internal static class Command { - [SuppressMessage("ReSharper", "UnusedParameter.Global")] - internal static class Command + /// Sends a SCSI command + /// 0 if no error occurred, otherwise, errno + /// File handle + /// SCSI CDB + /// Buffer for SCSI command response + /// Buffer with the SCSI sense + /// Timeout in seconds + /// SCSI command transfer direction + /// Time it took to execute the command in milliseconds + /// + /// True if SCSI error returned non-OK status and contains SCSI + /// sense + /// + internal static int SendScsiCommand(SafeFileHandle fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, + uint timeout, ScsiIoctlDirection direction, out double duration, + out bool sense) { - /// Sends a SCSI command - /// 0 if no error occurred, otherwise, errno - /// File handle - /// SCSI CDB - /// Buffer for SCSI command response - /// Buffer with the SCSI sense - /// Timeout in seconds - /// SCSI command transfer direction - /// Time it took to execute the command in milliseconds - /// - /// True if SCSI error returned non-OK status and contains SCSI - /// sense - /// - internal static int SendScsiCommand(SafeFileHandle fd, byte[] cdb, ref byte[] buffer, out byte[] senseBuffer, - uint timeout, ScsiIoctlDirection direction, out double duration, - out bool sense) + senseBuffer = null; + duration = 0; + sense = false; + + if(buffer == null) + return -1; + + var sptdSb = new ScsiPassThroughDirectAndSenseBuffer { - senseBuffer = null; - duration = 0; - sense = false; - - if(buffer == null) - return -1; - - var sptdSb = new ScsiPassThroughDirectAndSenseBuffer + SenseBuf = new byte[32], + sptd = new ScsiPassThroughDirect { - SenseBuf = new byte[32], - sptd = new ScsiPassThroughDirect - { - Cdb = new byte[16], - CdbLength = (byte)cdb.Length, - SenseInfoLength = 32, - DataIn = direction, - DataTransferLength = (uint)buffer.Length, - TimeOutValue = timeout, - DataBuffer = Marshal.AllocHGlobal(buffer.Length) - } - }; - - sptdSb.sptd.Length = (ushort)Marshal.SizeOf(sptdSb.sptd); - sptdSb.sptd.SenseInfoOffset = (uint)Marshal.SizeOf(sptdSb.sptd); - Array.Copy(cdb, sptdSb.sptd.Cdb, cdb.Length); - - uint k = 0; - int error = 0; - - Marshal.Copy(buffer, 0, sptdSb.sptd.DataBuffer, buffer.Length); - - DateTime start = DateTime.Now; - - bool hasError = !Extern.DeviceIoControlScsi(fd, WindowsIoctl.IoctlScsiPassThroughDirect, ref sptdSb, - (uint)Marshal.SizeOf(sptdSb), ref sptdSb, - (uint)Marshal.SizeOf(sptdSb), ref k, IntPtr.Zero); - - DateTime end = DateTime.Now; - - if(hasError) - error = Marshal.GetLastWin32Error(); - - Marshal.Copy(sptdSb.sptd.DataBuffer, buffer, 0, buffer.Length); - - sense |= sptdSb.sptd.ScsiStatus != 0; - - senseBuffer = new byte[64]; - Array.Copy(sptdSb.SenseBuf, senseBuffer, 32); - - duration = (end - start).TotalMilliseconds; - - Marshal.FreeHGlobal(sptdSb.sptd.DataBuffer); - - return error; - } - - /// Sends an ATA command in CHS mode - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA error returned non-OK status - /// Registers to send to drive - /// Registers returned by drive - /// ATA protocol to use - internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersChs registers, - out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol, - ref byte[] buffer, uint timeout, out double duration, out bool sense) - { - duration = 0; - sense = false; - errorRegisters = new AtaErrorRegistersChs(); - - if(buffer == null) - return -1; - - var aptd = new AtaPassThroughDirect - { - TimeOutValue = timeout, - DataBuffer = Marshal.AllocHGlobal(buffer.Length), - Length = (ushort)Marshal.SizeOf(typeof(AtaPassThroughDirect)), + Cdb = new byte[16], + CdbLength = (byte)cdb.Length, + SenseInfoLength = 32, + DataIn = direction, DataTransferLength = (uint)buffer.Length, - PreviousTaskFile = new AtaTaskFile(), - CurrentTaskFile = new AtaTaskFile - { - Command = registers.Command, - CylinderHigh = registers.CylinderHigh, - CylinderLow = registers.CylinderLow, - DeviceHead = registers.DeviceHead, - Features = registers.Feature, - SectorCount = registers.SectorCount, - SectorNumber = registers.Sector - } - }; - - switch(protocol) - { - case AtaProtocol.PioIn: - case AtaProtocol.UDmaIn: - case AtaProtocol.Dma: - aptd.AtaFlags = AtaFlags.DataIn; - - break; - case AtaProtocol.PioOut: - case AtaProtocol.UDmaOut: - aptd.AtaFlags = AtaFlags.DataOut; - - break; - } - - switch(protocol) - { - case AtaProtocol.Dma: - case AtaProtocol.DmaQueued: - case AtaProtocol.FpDma: - case AtaProtocol.UDmaIn: - case AtaProtocol.UDmaOut: - aptd.AtaFlags |= AtaFlags.Dma; - - break; - } - - // Unknown if needed - aptd.AtaFlags |= AtaFlags.DrdyRequired; - - uint k = 0; - int error = 0; - - Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); - - DateTime start = DateTime.Now; - - sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, - (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, - IntPtr.Zero); - - DateTime end = DateTime.Now; - - if(sense) - error = Marshal.GetLastWin32Error(); - - Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); - - duration = (end - start).TotalMilliseconds; - - errorRegisters.CylinderHigh = aptd.CurrentTaskFile.CylinderHigh; - errorRegisters.CylinderLow = aptd.CurrentTaskFile.CylinderLow; - errorRegisters.DeviceHead = aptd.CurrentTaskFile.DeviceHead; - errorRegisters.Error = aptd.CurrentTaskFile.Error; - errorRegisters.Sector = aptd.CurrentTaskFile.SectorNumber; - errorRegisters.SectorCount = aptd.CurrentTaskFile.SectorCount; - errorRegisters.Status = aptd.CurrentTaskFile.Status; - - sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; - - Marshal.FreeHGlobal(aptd.DataBuffer); - - return error; - } - - /// Sends an ATA command in 28-bit LBA mode - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA error returned non-OK status - /// Registers to send to drive - /// Registers returned by drive - /// ATA protocol to use - internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersLba28 registers, - out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, - ref byte[] buffer, uint timeout, out double duration, out bool sense) - { - duration = 0; - sense = false; - errorRegisters = new AtaErrorRegistersLba28(); - - if(buffer == null) - return -1; - - var aptd = new AtaPassThroughDirect - { TimeOutValue = timeout, - DataBuffer = Marshal.AllocHGlobal(buffer.Length), - Length = (ushort)Marshal.SizeOf(typeof(AtaPassThroughDirect)), - DataTransferLength = (uint)buffer.Length, - PreviousTaskFile = new AtaTaskFile(), - CurrentTaskFile = new AtaTaskFile - { - Command = registers.Command, - CylinderHigh = registers.LbaHigh, - CylinderLow = registers.LbaMid, - DeviceHead = registers.DeviceHead, - Features = registers.Feature, - SectorCount = registers.SectorCount, - SectorNumber = registers.LbaLow - } - }; - - switch(protocol) - { - case AtaProtocol.PioIn: - case AtaProtocol.UDmaIn: - case AtaProtocol.Dma: - aptd.AtaFlags = AtaFlags.DataIn; - - break; - case AtaProtocol.PioOut: - case AtaProtocol.UDmaOut: - aptd.AtaFlags = AtaFlags.DataOut; - - break; + DataBuffer = Marshal.AllocHGlobal(buffer.Length) } + }; - switch(protocol) + sptdSb.sptd.Length = (ushort)Marshal.SizeOf(sptdSb.sptd); + sptdSb.sptd.SenseInfoOffset = (uint)Marshal.SizeOf(sptdSb.sptd); + Array.Copy(cdb, sptdSb.sptd.Cdb, cdb.Length); + + uint k = 0; + int error = 0; + + Marshal.Copy(buffer, 0, sptdSb.sptd.DataBuffer, buffer.Length); + + DateTime start = DateTime.Now; + + bool hasError = !Extern.DeviceIoControlScsi(fd, WindowsIoctl.IoctlScsiPassThroughDirect, ref sptdSb, + (uint)Marshal.SizeOf(sptdSb), ref sptdSb, + (uint)Marshal.SizeOf(sptdSb), ref k, IntPtr.Zero); + + DateTime end = DateTime.Now; + + if(hasError) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(sptdSb.sptd.DataBuffer, buffer, 0, buffer.Length); + + sense |= sptdSb.sptd.ScsiStatus != 0; + + senseBuffer = new byte[64]; + Array.Copy(sptdSb.SenseBuf, senseBuffer, 32); + + duration = (end - start).TotalMilliseconds; + + Marshal.FreeHGlobal(sptdSb.sptd.DataBuffer); + + return error; + } + + /// Sends an ATA command in CHS mode + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA error returned non-OK status + /// Registers to send to drive + /// Registers returned by drive + /// ATA protocol to use + internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersChs registers, + out AtaErrorRegistersChs errorRegisters, AtaProtocol protocol, + ref byte[] buffer, uint timeout, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new AtaErrorRegistersChs(); + + if(buffer == null) + return -1; + + var aptd = new AtaPassThroughDirect + { + TimeOutValue = timeout, + DataBuffer = Marshal.AllocHGlobal(buffer.Length), + Length = (ushort)Marshal.SizeOf(typeof(AtaPassThroughDirect)), + DataTransferLength = (uint)buffer.Length, + PreviousTaskFile = new AtaTaskFile(), + CurrentTaskFile = new AtaTaskFile { - case AtaProtocol.Dma: - case AtaProtocol.DmaQueued: - case AtaProtocol.FpDma: - case AtaProtocol.UDmaIn: - case AtaProtocol.UDmaOut: - aptd.AtaFlags |= AtaFlags.Dma; - - break; + Command = registers.Command, + CylinderHigh = registers.CylinderHigh, + CylinderLow = registers.CylinderLow, + DeviceHead = registers.DeviceHead, + Features = registers.Feature, + SectorCount = registers.SectorCount, + SectorNumber = registers.Sector } + }; - // Unknown if needed - aptd.AtaFlags |= AtaFlags.DrdyRequired; + switch(protocol) + { + case AtaProtocol.PioIn: + case AtaProtocol.UDmaIn: + case AtaProtocol.Dma: + aptd.AtaFlags = AtaFlags.DataIn; - uint k = 0; - int error = 0; + break; + case AtaProtocol.PioOut: + case AtaProtocol.UDmaOut: + aptd.AtaFlags = AtaFlags.DataOut; - Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); + break; + } - DateTime start = DateTime.Now; + switch(protocol) + { + case AtaProtocol.Dma: + case AtaProtocol.DmaQueued: + case AtaProtocol.FpDma: + case AtaProtocol.UDmaIn: + case AtaProtocol.UDmaOut: + aptd.AtaFlags |= AtaFlags.Dma; - sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, - (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, - IntPtr.Zero); + break; + } - DateTime end = DateTime.Now; + // Unknown if needed + aptd.AtaFlags |= AtaFlags.DrdyRequired; - if(sense) - error = Marshal.GetLastWin32Error(); + uint k = 0; + int error = 0; - Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); + Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); + DateTime start = DateTime.Now; + + sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, + (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, + IntPtr.Zero); + + DateTime end = DateTime.Now; + + if(sense) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); + + duration = (end - start).TotalMilliseconds; + + errorRegisters.CylinderHigh = aptd.CurrentTaskFile.CylinderHigh; + errorRegisters.CylinderLow = aptd.CurrentTaskFile.CylinderLow; + errorRegisters.DeviceHead = aptd.CurrentTaskFile.DeviceHead; + errorRegisters.Error = aptd.CurrentTaskFile.Error; + errorRegisters.Sector = aptd.CurrentTaskFile.SectorNumber; + errorRegisters.SectorCount = aptd.CurrentTaskFile.SectorCount; + errorRegisters.Status = aptd.CurrentTaskFile.Status; + + sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; + + Marshal.FreeHGlobal(aptd.DataBuffer); + + return error; + } + + /// Sends an ATA command in 28-bit LBA mode + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA error returned non-OK status + /// Registers to send to drive + /// Registers returned by drive + /// ATA protocol to use + internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersLba28 registers, + out AtaErrorRegistersLba28 errorRegisters, AtaProtocol protocol, + ref byte[] buffer, uint timeout, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new AtaErrorRegistersLba28(); + + if(buffer == null) + return -1; + + var aptd = new AtaPassThroughDirect + { + TimeOutValue = timeout, + DataBuffer = Marshal.AllocHGlobal(buffer.Length), + Length = (ushort)Marshal.SizeOf(typeof(AtaPassThroughDirect)), + DataTransferLength = (uint)buffer.Length, + PreviousTaskFile = new AtaTaskFile(), + CurrentTaskFile = new AtaTaskFile + { + Command = registers.Command, + CylinderHigh = registers.LbaHigh, + CylinderLow = registers.LbaMid, + DeviceHead = registers.DeviceHead, + Features = registers.Feature, + SectorCount = registers.SectorCount, + SectorNumber = registers.LbaLow + } + }; + + switch(protocol) + { + case AtaProtocol.PioIn: + case AtaProtocol.UDmaIn: + case AtaProtocol.Dma: + aptd.AtaFlags = AtaFlags.DataIn; + + break; + case AtaProtocol.PioOut: + case AtaProtocol.UDmaOut: + aptd.AtaFlags = AtaFlags.DataOut; + + break; + } + + switch(protocol) + { + case AtaProtocol.Dma: + case AtaProtocol.DmaQueued: + case AtaProtocol.FpDma: + case AtaProtocol.UDmaIn: + case AtaProtocol.UDmaOut: + aptd.AtaFlags |= AtaFlags.Dma; + + break; + } + + // Unknown if needed + aptd.AtaFlags |= AtaFlags.DrdyRequired; + + uint k = 0; + int error = 0; + + Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); + + DateTime start = DateTime.Now; + + sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, + (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, + IntPtr.Zero); + + DateTime end = DateTime.Now; + + if(sense) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); + + duration = (end - start).TotalMilliseconds; + + errorRegisters.LbaHigh = aptd.CurrentTaskFile.CylinderHigh; + errorRegisters.LbaMid = aptd.CurrentTaskFile.CylinderLow; + errorRegisters.DeviceHead = aptd.CurrentTaskFile.DeviceHead; + errorRegisters.Error = aptd.CurrentTaskFile.Error; + errorRegisters.LbaLow = aptd.CurrentTaskFile.SectorNumber; + errorRegisters.SectorCount = aptd.CurrentTaskFile.SectorCount; + errorRegisters.Status = aptd.CurrentTaskFile.Status; + + sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; + + Marshal.FreeHGlobal(aptd.DataBuffer); + + return error; + } + + /// Sends an ATA command in 48-bit LBA mode + /// 0 if no error occurred, otherwise, errno + /// File handle + /// Buffer for SCSI command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if ATA error returned non-OK status + /// Registers to send to drive + /// Registers returned by drive + /// ATA protocol to use + internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersLba48 registers, + out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, + ref byte[] buffer, uint timeout, out double duration, out bool sense) + { + duration = 0; + sense = false; + errorRegisters = new AtaErrorRegistersLba48(); + + if(buffer == null) + return -1; + + var aptd = new AtaPassThroughDirect + { + TimeOutValue = timeout, + DataBuffer = Marshal.AllocHGlobal(buffer.Length), + Length = (ushort)Marshal.SizeOf(typeof(AtaPassThroughDirect)), + DataTransferLength = (uint)buffer.Length, + PreviousTaskFile = new AtaTaskFile + { + CylinderHigh = registers.LbaHighPrevious, + CylinderLow = registers.LbaMidPrevious, + Features = (byte)((registers.Feature & 0xFF00) >> 8), + SectorCount = (byte)((registers.SectorCount & 0xFF00) >> 8), + SectorNumber = registers.LbaLowPrevious + }, + CurrentTaskFile = new AtaTaskFile + { + Command = registers.Command, + CylinderHigh = registers.LbaHighCurrent, + CylinderLow = registers.LbaMidCurrent, + DeviceHead = registers.DeviceHead, + Features = (byte)(registers.Feature & 0xFF), + SectorCount = (byte)(registers.SectorCount & 0xFF), + SectorNumber = registers.LbaLowCurrent + } + }; + + switch(protocol) + { + case AtaProtocol.PioIn: + case AtaProtocol.UDmaIn: + case AtaProtocol.Dma: + aptd.AtaFlags = AtaFlags.DataIn; + + break; + case AtaProtocol.PioOut: + case AtaProtocol.UDmaOut: + aptd.AtaFlags = AtaFlags.DataOut; + + break; + } + + switch(protocol) + { + case AtaProtocol.Dma: + case AtaProtocol.DmaQueued: + case AtaProtocol.FpDma: + case AtaProtocol.UDmaIn: + case AtaProtocol.UDmaOut: + aptd.AtaFlags |= AtaFlags.Dma; + + break; + } + + aptd.AtaFlags |= AtaFlags.ExtendedCommand; + + // Unknown if needed + aptd.AtaFlags |= AtaFlags.DrdyRequired; + + uint k = 0; + int error = 0; + + Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); + + DateTime start = DateTime.Now; + + sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, + (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, + IntPtr.Zero); + + DateTime end = DateTime.Now; + + if(sense) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); + + duration = (end - start).TotalMilliseconds; + + errorRegisters.SectorCount = (ushort)((aptd.PreviousTaskFile.SectorCount << 8) + + aptd.CurrentTaskFile.SectorCount); + + errorRegisters.LbaLowPrevious = aptd.PreviousTaskFile.SectorNumber; + errorRegisters.LbaMidPrevious = aptd.PreviousTaskFile.CylinderLow; + errorRegisters.LbaHighPrevious = aptd.PreviousTaskFile.CylinderHigh; + errorRegisters.LbaLowCurrent = aptd.CurrentTaskFile.SectorNumber; + errorRegisters.LbaMidCurrent = aptd.CurrentTaskFile.CylinderLow; + errorRegisters.LbaHighCurrent = aptd.CurrentTaskFile.CylinderHigh; + errorRegisters.DeviceHead = aptd.CurrentTaskFile.DeviceHead; + errorRegisters.Error = aptd.CurrentTaskFile.Error; + errorRegisters.Status = aptd.CurrentTaskFile.Status; + + sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; + + Marshal.FreeHGlobal(aptd.DataBuffer); + + return error; + } + + /// Returns true if the specified handle is controlled by a SFFDISK (aka SDHCI) driver + /// Device handle + /// true if SDHCI, false otherwise + internal static bool IsSdhci(SafeFileHandle fd) + { + var queryData1 = new SffdiskQueryDeviceProtocolData(); + queryData1.size = (ushort)Marshal.SizeOf(queryData1); + + Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskQueryDeviceProtocol, IntPtr.Zero, 0, ref queryData1, + queryData1.size, out _, IntPtr.Zero); + + return queryData1.protocolGuid.Equals(Consts.GuidSffProtocolSd) || + queryData1.protocolGuid.Equals(Consts.GuidSffProtocolMmc); + } + + /// Sends a MMC/SD command + /// The result of the command. + /// File handle + /// MMC/SD opcode + /// Buffer for MMC/SD command response + /// Timeout in seconds + /// Time it took to execute the command in milliseconds + /// True if MMC/SD returned non-OK status + /// True if data is sent from host to card + /// True if command should be preceded with CMD55 + /// Flags indicating kind and place of response + /// How many blocks to transfer + /// Command argument + /// Response registers + /// Size of block in bytes + internal static int SendMmcCommand(SafeFileHandle fd, MmcCommands command, bool write, bool isApplication, + MmcFlags flags, uint argument, uint blockSize, uint blocks, + ref byte[] buffer, out uint[] response, out double duration, out bool sense, + uint timeout = 0) + { + var commandData = new SffdiskDeviceCommandData(); + var commandDescriptor = new SdCmdDescriptor(); + commandData.size = (ushort)Marshal.SizeOf(commandData); + commandData.command = SffdiskDcmd.DeviceCommand; + commandData.protocolArgumentSize = (ushort)Marshal.SizeOf(commandDescriptor); + commandData.deviceDataBufferSize = blockSize * blocks; + commandDescriptor.commandCode = (byte)command; + commandDescriptor.cmdClass = isApplication ? SdCommandClass.AppCmd : SdCommandClass.Standard; + commandDescriptor.transferDirection = write ? SdTransferDirection.Write : SdTransferDirection.Read; + + commandDescriptor.transferType = flags.HasFlag(MmcFlags.CommandAdtc) + ? command == MmcCommands.ReadMultipleBlock + ? SdTransferType.MultiBlock + : SdTransferType.SingleBlock : SdTransferType.CmdOnly; + + commandDescriptor.responseType = 0; + + if(flags.HasFlag(MmcFlags.ResponseR1) || + flags.HasFlag(MmcFlags.ResponseSpiR1)) + commandDescriptor.responseType = SdResponseType.R1; + + if(flags.HasFlag(MmcFlags.ResponseR1B) || + flags.HasFlag(MmcFlags.ResponseSpiR1B)) + commandDescriptor.responseType = SdResponseType.R1b; + + if(flags.HasFlag(MmcFlags.ResponseR2) || + flags.HasFlag(MmcFlags.ResponseSpiR2)) + commandDescriptor.responseType = SdResponseType.R2; + + if(flags.HasFlag(MmcFlags.ResponseR3) || + flags.HasFlag(MmcFlags.ResponseSpiR3)) + commandDescriptor.responseType = SdResponseType.R3; + + if(flags.HasFlag(MmcFlags.ResponseR4) || + flags.HasFlag(MmcFlags.ResponseSpiR4)) + commandDescriptor.responseType = SdResponseType.R4; + + if(flags.HasFlag(MmcFlags.ResponseR5) || + flags.HasFlag(MmcFlags.ResponseSpiR5)) + commandDescriptor.responseType = SdResponseType.R5; + + if(flags.HasFlag(MmcFlags.ResponseR6)) + commandDescriptor.responseType = SdResponseType.R6; + + byte[] commandB = new byte[commandData.size + commandData.protocolArgumentSize + + commandData.deviceDataBufferSize]; + + Array.Copy(buffer, 0, commandB, commandData.size + commandData.protocolArgumentSize, buffer.Length); + IntPtr hBuf = Marshal.AllocHGlobal(commandB.Length); + Marshal.StructureToPtr(commandData, hBuf, true); + var descriptorOffset = IntPtr.Add(hBuf, commandData.size); + Marshal.StructureToPtr(commandDescriptor, descriptorOffset, true); + Marshal.Copy(hBuf, commandB, 0, commandB.Length); + Marshal.FreeHGlobal(hBuf); + + int error = 0; + DateTime start = DateTime.Now; + + sense = !Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskDeviceCommand, commandB, (uint)commandB.Length, + commandB, (uint)commandB.Length, out _, IntPtr.Zero); + + DateTime end = DateTime.Now; + + if(sense) + error = Marshal.GetLastWin32Error(); + + buffer = new byte[blockSize * blocks]; + Buffer.BlockCopy(commandB, commandB.Length - buffer.Length, buffer, 0, buffer.Length); + + response = new uint[4]; + duration = (end - start).TotalMilliseconds; + + return error; + } + + internal static int SendMultipleMmcCommands(SafeFileHandle fd, Device.MmcSingleCommand[] commands, + out double duration, out bool sense, uint timeout = 0) + { + int error = 0; + duration = 0; + sense = false; + + if(commands.Length == 3 && + commands[0].command == MmcCommands.SetBlocklen && + commands[1].command == MmcCommands.ReadMultipleBlock && + commands[2].command == MmcCommands.StopTransmission) + return SendMmcCommand(fd, commands[1].command, commands[1].write, commands[1].isApplication, + commands[1].flags, commands[1].argument, commands[1].blockSize, + commands[1].blocks, ref commands[1].buffer, out commands[1].response, + out duration, out sense, timeout); + + foreach(Device.MmcSingleCommand command in commands) + { + int singleError = SendMmcCommand(fd, command.command, command.write, command.isApplication, + command.flags, command.argument, command.blockSize, command.blocks, + ref command.buffer, out command.response, out double cmdDuration, + out bool cmdSense, timeout); + + if(error == 0 && + singleError != 0) + error = singleError; + + duration += cmdDuration; + + if(cmdSense) + sense = true; + } + + return error; + } + + internal static int ReOpen(string devicePath, SafeFileHandle fd, out object newFd) + { + Extern.CloseHandle(fd); + + newFd = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, + FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, + FileAttributes.Normal, IntPtr.Zero); + + return ((SafeFileHandle)newFd).IsInvalid ? Marshal.GetLastWin32Error() : 0; + } + + internal static int BufferedOsRead(SafeFileHandle fd, out byte[] buffer, long offset, uint length, + out double duration) + { + buffer = new byte[length]; + + DateTime start = DateTime.Now; + + bool sense = !Extern.SetFilePointerEx(fd, offset, out _, MoveMethod.Begin); + + DateTime end = DateTime.Now; + + if(sense) + { duration = (end - start).TotalMilliseconds; - errorRegisters.LbaHigh = aptd.CurrentTaskFile.CylinderHigh; - errorRegisters.LbaMid = aptd.CurrentTaskFile.CylinderLow; - errorRegisters.DeviceHead = aptd.CurrentTaskFile.DeviceHead; - errorRegisters.Error = aptd.CurrentTaskFile.Error; - errorRegisters.LbaLow = aptd.CurrentTaskFile.SectorNumber; - errorRegisters.SectorCount = aptd.CurrentTaskFile.SectorCount; - errorRegisters.Status = aptd.CurrentTaskFile.Status; - - sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; - - Marshal.FreeHGlobal(aptd.DataBuffer); - - return error; + return Marshal.GetLastWin32Error(); } - /// Sends an ATA command in 48-bit LBA mode - /// 0 if no error occurred, otherwise, errno - /// File handle - /// Buffer for SCSI command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if ATA error returned non-OK status - /// Registers to send to drive - /// Registers returned by drive - /// ATA protocol to use - internal static int SendAtaCommand(SafeFileHandle fd, AtaRegistersLba48 registers, - out AtaErrorRegistersLba48 errorRegisters, AtaProtocol protocol, - ref byte[] buffer, uint timeout, out double duration, out bool sense) - { - duration = 0; - sense = false; - errorRegisters = new AtaErrorRegistersLba48(); + sense = !Extern.ReadFile(fd, buffer, length, out _, IntPtr.Zero); - if(buffer == null) - return -1; + end = DateTime.Now; + duration = (end - start).TotalMilliseconds; - var aptd = new AtaPassThroughDirect - { - TimeOutValue = timeout, - DataBuffer = Marshal.AllocHGlobal(buffer.Length), - Length = (ushort)Marshal.SizeOf(typeof(AtaPassThroughDirect)), - DataTransferLength = (uint)buffer.Length, - PreviousTaskFile = new AtaTaskFile - { - CylinderHigh = registers.LbaHighPrevious, - CylinderLow = registers.LbaMidPrevious, - Features = (byte)((registers.Feature & 0xFF00) >> 8), - SectorCount = (byte)((registers.SectorCount & 0xFF00) >> 8), - SectorNumber = registers.LbaLowPrevious - }, - CurrentTaskFile = new AtaTaskFile - { - Command = registers.Command, - CylinderHigh = registers.LbaHighCurrent, - CylinderLow = registers.LbaMidCurrent, - DeviceHead = registers.DeviceHead, - Features = (byte)(registers.Feature & 0xFF), - SectorCount = (byte)(registers.SectorCount & 0xFF), - SectorNumber = registers.LbaLowCurrent - } - }; - - switch(protocol) - { - case AtaProtocol.PioIn: - case AtaProtocol.UDmaIn: - case AtaProtocol.Dma: - aptd.AtaFlags = AtaFlags.DataIn; - - break; - case AtaProtocol.PioOut: - case AtaProtocol.UDmaOut: - aptd.AtaFlags = AtaFlags.DataOut; - - break; - } - - switch(protocol) - { - case AtaProtocol.Dma: - case AtaProtocol.DmaQueued: - case AtaProtocol.FpDma: - case AtaProtocol.UDmaIn: - case AtaProtocol.UDmaOut: - aptd.AtaFlags |= AtaFlags.Dma; - - break; - } - - aptd.AtaFlags |= AtaFlags.ExtendedCommand; - - // Unknown if needed - aptd.AtaFlags |= AtaFlags.DrdyRequired; - - uint k = 0; - int error = 0; - - Marshal.Copy(buffer, 0, aptd.DataBuffer, buffer.Length); - - DateTime start = DateTime.Now; - - sense = !Extern.DeviceIoControlAta(fd, WindowsIoctl.IoctlAtaPassThroughDirect, ref aptd, - (uint)Marshal.SizeOf(aptd), ref aptd, (uint)Marshal.SizeOf(aptd), ref k, - IntPtr.Zero); - - DateTime end = DateTime.Now; - - if(sense) - error = Marshal.GetLastWin32Error(); - - Marshal.Copy(aptd.DataBuffer, buffer, 0, buffer.Length); - - duration = (end - start).TotalMilliseconds; - - errorRegisters.SectorCount = (ushort)((aptd.PreviousTaskFile.SectorCount << 8) + - aptd.CurrentTaskFile.SectorCount); - - errorRegisters.LbaLowPrevious = aptd.PreviousTaskFile.SectorNumber; - errorRegisters.LbaMidPrevious = aptd.PreviousTaskFile.CylinderLow; - errorRegisters.LbaHighPrevious = aptd.PreviousTaskFile.CylinderHigh; - errorRegisters.LbaLowCurrent = aptd.CurrentTaskFile.SectorNumber; - errorRegisters.LbaMidCurrent = aptd.CurrentTaskFile.CylinderLow; - errorRegisters.LbaHighCurrent = aptd.CurrentTaskFile.CylinderHigh; - errorRegisters.DeviceHead = aptd.CurrentTaskFile.DeviceHead; - errorRegisters.Error = aptd.CurrentTaskFile.Error; - errorRegisters.Status = aptd.CurrentTaskFile.Status; - - sense = errorRegisters.Error != 0 || (errorRegisters.Status & 0xA5) != 0; - - Marshal.FreeHGlobal(aptd.DataBuffer); - - return error; - } - - /// Returns true if the specified handle is controlled by a SFFDISK (aka SDHCI) driver - /// Device handle - /// true if SDHCI, false otherwise - internal static bool IsSdhci(SafeFileHandle fd) - { - var queryData1 = new SffdiskQueryDeviceProtocolData(); - queryData1.size = (ushort)Marshal.SizeOf(queryData1); - - Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskQueryDeviceProtocol, IntPtr.Zero, 0, ref queryData1, - queryData1.size, out _, IntPtr.Zero); - - return queryData1.protocolGuid.Equals(Consts.GuidSffProtocolSd) || - queryData1.protocolGuid.Equals(Consts.GuidSffProtocolMmc); - } - - /// Sends a MMC/SD command - /// The result of the command. - /// File handle - /// MMC/SD opcode - /// Buffer for MMC/SD command response - /// Timeout in seconds - /// Time it took to execute the command in milliseconds - /// True if MMC/SD returned non-OK status - /// True if data is sent from host to card - /// True if command should be preceded with CMD55 - /// Flags indicating kind and place of response - /// How many blocks to transfer - /// Command argument - /// Response registers - /// Size of block in bytes - internal static int SendMmcCommand(SafeFileHandle fd, MmcCommands command, bool write, bool isApplication, - MmcFlags flags, uint argument, uint blockSize, uint blocks, - ref byte[] buffer, out uint[] response, out double duration, out bool sense, - uint timeout = 0) - { - var commandData = new SffdiskDeviceCommandData(); - var commandDescriptor = new SdCmdDescriptor(); - commandData.size = (ushort)Marshal.SizeOf(commandData); - commandData.command = SffdiskDcmd.DeviceCommand; - commandData.protocolArgumentSize = (ushort)Marshal.SizeOf(commandDescriptor); - commandData.deviceDataBufferSize = blockSize * blocks; - commandDescriptor.commandCode = (byte)command; - commandDescriptor.cmdClass = isApplication ? SdCommandClass.AppCmd : SdCommandClass.Standard; - commandDescriptor.transferDirection = write ? SdTransferDirection.Write : SdTransferDirection.Read; - - commandDescriptor.transferType = flags.HasFlag(MmcFlags.CommandAdtc) - ? command == MmcCommands.ReadMultipleBlock - ? SdTransferType.MultiBlock - : SdTransferType.SingleBlock : SdTransferType.CmdOnly; - - commandDescriptor.responseType = 0; - - if(flags.HasFlag(MmcFlags.ResponseR1) || - flags.HasFlag(MmcFlags.ResponseSpiR1)) - commandDescriptor.responseType = SdResponseType.R1; - - if(flags.HasFlag(MmcFlags.ResponseR1B) || - flags.HasFlag(MmcFlags.ResponseSpiR1B)) - commandDescriptor.responseType = SdResponseType.R1b; - - if(flags.HasFlag(MmcFlags.ResponseR2) || - flags.HasFlag(MmcFlags.ResponseSpiR2)) - commandDescriptor.responseType = SdResponseType.R2; - - if(flags.HasFlag(MmcFlags.ResponseR3) || - flags.HasFlag(MmcFlags.ResponseSpiR3)) - commandDescriptor.responseType = SdResponseType.R3; - - if(flags.HasFlag(MmcFlags.ResponseR4) || - flags.HasFlag(MmcFlags.ResponseSpiR4)) - commandDescriptor.responseType = SdResponseType.R4; - - if(flags.HasFlag(MmcFlags.ResponseR5) || - flags.HasFlag(MmcFlags.ResponseSpiR5)) - commandDescriptor.responseType = SdResponseType.R5; - - if(flags.HasFlag(MmcFlags.ResponseR6)) - commandDescriptor.responseType = SdResponseType.R6; - - byte[] commandB = new byte[commandData.size + commandData.protocolArgumentSize + - commandData.deviceDataBufferSize]; - - Array.Copy(buffer, 0, commandB, commandData.size + commandData.protocolArgumentSize, buffer.Length); - IntPtr hBuf = Marshal.AllocHGlobal(commandB.Length); - Marshal.StructureToPtr(commandData, hBuf, true); - var descriptorOffset = IntPtr.Add(hBuf, commandData.size); - Marshal.StructureToPtr(commandDescriptor, descriptorOffset, true); - Marshal.Copy(hBuf, commandB, 0, commandB.Length); - Marshal.FreeHGlobal(hBuf); - - int error = 0; - DateTime start = DateTime.Now; - - sense = !Extern.DeviceIoControl(fd, WindowsIoctl.IoctlSffdiskDeviceCommand, commandB, (uint)commandB.Length, - commandB, (uint)commandB.Length, out _, IntPtr.Zero); - - DateTime end = DateTime.Now; - - if(sense) - error = Marshal.GetLastWin32Error(); - - buffer = new byte[blockSize * blocks]; - Buffer.BlockCopy(commandB, commandB.Length - buffer.Length, buffer, 0, buffer.Length); - - response = new uint[4]; - duration = (end - start).TotalMilliseconds; - - return error; - } - - internal static int SendMultipleMmcCommands(SafeFileHandle fd, Device.MmcSingleCommand[] commands, - out double duration, out bool sense, uint timeout = 0) - { - int error = 0; - duration = 0; - sense = false; - - if(commands.Length == 3 && - commands[0].command == MmcCommands.SetBlocklen && - commands[1].command == MmcCommands.ReadMultipleBlock && - commands[2].command == MmcCommands.StopTransmission) - return SendMmcCommand(fd, commands[1].command, commands[1].write, commands[1].isApplication, - commands[1].flags, commands[1].argument, commands[1].blockSize, - commands[1].blocks, ref commands[1].buffer, out commands[1].response, - out duration, out sense, timeout); - - foreach(Device.MmcSingleCommand command in commands) - { - int singleError = SendMmcCommand(fd, command.command, command.write, command.isApplication, - command.flags, command.argument, command.blockSize, command.blocks, - ref command.buffer, out command.response, out double cmdDuration, - out bool cmdSense, timeout); - - if(error == 0 && - singleError != 0) - error = singleError; - - duration += cmdDuration; - - if(cmdSense) - sense = true; - } - - return error; - } - - internal static int ReOpen(string devicePath, SafeFileHandle fd, out object newFd) - { - Extern.CloseHandle(fd); - - newFd = Extern.CreateFile(devicePath, FileAccess.GenericRead | FileAccess.GenericWrite, - FileShare.Read | FileShare.Write, IntPtr.Zero, FileMode.OpenExisting, - FileAttributes.Normal, IntPtr.Zero); - - return ((SafeFileHandle)newFd).IsInvalid ? Marshal.GetLastWin32Error() : 0; - } - - internal static int BufferedOsRead(SafeFileHandle fd, out byte[] buffer, long offset, uint length, - out double duration) - { - buffer = new byte[length]; - - DateTime start = DateTime.Now; - - bool sense = !Extern.SetFilePointerEx(fd, offset, out _, MoveMethod.Begin); - - DateTime end = DateTime.Now; - - if(sense) - { - duration = (end - start).TotalMilliseconds; - - return Marshal.GetLastWin32Error(); - } - - sense = !Extern.ReadFile(fd, buffer, length, out _, IntPtr.Zero); - - end = DateTime.Now; - duration = (end - start).TotalMilliseconds; - - return sense ? Marshal.GetLastWin32Error() : 0; - } + return sense ? Marshal.GetLastWin32Error() : 0; } } \ No newline at end of file diff --git a/Aaru.Devices/Windows/Enums.cs b/Aaru.Devices/Windows/Enums.cs index d01bbadee..e57f59d77 100644 --- a/Aaru.Devices/Windows/Enums.cs +++ b/Aaru.Devices/Windows/Enums.cs @@ -36,329 +36,328 @@ using System.Diagnostics.CodeAnalysis; // ReSharper disable UnusedMember.Global -namespace Aaru.Devices.Windows +namespace Aaru.Devices.Windows; + +[Flags] +internal enum FileAttributes : uint { - [Flags] - internal enum FileAttributes : uint - { - /// FILE_ATTRIBUTE_ARCHIVE - Archive = 0x20, + /// FILE_ATTRIBUTE_ARCHIVE + Archive = 0x20, - /// FILE_ATTRIBUTE_COMPRESSED - Compressed = 0x800, + /// FILE_ATTRIBUTE_COMPRESSED + Compressed = 0x800, - /// FILE_ATTRIBUTE_DEVICE - Device = 0x40, + /// FILE_ATTRIBUTE_DEVICE + Device = 0x40, - /// FILE_ATTRIBUTE_DIRECTORY - Directory = 0x10, + /// FILE_ATTRIBUTE_DIRECTORY + Directory = 0x10, - /// FILE_ATTRIBUTE_ENCRYPTED - Encrypted = 0x4000, + /// FILE_ATTRIBUTE_ENCRYPTED + Encrypted = 0x4000, - /// FILE_ATTRIBUTE_HIDDEN - Hidden = 0x02, + /// FILE_ATTRIBUTE_HIDDEN + Hidden = 0x02, - /// FILE_ATTRIBUTE_INTEGRITY_STREAM - IntegrityStream = 0x8000, + /// FILE_ATTRIBUTE_INTEGRITY_STREAM + IntegrityStream = 0x8000, - /// FILE_ATTRIBUTE_NORMAL - Normal = 0x80, + /// FILE_ATTRIBUTE_NORMAL + Normal = 0x80, - /// FILE_ATTRIBUTE_NOT_CONTENT_INDEXED - NotContentIndexed = 0x2000, + /// FILE_ATTRIBUTE_NOT_CONTENT_INDEXED + NotContentIndexed = 0x2000, - /// FILE_ATTRIBUTE_NO_SCRUB_DATA - NoScrubData = 0x20000, + /// FILE_ATTRIBUTE_NO_SCRUB_DATA + NoScrubData = 0x20000, - /// FILE_ATTRIBUTE_OFFLINE - Offline = 0x1000, + /// FILE_ATTRIBUTE_OFFLINE + Offline = 0x1000, - /// FILE_ATTRIBUTE_READONLY - Readonly = 0x01, + /// FILE_ATTRIBUTE_READONLY + Readonly = 0x01, - /// FILE_ATTRIBUTE_REPARSE_POINT - ReparsePoint = 0x400, + /// FILE_ATTRIBUTE_REPARSE_POINT + ReparsePoint = 0x400, - /// FILE_ATTRIBUTE_SPARSE_FILE - SparseFile = 0x200, + /// FILE_ATTRIBUTE_SPARSE_FILE + SparseFile = 0x200, - /// FILE_ATTRIBUTE_SYSTEM - System = 0x04, + /// FILE_ATTRIBUTE_SYSTEM + System = 0x04, - /// FILE_ATTRIBUTE_TEMPORARY - Temporary = 0x100, + /// FILE_ATTRIBUTE_TEMPORARY + Temporary = 0x100, - /// FILE_ATTRIBUTE_VIRTUAL - Virtual = 0x10000, + /// FILE_ATTRIBUTE_VIRTUAL + Virtual = 0x10000, - /// FILE_FLAG_BACKUP_SEMANTICS - BackupSemantics = 0x02000000, + /// FILE_FLAG_BACKUP_SEMANTICS + BackupSemantics = 0x02000000, - /// FILE_FLAG_DELETE_ON_CLOSE - DeleteOnClose = 0x04000000, + /// FILE_FLAG_DELETE_ON_CLOSE + DeleteOnClose = 0x04000000, - /// FILE_FLAG_NO_BUFFERING - NoBuffering = 0x20000000, + /// FILE_FLAG_NO_BUFFERING + NoBuffering = 0x20000000, - /// FILE_FLAG_OPEN_NO_RECALL - OpenNoRecall = 0x00100000, + /// FILE_FLAG_OPEN_NO_RECALL + OpenNoRecall = 0x00100000, - /// FILE_FLAG_OPEN_REPARSE_POINT - OpenReparsePoint = 0x00200000, + /// FILE_FLAG_OPEN_REPARSE_POINT + OpenReparsePoint = 0x00200000, - /// FILE_FLAG_OVERLAPPED - Overlapped = 0x40000000, + /// FILE_FLAG_OVERLAPPED + Overlapped = 0x40000000, - /// FILE_FLAG_POSIX_SEMANTICS - PosixSemantics = 0x0100000, + /// FILE_FLAG_POSIX_SEMANTICS + PosixSemantics = 0x0100000, - /// FILE_FLAG_RANDOM_ACCESS - RandomAccess = 0x10000000, + /// FILE_FLAG_RANDOM_ACCESS + RandomAccess = 0x10000000, - /// FILE_FLAG_SESSION_AWARE - SessionAware = 0x00800000, + /// FILE_FLAG_SESSION_AWARE + SessionAware = 0x00800000, - /// FILE_FLAG_SEQUENTIAL_SCAN - SequentialScan = 0x08000000, + /// FILE_FLAG_SEQUENTIAL_SCAN + SequentialScan = 0x08000000, - /// FILE_FLAG_WRITE_THROUGH - WriteThrough = 0x80000000 - } + /// FILE_FLAG_WRITE_THROUGH + WriteThrough = 0x80000000 +} - [Flags] - internal enum FileAccess : uint - { - /// FILE_READ_DATA - ReadData = 0x0001, +[Flags] +internal enum FileAccess : uint +{ + /// FILE_READ_DATA + ReadData = 0x0001, - /// FILE_LIST_DIRECTORY - ListDirectory = ReadData, + /// FILE_LIST_DIRECTORY + ListDirectory = ReadData, - /// FILE_WRITE_DATA - WriteData = 0x0002, + /// FILE_WRITE_DATA + WriteData = 0x0002, - /// FILE_ADD_FILE - AddFile = WriteData, + /// FILE_ADD_FILE + AddFile = WriteData, - /// FILE_APPEND_DATA - AppendData = 0x0004, + /// FILE_APPEND_DATA + AppendData = 0x0004, - /// FILE_ADD_SUBDIRECTORY - AddSubdirectory = AppendData, + /// FILE_ADD_SUBDIRECTORY + AddSubdirectory = AppendData, - /// FILE_CREATE_PIPE_INSTANCE - CreatePipeInstance = AppendData, + /// FILE_CREATE_PIPE_INSTANCE + CreatePipeInstance = AppendData, - /// FILE_READ_EA - ReadEa = 0x0008, + /// FILE_READ_EA + ReadEa = 0x0008, - /// FILE_WRITE_EA - WriteEa = 0x0010, + /// FILE_WRITE_EA + WriteEa = 0x0010, - /// FILE_EXECUTE - Execute = 0x0020, + /// FILE_EXECUTE + Execute = 0x0020, - /// FILE_TRAVERSE - Traverse = Execute, + /// FILE_TRAVERSE + Traverse = Execute, - /// FILE_DELETE_CHILD - DeleteChild = 0x0040, + /// FILE_DELETE_CHILD + DeleteChild = 0x0040, - /// FILE_READ_ATTRIBUTES - ReadAttributes = 0x0080, + /// FILE_READ_ATTRIBUTES + ReadAttributes = 0x0080, - /// FILE_WRITE_ATTRIBUTES - WriteAttributes = 0x0100, + /// FILE_WRITE_ATTRIBUTES + WriteAttributes = 0x0100, - /// GENERIC_READ - GenericRead = 0x80000000, + /// GENERIC_READ + GenericRead = 0x80000000, - /// GENERIC_WRITE - GenericWrite = 0x40000000, + /// GENERIC_WRITE + GenericWrite = 0x40000000, - /// GENERIC_EXECUTE - GenericExecute = 0x20000000, + /// GENERIC_EXECUTE + GenericExecute = 0x20000000, - /// GENERIC_ALL - GenericAll = 0x10000000 - } + /// GENERIC_ALL + GenericAll = 0x10000000 +} - [Flags] - internal enum FileShare : uint - { - /// FILE_SHARE_NONE - None = 0x00, +[Flags] +internal enum FileShare : uint +{ + /// FILE_SHARE_NONE + None = 0x00, - /// FILE_SHARE_READ - Read = 0x01, + /// FILE_SHARE_READ + Read = 0x01, - /// FILE_SHARE_WRITE - Write = 0x02, + /// FILE_SHARE_WRITE + Write = 0x02, - /// FILE_SHARE_DELETE - Delete = 0x03 - } + /// FILE_SHARE_DELETE + Delete = 0x03 +} - [Flags] - internal enum FileMode : uint - { - /// NEW - New = 0x01, +[Flags] +internal enum FileMode : uint +{ + /// NEW + New = 0x01, - /// CREATE_ALWAYS - CreateAlways = 0x02, + /// CREATE_ALWAYS + CreateAlways = 0x02, - /// OPEN_EXISTING - OpenExisting = 0x03, + /// OPEN_EXISTING + OpenExisting = 0x03, - /// OPEN_ALWAYS - OpenAlways = 0x04, + /// OPEN_ALWAYS + OpenAlways = 0x04, - /// TRUNCATE_EXISTING - TruncateExisting = 0x05 - } + /// TRUNCATE_EXISTING + TruncateExisting = 0x05 +} - /// Direction of SCSI transfer - internal enum ScsiIoctlDirection : byte - { - /// From host to device SCSI_IOCTL_DATA_OUT - Out = 0, +/// Direction of SCSI transfer +internal enum ScsiIoctlDirection : byte +{ + /// From host to device SCSI_IOCTL_DATA_OUT + Out = 0, - /// From device to host SCSI_IOCTL_DATA_IN - In = 1, + /// From device to host SCSI_IOCTL_DATA_IN + In = 1, - /// Unspecified direction, or bidirectional, or no data SCSI_IOCTL_DATA_UNSPECIFIED - Unspecified = 2 - } + /// Unspecified direction, or bidirectional, or no data SCSI_IOCTL_DATA_UNSPECIFIED + Unspecified = 2 +} - internal enum WindowsIoctl : uint - { - IoctlAtaPassThrough = 0x4D02C, IoctlAtaPassThroughDirect = 0x4D030, - - /// ScsiPassThrough - IoctlScsiPassThrough = 0x4D004, - - /// ScsiPassThroughDirect - IoctlScsiPassThroughDirect = 0x4D014, - - /// ScsiGetAddress - IoctlScsiGetAddress = 0x41018, IoctlStorageQueryProperty = 0x2D1400, IoctlIdePassThrough = 0x4D028, - IoctlStorageGetDeviceNumber = 0x2D1080, IoctlSffdiskQueryDeviceProtocol = 0x71E80, - IoctlSffdiskDeviceCommand = 0x79E84 - } - - [Flags] - internal enum AtaFlags : ushort - { - /// ATA_FLAGS_DRDY_REQUIRED - DrdyRequired = 0x01, - - /// ATA_FLAGS_DATA_IN - DataIn = 0x02, - - /// ATA_FLAGS_DATA_OUT - DataOut = 0x04, - - /// ATA_FLAGS_48BIT_COMMAND - ExtendedCommand = 0x08, - - /// ATA_FLAGS_USE_DMA - Dma = 0x10, - - /// ATA_FLAGS_NO_MULTIPLE - NoMultiple = 0x20 - } - - internal enum StoragePropertyId - { - Device = 0, Adapter = 1, Id = 2, - UniqueId = 3, WriteCache = 4, Miniport = 5, - AccessAlignment = 6, SeekPenalty = 7, Trim = 8, - WriteAggregation = 9, Telemetry = 10, LbProvisioning = 11, - Power = 12, Copyoffload = 13, Resiliency = 14 - } - - internal enum StorageQueryType - { - Standard = 0, Exists = 1, Mask = 2, - Max = 3 - } - - [SuppressMessage("ReSharper", "InconsistentNaming")] - internal enum StorageBusType - { - Unknown = 0, SCSI = 1, ATAPI = 2, - ATA = 3, FireWire = 4, SSA = 5, - Fibre = 6, USB = 7, RAID = 8, - iSCSI = 9, SAS = 0xA, SATA = 0xB, - SecureDigital = 0xC, MultiMediaCard = 0xD, Virtual = 0xE, - FileBackedVirtual = 0xF, Spaces = 16, SCM = 18, - UFS = 19, Max = 20, MaxReserved = 127, - NVMe = 0x11 - } - - [Flags] - internal enum DeviceGetClassFlags : uint - { - /// DIGCF_DEFAULT - Default = 0x01, - - /// DIGCF_PRESENT - Present = 0x02, - - /// DIGCF_ALLCLASSES - AllClasses = 0x04, - - /// DIGCF_PROFILE - Profile = 0x08, - - /// DIGCF_DEVICEINTERFACE - DeviceInterface = 0x10 - } - - internal enum SdCommandClass : uint - { - Standard, AppCmd - } - - internal enum SdTransferDirection : uint - { - Unspecified, Read, Write - } - - internal enum SdTransferType : uint - { - Unspecified, CmdOnly, SingleBlock, - MultiBlock, MultiBlockNoCmd12 - } - - [SuppressMessage("ReSharper", "InconsistentNaming")] - internal enum SdResponseType : uint - { - Unspecified, None, R1, - R1b, R2, R3, - R4, R5, R5b, - R6 - } - - internal enum SffdiskDcmd : uint - { - GetVersion, LockChannel, UnlockChannel, - DeviceCommand - } - - internal static class Consts - { - public static Guid GuidSffProtocolSd = new Guid("AD7536A8-D055-4C40-AA4D-96312DDB6B38"); - public static Guid GuidSffProtocolMmc = new Guid("77274D3F-2365-4491-A030-8BB44AE60097"); - - public static Guid GuidDevinterfaceDisk = - new Guid(0x53F56307, 0xB6BF, 0x11D0, 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B); - } - - internal enum MoveMethod : uint - { - Begin = 0, Current = 1, End = 2 - } +internal enum WindowsIoctl : uint +{ + IoctlAtaPassThrough = 0x4D02C, IoctlAtaPassThroughDirect = 0x4D030, + + /// ScsiPassThrough + IoctlScsiPassThrough = 0x4D004, + + /// ScsiPassThroughDirect + IoctlScsiPassThroughDirect = 0x4D014, + + /// ScsiGetAddress + IoctlScsiGetAddress = 0x41018, IoctlStorageQueryProperty = 0x2D1400, IoctlIdePassThrough = 0x4D028, + IoctlStorageGetDeviceNumber = 0x2D1080, IoctlSffdiskQueryDeviceProtocol = 0x71E80, + IoctlSffdiskDeviceCommand = 0x79E84 +} + +[Flags] +internal enum AtaFlags : ushort +{ + /// ATA_FLAGS_DRDY_REQUIRED + DrdyRequired = 0x01, + + /// ATA_FLAGS_DATA_IN + DataIn = 0x02, + + /// ATA_FLAGS_DATA_OUT + DataOut = 0x04, + + /// ATA_FLAGS_48BIT_COMMAND + ExtendedCommand = 0x08, + + /// ATA_FLAGS_USE_DMA + Dma = 0x10, + + /// ATA_FLAGS_NO_MULTIPLE + NoMultiple = 0x20 +} + +internal enum StoragePropertyId +{ + Device = 0, Adapter = 1, Id = 2, + UniqueId = 3, WriteCache = 4, Miniport = 5, + AccessAlignment = 6, SeekPenalty = 7, Trim = 8, + WriteAggregation = 9, Telemetry = 10, LbProvisioning = 11, + Power = 12, Copyoffload = 13, Resiliency = 14 +} + +internal enum StorageQueryType +{ + Standard = 0, Exists = 1, Mask = 2, + Max = 3 +} + +[SuppressMessage("ReSharper", "InconsistentNaming")] +internal enum StorageBusType +{ + Unknown = 0, SCSI = 1, ATAPI = 2, + ATA = 3, FireWire = 4, SSA = 5, + Fibre = 6, USB = 7, RAID = 8, + iSCSI = 9, SAS = 0xA, SATA = 0xB, + SecureDigital = 0xC, MultiMediaCard = 0xD, Virtual = 0xE, + FileBackedVirtual = 0xF, Spaces = 16, SCM = 18, + UFS = 19, Max = 20, MaxReserved = 127, + NVMe = 0x11 +} + +[Flags] +internal enum DeviceGetClassFlags : uint +{ + /// DIGCF_DEFAULT + Default = 0x01, + + /// DIGCF_PRESENT + Present = 0x02, + + /// DIGCF_ALLCLASSES + AllClasses = 0x04, + + /// DIGCF_PROFILE + Profile = 0x08, + + /// DIGCF_DEVICEINTERFACE + DeviceInterface = 0x10 +} + +internal enum SdCommandClass : uint +{ + Standard, AppCmd +} + +internal enum SdTransferDirection : uint +{ + Unspecified, Read, Write +} + +internal enum SdTransferType : uint +{ + Unspecified, CmdOnly, SingleBlock, + MultiBlock, MultiBlockNoCmd12 +} + +[SuppressMessage("ReSharper", "InconsistentNaming")] +internal enum SdResponseType : uint +{ + Unspecified, None, R1, + R1b, R2, R3, + R4, R5, R5b, + R6 +} + +internal enum SffdiskDcmd : uint +{ + GetVersion, LockChannel, UnlockChannel, + DeviceCommand +} + +internal static class Consts +{ + public static Guid GuidSffProtocolSd = new Guid("AD7536A8-D055-4C40-AA4D-96312DDB6B38"); + public static Guid GuidSffProtocolMmc = new Guid("77274D3F-2365-4491-A030-8BB44AE60097"); + + public static Guid GuidDevinterfaceDisk = + new Guid(0x53F56307, 0xB6BF, 0x11D0, 0x94, 0xF2, 0x00, 0xA0, 0xC9, 0x1E, 0xFB, 0x8B); +} + +internal enum MoveMethod : uint +{ + Begin = 0, Current = 1, End = 2 } \ No newline at end of file diff --git a/Aaru.Devices/Windows/Extern.cs b/Aaru.Devices/Windows/Extern.cs index 595cdb299..4bdcb5975 100644 --- a/Aaru.Devices/Windows/Extern.cs +++ b/Aaru.Devices/Windows/Extern.cs @@ -35,90 +35,89 @@ using System; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; -namespace Aaru.Devices.Windows +namespace Aaru.Devices.Windows; + +internal static class Extern { - internal static class Extern - { - [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] - internal static extern SafeFileHandle CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename, - [MarshalAs(UnmanagedType.U4)] FileAccess access, - [MarshalAs(UnmanagedType.U4)] FileShare share, - IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero - [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, - [MarshalAs(UnmanagedType.U4)] - FileAttributes flagsAndAttributes, IntPtr templateFile); + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern SafeFileHandle CreateFile([MarshalAs(UnmanagedType.LPTStr)] string filename, + [MarshalAs(UnmanagedType.U4)] FileAccess access, + [MarshalAs(UnmanagedType.U4)] FileShare share, + IntPtr securityAttributes, // optional SECURITY_ATTRIBUTES struct or IntPtr.Zero + [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, + [MarshalAs(UnmanagedType.U4)] + FileAttributes flagsAndAttributes, IntPtr templateFile); - [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] - internal static extern bool DeviceIoControlScsi(SafeFileHandle hDevice, WindowsIoctl ioControlCode, - ref ScsiPassThroughDirectAndSenseBuffer inBuffer, - uint nInBufferSize, - ref ScsiPassThroughDirectAndSenseBuffer outBuffer, - uint nOutBufferSize, ref uint pBytesReturned, - IntPtr overlapped); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControlScsi(SafeFileHandle hDevice, WindowsIoctl ioControlCode, + ref ScsiPassThroughDirectAndSenseBuffer inBuffer, + uint nInBufferSize, + ref ScsiPassThroughDirectAndSenseBuffer outBuffer, + uint nOutBufferSize, ref uint pBytesReturned, + IntPtr overlapped); - [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] - internal static extern bool DeviceIoControlAta(SafeFileHandle hDevice, WindowsIoctl ioControlCode, - ref AtaPassThroughDirect inBuffer, uint nInBufferSize, - ref AtaPassThroughDirect outBuffer, uint nOutBufferSize, - ref uint pBytesReturned, IntPtr overlapped); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControlAta(SafeFileHandle hDevice, WindowsIoctl ioControlCode, + ref AtaPassThroughDirect inBuffer, uint nInBufferSize, + ref AtaPassThroughDirect outBuffer, uint nOutBufferSize, + ref uint pBytesReturned, IntPtr overlapped); - [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] - internal static extern bool DeviceIoControlStorageQuery(SafeFileHandle hDevice, WindowsIoctl ioControlCode, - ref StoragePropertyQuery inBuffer, uint nInBufferSize, - IntPtr outBuffer, uint nOutBufferSize, - ref uint pBytesReturned, IntPtr overlapped); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControlStorageQuery(SafeFileHandle hDevice, WindowsIoctl ioControlCode, + ref StoragePropertyQuery inBuffer, uint nInBufferSize, + IntPtr outBuffer, uint nOutBufferSize, + ref uint pBytesReturned, IntPtr overlapped); - [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] - internal static extern bool DeviceIoControlIde(SafeFileHandle hDevice, WindowsIoctl ioControlCode, - ref IdePassThroughDirect inBuffer, uint nInBufferSize, - ref IdePassThroughDirect outBuffer, uint nOutBufferSize, - ref uint pBytesReturned, IntPtr overlapped); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControlIde(SafeFileHandle hDevice, WindowsIoctl ioControlCode, + ref IdePassThroughDirect inBuffer, uint nInBufferSize, + ref IdePassThroughDirect outBuffer, uint nOutBufferSize, + ref uint pBytesReturned, IntPtr overlapped); - [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] - internal static extern bool DeviceIoControlGetDeviceNumber(SafeFileHandle hDevice, WindowsIoctl ioControlCode, - IntPtr inBuffer, uint nInBufferSize, - ref StorageDeviceNumber outBuffer, - uint nOutBufferSize, ref uint pBytesReturned, - IntPtr overlapped); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControlGetDeviceNumber(SafeFileHandle hDevice, WindowsIoctl ioControlCode, + IntPtr inBuffer, uint nInBufferSize, + ref StorageDeviceNumber outBuffer, + uint nOutBufferSize, ref uint pBytesReturned, + IntPtr overlapped); - [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] - internal static extern bool DeviceIoControl(SafeFileHandle hDevice, WindowsIoctl ioControlCode, IntPtr inBuffer, - uint nInBufferSize, ref SffdiskQueryDeviceProtocolData outBuffer, - uint nOutBufferSize, out uint pBytesReturned, IntPtr overlapped); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControl(SafeFileHandle hDevice, WindowsIoctl ioControlCode, IntPtr inBuffer, + uint nInBufferSize, ref SffdiskQueryDeviceProtocolData outBuffer, + uint nOutBufferSize, out uint pBytesReturned, IntPtr overlapped); - [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] - internal static extern bool DeviceIoControl(SafeFileHandle hDevice, WindowsIoctl ioControlCode, byte[] inBuffer, - uint nInBufferSize, byte[] outBuffer, uint nOutBufferSize, - out uint pBytesReturned, IntPtr overlapped); + [DllImport("Kernel32.dll", SetLastError = true, EntryPoint = "DeviceIoControl", CharSet = CharSet.Auto)] + internal static extern bool DeviceIoControl(SafeFileHandle hDevice, WindowsIoctl ioControlCode, byte[] inBuffer, + uint nInBufferSize, byte[] outBuffer, uint nOutBufferSize, + out uint pBytesReturned, IntPtr overlapped); - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] - internal static extern SafeFileHandle SetupDiGetClassDevs(ref Guid classGuid, IntPtr enumerator, - IntPtr hwndParent, DeviceGetClassFlags flags); + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + internal static extern SafeFileHandle SetupDiGetClassDevs(ref Guid classGuid, IntPtr enumerator, + IntPtr hwndParent, DeviceGetClassFlags flags); - [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern bool SetupDiEnumDeviceInterfaces(SafeFileHandle hDevInfo, IntPtr devInfo, - ref Guid interfaceClassGuid, uint memberIndex, - ref DeviceInterfaceData deviceInterfaceData); + [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool SetupDiEnumDeviceInterfaces(SafeFileHandle hDevInfo, IntPtr devInfo, + ref Guid interfaceClassGuid, uint memberIndex, + ref DeviceInterfaceData deviceInterfaceData); - [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern bool SetupDiGetDeviceInterfaceDetail(SafeFileHandle hDevInfo, - ref DeviceInterfaceData deviceInterfaceData, - IntPtr deviceInterfaceDetailData, - uint deviceInterfaceDetailDataSize, - ref uint requiredSize, IntPtr deviceInfoData); + [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool SetupDiGetDeviceInterfaceDetail(SafeFileHandle hDevInfo, + ref DeviceInterfaceData deviceInterfaceData, + IntPtr deviceInterfaceDetailData, + uint deviceInterfaceDetailDataSize, + ref uint requiredSize, IntPtr deviceInfoData); - [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] - public static extern bool SetupDiDestroyDeviceInfoList(SafeFileHandle hDevInfo); + [DllImport("setupapi.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern bool SetupDiDestroyDeviceInfoList(SafeFileHandle hDevInfo); - [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern bool CloseHandle(SafeFileHandle hDevice); + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern bool CloseHandle(SafeFileHandle hDevice); - [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - public static extern bool SetFilePointerEx(SafeFileHandle hFile, long liDistanceToMove, - out long lpNewFilePointer, MoveMethod dwMoveMethod); + [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool SetFilePointerEx(SafeFileHandle hFile, long liDistanceToMove, + out long lpNewFilePointer, MoveMethod dwMoveMethod); - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - public static extern bool ReadFile(SafeFileHandle hFile, byte[] lpBuffer, uint nNumberOfBytesToRead, - out uint lpNumberOfBytesRead, IntPtr lpOverlapped); - } + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + public static extern bool ReadFile(SafeFileHandle hFile, byte[] lpBuffer, uint nNumberOfBytesToRead, + out uint lpNumberOfBytesRead, IntPtr lpOverlapped); } \ No newline at end of file diff --git a/Aaru.Devices/Windows/ListDevices.cs b/Aaru.Devices/Windows/ListDevices.cs index 1cf2947a4..f854fcbca 100644 --- a/Aaru.Devices/Windows/ListDevices.cs +++ b/Aaru.Devices/Windows/ListDevices.cs @@ -40,186 +40,185 @@ using Aaru.Helpers; using Microsoft.Win32.SafeHandles; using Marshal = System.Runtime.InteropServices.Marshal; -namespace Aaru.Devices.Windows +namespace Aaru.Devices.Windows; + +internal static class ListDevices { - internal static class ListDevices + /// Converts a hex dump string to the ASCII string it represents + /// Hex dump + /// Decoded string + static string HexStringToString(string hex) { - /// Converts a hex dump string to the ASCII string it represents - /// Hex dump - /// Decoded string - static string HexStringToString(string hex) + var result = new StringBuilder(); + const string HEXTABLE = "0123456789abcdef"; + + for(int i = 0; i < hex.Length / 2; i++) + result.Append((char)((16 * HEXTABLE.IndexOf(hex[2 * i])) + HEXTABLE.IndexOf(hex[(2 * i) + 1]))); + + return result.ToString(); + } + + /// Gets a list of all known storage devices on Windows + /// List of devices + [SuppressMessage("ReSharper", "RedundantCatchClause")] + internal static DeviceInfo[] GetList() + { + List deviceIDs = new List(); + + try { - var result = new StringBuilder(); - const string HEXTABLE = "0123456789abcdef"; + var mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive"); - for(int i = 0; i < hex.Length / 2; i++) - result.Append((char)((16 * HEXTABLE.IndexOf(hex[2 * i])) + HEXTABLE.IndexOf(hex[(2 * i) + 1]))); + ManagementObjectCollection objCol = mgmtObjSearcher.Get(); - return result.ToString(); + deviceIDs.AddRange(from ManagementObject drive in objCol select (string)drive["DeviceID"]); + + mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_TapeDrive"); + objCol = mgmtObjSearcher.Get(); + + deviceIDs.AddRange(from ManagementObject drive in objCol select (string)drive["DeviceID"]); + + mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_CDROMDrive"); + objCol = mgmtObjSearcher.Get(); + + deviceIDs.AddRange(from ManagementObject drive in objCol select (string)drive["Drive"]); } - - /// Gets a list of all known storage devices on Windows - /// List of devices - [SuppressMessage("ReSharper", "RedundantCatchClause")] - internal static DeviceInfo[] GetList() + catch(Exception) { - List deviceIDs = new List(); - - try - { - var mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive"); - - ManagementObjectCollection objCol = mgmtObjSearcher.Get(); - - deviceIDs.AddRange(from ManagementObject drive in objCol select (string)drive["DeviceID"]); - - mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_TapeDrive"); - objCol = mgmtObjSearcher.Get(); - - deviceIDs.AddRange(from ManagementObject drive in objCol select (string)drive["DeviceID"]); - - mgmtObjSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_CDROMDrive"); - objCol = mgmtObjSearcher.Get(); - - deviceIDs.AddRange(from ManagementObject drive in objCol select (string)drive["Drive"]); - } - catch(Exception) - { - #if DEBUG - throw; - #else + #if DEBUG + throw; + #else return null; - #endif - } - - List devList = new List(); - - foreach(string devId in deviceIDs) - { - if(devId is null) - continue; - - string physId = devId; - - // TODO: This can be done better - if(devId.Length == 2 && - devId[1] == ':') - physId = "\\\\?\\" + devId; - - SafeFileHandle fd = Extern.CreateFile(physId, 0, FileShare.Read | FileShare.Write, IntPtr.Zero, - FileMode.OpenExisting, 0, IntPtr.Zero); - - if(fd.IsInvalid) - continue; - - var query = new StoragePropertyQuery - { - PropertyId = StoragePropertyId.Device, - QueryType = StorageQueryType.Standard, - AdditionalParameters = new byte[1] - }; - - //StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor(); - //descriptor.RawDeviceProperties = new byte[16384]; - - IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); - byte[] descriptorB = new byte[1000]; - - uint returned = 0; - int error = 0; - - bool hasError = !Extern.DeviceIoControlStorageQuery(fd, WindowsIoctl.IoctlStorageQueryProperty, - ref query, (uint)Marshal.SizeOf(query), - descriptorPtr, 1000, ref returned, IntPtr.Zero); - - if(hasError) - error = Marshal.GetLastWin32Error(); - - Marshal.Copy(descriptorPtr, descriptorB, 0, 1000); - - if(hasError && error != 0) - continue; - - var descriptor = new StorageDeviceDescriptor - { - Version = BitConverter.ToUInt32(descriptorB, 0), - Size = BitConverter.ToUInt32(descriptorB, 4), - DeviceType = descriptorB[8], - DeviceTypeModifier = descriptorB[9], - RemovableMedia = BitConverter.ToBoolean(descriptorB, 10), - CommandQueueing = BitConverter.ToBoolean(descriptorB, 11), - VendorIdOffset = BitConverter.ToInt32(descriptorB, 12), - ProductIdOffset = BitConverter.ToInt32(descriptorB, 16), - ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20), - SerialNumberOffset = BitConverter.ToInt32(descriptorB, 24), - BusType = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28), - RawPropertiesLength = BitConverter.ToUInt32(descriptorB, 32) - }; - - var info = new DeviceInfo - { - Path = physId, - Bus = descriptor.BusType.ToString() - }; - - if(descriptor.VendorIdOffset > 0) - info.Vendor = - StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.VendorIdOffset); - - if(descriptor.ProductIdOffset > 0) - info.Model = - StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.ProductIdOffset); - - // TODO: Get serial number of SCSI and USB devices, probably also FireWire (untested) - if(descriptor.SerialNumberOffset > 0) - { - info.Serial = - StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.SerialNumberOffset); - - // fix any serial numbers that are returned as hex-strings - if(Array.TrueForAll(info.Serial.ToCharArray(), c => "0123456789abcdef".IndexOf(c) >= 0) && - info.Serial.Length == 40) - info.Serial = HexStringToString(info.Serial).Trim(); - } - - if(string.IsNullOrEmpty(info.Vendor) || - info.Vendor == "ATA") - { - string[] pieces = info.Model?.Split(' '); - - if(pieces?.Length > 1) - { - info.Vendor = pieces[0]; - info.Model = info.Model.Substring(pieces[0].Length + 1); - } - } - - switch(descriptor.BusType) - { - case StorageBusType.SCSI: - case StorageBusType.ATAPI: - case StorageBusType.ATA: - case StorageBusType.FireWire: - case StorageBusType.SSA: - case StorageBusType.Fibre: - case StorageBusType.USB: - case StorageBusType.iSCSI: - case StorageBusType.SAS: - case StorageBusType.SATA: - case StorageBusType.SecureDigital: - case StorageBusType.MultiMediaCard: - info.Supported = true; - - break; - } - - Marshal.FreeHGlobal(descriptorPtr); - devList.Add(info); - } - - DeviceInfo[] devices = devList.ToArray(); - - return devices; + #endif } + + List devList = new List(); + + foreach(string devId in deviceIDs) + { + if(devId is null) + continue; + + string physId = devId; + + // TODO: This can be done better + if(devId.Length == 2 && + devId[1] == ':') + physId = "\\\\?\\" + devId; + + SafeFileHandle fd = Extern.CreateFile(physId, 0, FileShare.Read | FileShare.Write, IntPtr.Zero, + FileMode.OpenExisting, 0, IntPtr.Zero); + + if(fd.IsInvalid) + continue; + + var query = new StoragePropertyQuery + { + PropertyId = StoragePropertyId.Device, + QueryType = StorageQueryType.Standard, + AdditionalParameters = new byte[1] + }; + + //StorageDeviceDescriptor descriptor = new StorageDeviceDescriptor(); + //descriptor.RawDeviceProperties = new byte[16384]; + + IntPtr descriptorPtr = Marshal.AllocHGlobal(1000); + byte[] descriptorB = new byte[1000]; + + uint returned = 0; + int error = 0; + + bool hasError = !Extern.DeviceIoControlStorageQuery(fd, WindowsIoctl.IoctlStorageQueryProperty, + ref query, (uint)Marshal.SizeOf(query), + descriptorPtr, 1000, ref returned, IntPtr.Zero); + + if(hasError) + error = Marshal.GetLastWin32Error(); + + Marshal.Copy(descriptorPtr, descriptorB, 0, 1000); + + if(hasError && error != 0) + continue; + + var descriptor = new StorageDeviceDescriptor + { + Version = BitConverter.ToUInt32(descriptorB, 0), + Size = BitConverter.ToUInt32(descriptorB, 4), + DeviceType = descriptorB[8], + DeviceTypeModifier = descriptorB[9], + RemovableMedia = BitConverter.ToBoolean(descriptorB, 10), + CommandQueueing = BitConverter.ToBoolean(descriptorB, 11), + VendorIdOffset = BitConverter.ToInt32(descriptorB, 12), + ProductIdOffset = BitConverter.ToInt32(descriptorB, 16), + ProductRevisionOffset = BitConverter.ToInt32(descriptorB, 20), + SerialNumberOffset = BitConverter.ToInt32(descriptorB, 24), + BusType = (StorageBusType)BitConverter.ToUInt32(descriptorB, 28), + RawPropertiesLength = BitConverter.ToUInt32(descriptorB, 32) + }; + + var info = new DeviceInfo + { + Path = physId, + Bus = descriptor.BusType.ToString() + }; + + if(descriptor.VendorIdOffset > 0) + info.Vendor = + StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.VendorIdOffset); + + if(descriptor.ProductIdOffset > 0) + info.Model = + StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.ProductIdOffset); + + // TODO: Get serial number of SCSI and USB devices, probably also FireWire (untested) + if(descriptor.SerialNumberOffset > 0) + { + info.Serial = + StringHandlers.CToString(descriptorB, Encoding.ASCII, start: descriptor.SerialNumberOffset); + + // fix any serial numbers that are returned as hex-strings + if(Array.TrueForAll(info.Serial.ToCharArray(), c => "0123456789abcdef".IndexOf(c) >= 0) && + info.Serial.Length == 40) + info.Serial = HexStringToString(info.Serial).Trim(); + } + + if(string.IsNullOrEmpty(info.Vendor) || + info.Vendor == "ATA") + { + string[] pieces = info.Model?.Split(' '); + + if(pieces?.Length > 1) + { + info.Vendor = pieces[0]; + info.Model = info.Model.Substring(pieces[0].Length + 1); + } + } + + switch(descriptor.BusType) + { + case StorageBusType.SCSI: + case StorageBusType.ATAPI: + case StorageBusType.ATA: + case StorageBusType.FireWire: + case StorageBusType.SSA: + case StorageBusType.Fibre: + case StorageBusType.USB: + case StorageBusType.iSCSI: + case StorageBusType.SAS: + case StorageBusType.SATA: + case StorageBusType.SecureDigital: + case StorageBusType.MultiMediaCard: + info.Supported = true; + + break; + } + + Marshal.FreeHGlobal(descriptorPtr); + devList.Add(info); + } + + DeviceInfo[] devices = devList.ToArray(); + + return devices; } } \ No newline at end of file diff --git a/Aaru.Devices/Windows/Structs.cs b/Aaru.Devices/Windows/Structs.cs index b141a8020..b80671940 100644 --- a/Aaru.Devices/Windows/Structs.cs +++ b/Aaru.Devices/Windows/Structs.cs @@ -35,234 +35,233 @@ using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace Aaru.Devices.Windows +namespace Aaru.Devices.Windows; + +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct ScsiPassThroughDirect { - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct ScsiPassThroughDirect - { - public ushort Length; - public byte ScsiStatus; - public byte PathId; - public byte TargetId; - public byte Lun; - public byte CdbLength; - public byte SenseInfoLength; - [MarshalAs(UnmanagedType.U1)] - public ScsiIoctlDirection DataIn; - public uint DataTransferLength; - public uint TimeOutValue; - public IntPtr DataBuffer; - public uint SenseInfoOffset; + public ushort Length; + public byte ScsiStatus; + public byte PathId; + public byte TargetId; + public byte Lun; + public byte CdbLength; + public byte SenseInfoLength; + [MarshalAs(UnmanagedType.U1)] + public ScsiIoctlDirection DataIn; + public uint DataTransferLength; + public uint TimeOutValue; + public IntPtr DataBuffer; + public uint SenseInfoOffset; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public byte[] Cdb; - } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public byte[] Cdb; +} - [StructLayout(LayoutKind.Sequential)] - internal struct ScsiPassThroughDirectAndSenseBuffer - { - public ScsiPassThroughDirect sptd; +[StructLayout(LayoutKind.Sequential)] +internal struct ScsiPassThroughDirectAndSenseBuffer +{ + public ScsiPassThroughDirect sptd; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public byte[] SenseBuf; - } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public byte[] SenseBuf; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct AtaPassThroughDirect - { - /// Length in bytes of this structure - public ushort Length; +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct AtaPassThroughDirect +{ + /// Length in bytes of this structure + public ushort Length; - /// Indicates transfer direction and kind of operation - [MarshalAs(UnmanagedType.U2)] - public AtaFlags AtaFlags; + /// Indicates transfer direction and kind of operation + [MarshalAs(UnmanagedType.U2)] + public AtaFlags AtaFlags; - /// Indicates IDE port or bus, set by driver - public byte PathId; + /// Indicates IDE port or bus, set by driver + public byte PathId; - /// Indicates target device on bus, set by driver - public byte TargetId; + /// Indicates target device on bus, set by driver + public byte TargetId; - /// Indicates logical unit number of device, set by driver - public byte Lun; + /// Indicates logical unit number of device, set by driver + public byte Lun; - /// Reserved - public byte ReservedAsUchar; + /// Reserved + public byte ReservedAsUchar; - /// Data transfer length in bytes - public uint DataTransferLength; + /// Data transfer length in bytes + public uint DataTransferLength; - /// Timeout value in seconds - public uint TimeOutValue; + /// Timeout value in seconds + public uint TimeOutValue; - /// Reserved - public uint ReservedAsUlong; + /// Reserved + public uint ReservedAsUlong; - /// Pointer to data buffer - public IntPtr DataBuffer; + /// Pointer to data buffer + public IntPtr DataBuffer; - /// Previous ATA registers, for LBA48 - public AtaTaskFile PreviousTaskFile; + /// Previous ATA registers, for LBA48 + public AtaTaskFile PreviousTaskFile; - /// ATA registers - public AtaTaskFile CurrentTaskFile; - } + /// ATA registers + public AtaTaskFile CurrentTaskFile; +} - [StructLayout(LayoutKind.Explicit), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct AtaTaskFile - { - // Fields for commands sent - [FieldOffset(0)] - public byte Features; - [FieldOffset(6)] - public byte Command; +[StructLayout(LayoutKind.Explicit), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct AtaTaskFile +{ + // Fields for commands sent + [FieldOffset(0)] + public byte Features; + [FieldOffset(6)] + public byte Command; - // Fields on command return - [FieldOffset(0)] - public byte Error; - [FieldOffset(6)] - public byte Status; + // Fields on command return + [FieldOffset(0)] + public byte Error; + [FieldOffset(6)] + public byte Status; - // Common fields - [FieldOffset(1)] - public byte SectorCount; - [FieldOffset(2)] - public byte SectorNumber; - [FieldOffset(3)] - public byte CylinderLow; - [FieldOffset(4)] - public byte CylinderHigh; - [FieldOffset(5)] - public byte DeviceHead; - [FieldOffset(7)] - public byte Reserved; - } + // Common fields + [FieldOffset(1)] + public byte SectorCount; + [FieldOffset(2)] + public byte SectorNumber; + [FieldOffset(3)] + public byte CylinderLow; + [FieldOffset(4)] + public byte CylinderHigh; + [FieldOffset(5)] + public byte DeviceHead; + [FieldOffset(7)] + public byte Reserved; +} - [StructLayout(LayoutKind.Sequential)] - internal struct StoragePropertyQuery - { - [MarshalAs(UnmanagedType.U4)] - public StoragePropertyId PropertyId; - [MarshalAs(UnmanagedType.U4)] - public StorageQueryType QueryType; +[StructLayout(LayoutKind.Sequential)] +internal struct StoragePropertyQuery +{ + [MarshalAs(UnmanagedType.U4)] + public StoragePropertyId PropertyId; + [MarshalAs(UnmanagedType.U4)] + public StorageQueryType QueryType; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] - public byte[] AdditionalParameters; - } + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public byte[] AdditionalParameters; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct StorageDescriptorHeader - { - public uint Version; - public uint Size; - } +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct StorageDescriptorHeader +{ + public uint Version; + public uint Size; +} - [StructLayout(LayoutKind.Sequential)] - internal struct StorageDeviceDescriptor - { - public uint Version; - public uint Size; - public byte DeviceType; - public byte DeviceTypeModifier; - [MarshalAs(UnmanagedType.U1)] - public bool RemovableMedia; - [MarshalAs(UnmanagedType.U1)] - public bool CommandQueueing; - public int VendorIdOffset; - public int ProductIdOffset; - public int ProductRevisionOffset; - public int SerialNumberOffset; - public StorageBusType BusType; - public uint RawPropertiesLength; - public byte[] RawDeviceProperties; - } +[StructLayout(LayoutKind.Sequential)] +internal struct StorageDeviceDescriptor +{ + public uint Version; + public uint Size; + public byte DeviceType; + public byte DeviceTypeModifier; + [MarshalAs(UnmanagedType.U1)] + public bool RemovableMedia; + [MarshalAs(UnmanagedType.U1)] + public bool CommandQueueing; + public int VendorIdOffset; + public int ProductIdOffset; + public int ProductRevisionOffset; + public int SerialNumberOffset; + public StorageBusType BusType; + public uint RawPropertiesLength; + public byte[] RawDeviceProperties; +} - [StructLayout(LayoutKind.Sequential)] - internal struct IdePassThroughDirect - { - /// ATA registers - public AtaTaskFile CurrentTaskFile; +[StructLayout(LayoutKind.Sequential)] +internal struct IdePassThroughDirect +{ + /// ATA registers + public AtaTaskFile CurrentTaskFile; - /// Size of data buffer - public uint DataBufferSize; + /// Size of data buffer + public uint DataBufferSize; - /// Data buffer - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] - public byte[] DataBuffer; - } + /// Data buffer + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] + public byte[] DataBuffer; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct StorageDeviceNumber - { - public int deviceType; - public int deviceNumber; - public int partitionNumber; - } +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct StorageDeviceNumber +{ + public int deviceType; + public int deviceNumber; + public int partitionNumber; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct DeviceInfoData - { - public int cbSize; - public Guid classGuid; - public uint devInst; - public IntPtr reserved; - } +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct DeviceInfoData +{ + public int cbSize; + public Guid classGuid; + public uint devInst; + public IntPtr reserved; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct DeviceInterfaceData - { - public int cbSize; - public Guid interfaceClassGuid; - public uint flags; - readonly IntPtr reserved; - } +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct DeviceInterfaceData +{ + public int cbSize; + public Guid interfaceClassGuid; + public uint flags; + readonly IntPtr reserved; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct UsbSetupPacket - { - public byte bmRequest; - public byte bRequest; - public short wValue; - public short wIndex; - public short wLength; - } +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct UsbSetupPacket +{ + public byte bmRequest; + public byte bRequest; + public short wValue; + public short wIndex; + public short wLength; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct UsbDescriptorRequest - { - public int ConnectionIndex; +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct UsbDescriptorRequest +{ + public int ConnectionIndex; - public UsbSetupPacket SetupPacket; + public UsbSetupPacket SetupPacket; - //public byte[] Data; - } + //public byte[] Data; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct SffdiskQueryDeviceProtocolData - { - public ushort size; - public ushort reserved; - public Guid protocolGuid; - } +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct SffdiskQueryDeviceProtocolData +{ + public ushort size; + public ushort reserved; + public Guid protocolGuid; +} - [StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct SffdiskDeviceCommandData - { - public ushort size; - public ushort reserved; - public SffdiskDcmd command; - public ushort protocolArgumentSize; - public uint deviceDataBufferSize; - public uint information; - } +[StructLayout(LayoutKind.Sequential), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] +internal struct SffdiskDeviceCommandData +{ + public ushort size; + public ushort reserved; + public SffdiskDcmd command; + public ushort protocolArgumentSize; + public uint deviceDataBufferSize; + public uint information; +} - [StructLayout(LayoutKind.Sequential)] - internal struct SdCmdDescriptor - { - public byte commandCode; - public SdCommandClass cmdClass; - public SdTransferDirection transferDirection; - public SdTransferType transferType; - public SdResponseType responseType; - } +[StructLayout(LayoutKind.Sequential)] +internal struct SdCmdDescriptor +{ + public byte commandCode; + public SdCommandClass cmdClass; + public SdTransferDirection transferDirection; + public SdTransferType transferType; + public SdResponseType responseType; } \ No newline at end of file diff --git a/Aaru.Devices/Windows/Usb.cs b/Aaru.Devices/Windows/Usb.cs index f579707cd..a7f437c8a 100644 --- a/Aaru.Devices/Windows/Usb.cs +++ b/Aaru.Devices/Windows/Usb.cs @@ -38,1050 +38,1049 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text; -namespace Aaru.Devices.Windows +namespace Aaru.Devices.Windows; + +// TODO: Even after cleaning, refactoring and xml-documenting, this code needs some love +/// Implements functions for getting and accessing information from the USB bus +[SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "UnusedType.Local")] +internal static partial class Usb { - // TODO: Even after cleaning, refactoring and xml-documenting, this code needs some love - /// Implements functions for getting and accessing information from the USB bus - [SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "UnusedType.Local")] - internal static partial class Usb + /// Return a list of USB Host Controllers + /// List of USB Host Controllers + static IEnumerable GetHostControllers() { - /// Return a list of USB Host Controllers - /// List of USB Host Controllers - static IEnumerable GetHostControllers() + List hostList = new List(); + var hostGuid = new Guid(GUID_DEVINTERFACE_HUBCONTROLLER); + + // We start at the "root" of the device tree and look for all + // devices that match the interface GUID of a Hub Controller + IntPtr h = SetupDiGetClassDevs(ref hostGuid, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if(h == INVALID_HANDLE_VALUE) + return new ReadOnlyCollection(hostList); + + IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); + bool success; + int i = 0; + + do { - List hostList = new List(); - var hostGuid = new Guid(GUID_DEVINTERFACE_HUBCONTROLLER); - - // We start at the "root" of the device tree and look for all - // devices that match the interface GUID of a Hub Controller - IntPtr h = SetupDiGetClassDevs(ref hostGuid, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - if(h == INVALID_HANDLE_VALUE) - return new ReadOnlyCollection(hostList); - - IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); - bool success; - int i = 0; - - do + var host = new UsbController { - var host = new UsbController + ControllerIndex = i + }; + + // create a Device Interface Data structure + var dia = new SpDeviceInterfaceData(); + dia.cbSize = Marshal.SizeOf(dia); + + // start the enumeration + success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref hostGuid, i, ref dia); + + if(success) + { + // build a DevInfo Data structure + var da = new SpDevinfoData(); + da.cbSize = Marshal.SizeOf(da); + + // build a Device Interface Detail Data structure + var didd = new SpDeviceInterfaceDetailData { - ControllerIndex = i + cbSize = 4 + Marshal.SystemDefaultCharSize }; - // create a Device Interface Data structure - var dia = new SpDeviceInterfaceData(); - dia.cbSize = Marshal.SizeOf(dia); + // trust me :) - // start the enumeration - success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref hostGuid, i, ref dia); + // now we can get some more detailed information + int nRequiredSize = 0; + const int N_BYTES = BUFFER_SIZE; - if(success) + if(SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, N_BYTES, ref nRequiredSize, ref da)) { - // build a DevInfo Data structure - var da = new SpDevinfoData(); - da.cbSize = Marshal.SizeOf(da); + host.ControllerDevicePath = didd.DevicePath; - // build a Device Interface Detail Data structure - var didd = new SpDeviceInterfaceDetailData - { - cbSize = 4 + Marshal.SystemDefaultCharSize - }; - - // trust me :) - - // now we can get some more detailed information - int nRequiredSize = 0; - const int N_BYTES = BUFFER_SIZE; - - if(SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, N_BYTES, ref nRequiredSize, ref da)) - { - host.ControllerDevicePath = didd.DevicePath; - - // get the Device Description and DriverKeyName - int requiredSize = 0; - int regType = REG_SZ; - - if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DEVICEDESC, ref regType, ptrBuf, - BUFFER_SIZE, ref requiredSize)) - host.ControllerDeviceDesc = Marshal.PtrToStringAuto(ptrBuf); - - if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DRIVER, ref regType, ptrBuf, BUFFER_SIZE, - ref requiredSize)) - host.ControllerDriverKeyName = Marshal.PtrToStringAuto(ptrBuf); - } - - hostList.Add(host); - } - - i++; - } while(success); - - Marshal.FreeHGlobal(ptrBuf); - SetupDiDestroyDeviceInfoList(h); - - // convert it into a Collection - return new ReadOnlyCollection(hostList); - } - - /// private function for finding a USB device's Description - /// Device driver key name - /// USB device description - static string GetDescriptionByKeyName(string driverKeyName) - { - string ans = ""; - const string DEV_ENUM = REGSTR_KEY_USB; - - // Use the "enumerator form" of the SetupDiGetClassDevs API - // to generate a list of all USB devices - IntPtr h = SetupDiGetClassDevs(0, DEV_ENUM, IntPtr.Zero, DIGCF_PRESENT | DIGCF_ALLCLASSES); - - if(h == INVALID_HANDLE_VALUE) - return ans; - - IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); - - bool success; - int i = 0; - - do - { - // create a Device Interface Data structure - var da = new SpDevinfoData(); - da.cbSize = Marshal.SizeOf(da); - - // start the enumeration - success = SetupDiEnumDeviceInfo(h, i, ref da); - - if(success) - { - int requiredSize = 0; - int regType = REG_SZ; - string keyName = ""; - - if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DRIVER, ref regType, ptrBuf, BUFFER_SIZE, - ref requiredSize)) - keyName = Marshal.PtrToStringAuto(ptrBuf); - - // is it a match? - if(keyName == driverKeyName) - { - if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DEVICEDESC, ref regType, ptrBuf, - BUFFER_SIZE, ref requiredSize)) - ans = Marshal.PtrToStringAuto(ptrBuf); - - break; - } - } - - i++; - } while(success); - - Marshal.FreeHGlobal(ptrBuf); - SetupDiDestroyDeviceInfoList(h); - - return ans; - } - - /// private function for finding a USB device's Instance ID - /// Device driver key name - /// Device instance ID - static string GetInstanceIdByKeyName(string driverKeyName) - { - string ans = ""; - const string DEV_ENUM = REGSTR_KEY_USB; - - // Use the "enumerator form" of the SetupDiGetClassDevs API - // to generate a list of all USB devices - IntPtr h = SetupDiGetClassDevs(0, DEV_ENUM, IntPtr.Zero, DIGCF_PRESENT | DIGCF_ALLCLASSES); - - if(h == INVALID_HANDLE_VALUE) - return ans; - - IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); - - bool success; - int i = 0; - - do - { - // create a Device Interface Data structure - var da = new SpDevinfoData(); - da.cbSize = Marshal.SizeOf(da); - - // start the enumeration - success = SetupDiEnumDeviceInfo(h, i, ref da); - - if(success) - { + // get the Device Description and DriverKeyName int requiredSize = 0; int regType = REG_SZ; - string keyName = ""; + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DEVICEDESC, ref regType, ptrBuf, + BUFFER_SIZE, ref requiredSize)) + host.ControllerDeviceDesc = Marshal.PtrToStringAuto(ptrBuf); if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DRIVER, ref regType, ptrBuf, BUFFER_SIZE, ref requiredSize)) - keyName = Marshal.PtrToStringAuto(ptrBuf); - - // is it a match? - if(keyName == driverKeyName) - { - const int N_BYTES = BUFFER_SIZE; - var sb = new StringBuilder(N_BYTES); - SetupDiGetDeviceInstanceId(h, ref da, sb, N_BYTES, out requiredSize); - ans = sb.ToString(); - - break; - } + host.ControllerDriverKeyName = Marshal.PtrToStringAuto(ptrBuf); } - i++; - } while(success); + hostList.Add(host); + } - Marshal.FreeHGlobal(ptrBuf); - SetupDiDestroyDeviceInfoList(h); + i++; + } while(success); + Marshal.FreeHGlobal(ptrBuf); + SetupDiDestroyDeviceInfoList(h); + + // convert it into a Collection + return new ReadOnlyCollection(hostList); + } + + /// private function for finding a USB device's Description + /// Device driver key name + /// USB device description + static string GetDescriptionByKeyName(string driverKeyName) + { + string ans = ""; + const string DEV_ENUM = REGSTR_KEY_USB; + + // Use the "enumerator form" of the SetupDiGetClassDevs API + // to generate a list of all USB devices + IntPtr h = SetupDiGetClassDevs(0, DEV_ENUM, IntPtr.Zero, DIGCF_PRESENT | DIGCF_ALLCLASSES); + + if(h == INVALID_HANDLE_VALUE) return ans; - } - /// Represents a USB Host Controller - sealed class UsbController + IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); + + bool success; + int i = 0; + + do { - internal string ControllerDriverKeyName, ControllerDevicePath, ControllerDeviceDesc; - internal int ControllerIndex; + // create a Device Interface Data structure + var da = new SpDevinfoData(); + da.cbSize = Marshal.SizeOf(da); - /// A simple default constructor - internal UsbController() + // start the enumeration + success = SetupDiEnumDeviceInfo(h, i, ref da); + + if(success) { - ControllerIndex = 0; - ControllerDevicePath = ""; - ControllerDeviceDesc = ""; - ControllerDriverKeyName = ""; + int requiredSize = 0; + int regType = REG_SZ; + string keyName = ""; + + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DRIVER, ref regType, ptrBuf, BUFFER_SIZE, + ref requiredSize)) + keyName = Marshal.PtrToStringAuto(ptrBuf); + + // is it a match? + if(keyName == driverKeyName) + { + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DEVICEDESC, ref regType, ptrBuf, + BUFFER_SIZE, ref requiredSize)) + ans = Marshal.PtrToStringAuto(ptrBuf); + + break; + } } - /// Return the index of the instance - internal int Index => ControllerIndex; + i++; + } while(success); - /// - /// Return the Device Path, such as "\\?\pci#ven_10de&dev_005a&subsys_815a1043&rev_a2#3&267a616a&0& - /// 58#{3abf6f2d-71c4-462a-8a92-1e6861e6af27}" - /// - internal string DevicePath => ControllerDevicePath; + Marshal.FreeHGlobal(ptrBuf); + SetupDiDestroyDeviceInfoList(h); - /// The DriverKeyName may be useful as a search key - internal string DriverKeyName => ControllerDriverKeyName; + return ans; + } - /// Return the Friendly Name, such as "VIA USB Enhanced Host Controller" - internal string Name => ControllerDeviceDesc; + /// private function for finding a USB device's Instance ID + /// Device driver key name + /// Device instance ID + static string GetInstanceIdByKeyName(string driverKeyName) + { + string ans = ""; + const string DEV_ENUM = REGSTR_KEY_USB; - /// Return Root Hub for this Controller - internal UsbHub GetRootHub() + // Use the "enumerator form" of the SetupDiGetClassDevs API + // to generate a list of all USB devices + IntPtr h = SetupDiGetClassDevs(0, DEV_ENUM, IntPtr.Zero, DIGCF_PRESENT | DIGCF_ALLCLASSES); + + if(h == INVALID_HANDLE_VALUE) + return ans; + + IntPtr ptrBuf = Marshal.AllocHGlobal(BUFFER_SIZE); + + bool success; + int i = 0; + + do + { + // create a Device Interface Data structure + var da = new SpDevinfoData(); + da.cbSize = Marshal.SizeOf(da); + + // start the enumeration + success = SetupDiEnumDeviceInfo(h, i, ref da); + + if(success) { - IntPtr h, h2; + int requiredSize = 0; + int regType = REG_SZ; - var root = new UsbHub + string keyName = ""; + + if(SetupDiGetDeviceRegistryProperty(h, ref da, SPDRP_DRIVER, ref regType, ptrBuf, BUFFER_SIZE, + ref requiredSize)) + keyName = Marshal.PtrToStringAuto(ptrBuf); + + // is it a match? + if(keyName == driverKeyName) { - HubIsRootHub = true, - HubDeviceDesc = "Root Hub" - }; + const int N_BYTES = BUFFER_SIZE; + var sb = new StringBuilder(N_BYTES); + SetupDiGetDeviceInstanceId(h, ref da, sb, N_BYTES, out requiredSize); + ans = sb.ToString(); - // Open a handle to the Host Controller - h = CreateFile(ControllerDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, - IntPtr.Zero); - - if(h == INVALID_HANDLE_VALUE) - return root; - - var hubName = new UsbRootHubName(); - int nBytes = Marshal.SizeOf(hubName); - IntPtr ptrHubName = Marshal.AllocHGlobal(nBytes); - - // get the Hub Name - if(DeviceIoControl(h, IOCTL_USB_GET_ROOT_HUB_NAME, ptrHubName, nBytes, ptrHubName, nBytes, out _, - IntPtr.Zero)) - { - hubName = (UsbRootHubName)Marshal.PtrToStructure(ptrHubName, typeof(UsbRootHubName)); - root.HubDevicePath = @"\\.\" + hubName.RootHubName; + break; } + } - // TODO: Get DriverKeyName for Root Hub + i++; + } while(success); - // Now let's open the Hub (based upon the HubName we got above) - h2 = CreateFile(root.HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, - IntPtr.Zero); + Marshal.FreeHGlobal(ptrBuf); + SetupDiDestroyDeviceInfoList(h); - if(h2 != INVALID_HANDLE_VALUE) - { - var nodeInfo = new UsbNodeInformation - { - NodeType = (int)UsbHubNode.UsbHub - }; + return ans; + } - nBytes = Marshal.SizeOf(nodeInfo); - IntPtr ptrNodeInfo = Marshal.AllocHGlobal(nBytes); - Marshal.StructureToPtr(nodeInfo, ptrNodeInfo, true); + /// Represents a USB Host Controller + sealed class UsbController + { + internal string ControllerDriverKeyName, ControllerDevicePath, ControllerDeviceDesc; + internal int ControllerIndex; - // get the Hub Information - if(DeviceIoControl(h2, IOCTL_USB_GET_NODE_INFORMATION, ptrNodeInfo, nBytes, ptrNodeInfo, nBytes, - out _, IntPtr.Zero)) - { - nodeInfo = (UsbNodeInformation)Marshal.PtrToStructure(ptrNodeInfo, typeof(UsbNodeInformation)); + /// A simple default constructor + internal UsbController() + { + ControllerIndex = 0; + ControllerDevicePath = ""; + ControllerDeviceDesc = ""; + ControllerDriverKeyName = ""; + } - root.HubIsBusPowered = Convert.ToBoolean(nodeInfo.HubInformation.HubIsBusPowered); - root.HubPortCount = nodeInfo.HubInformation.HubDescriptor.bNumberOfPorts; - } + /// Return the index of the instance + internal int Index => ControllerIndex; - Marshal.FreeHGlobal(ptrNodeInfo); - CloseHandle(h2); - } + /// + /// Return the Device Path, such as "\\?\pci#ven_10de&dev_005a&subsys_815a1043&rev_a2#3&267a616a&0& + /// 58#{3abf6f2d-71c4-462a-8a92-1e6861e6af27}" + /// + internal string DevicePath => ControllerDevicePath; - Marshal.FreeHGlobal(ptrHubName); - CloseHandle(h); + /// The DriverKeyName may be useful as a search key + internal string DriverKeyName => ControllerDriverKeyName; + /// Return the Friendly Name, such as "VIA USB Enhanced Host Controller" + internal string Name => ControllerDeviceDesc; + + /// Return Root Hub for this Controller + internal UsbHub GetRootHub() + { + IntPtr h, h2; + + var root = new UsbHub + { + HubIsRootHub = true, + HubDeviceDesc = "Root Hub" + }; + + // Open a handle to the Host Controller + h = CreateFile(ControllerDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + + if(h == INVALID_HANDLE_VALUE) return root; - } - } - /// The Hub class - internal class UsbHub - { - internal string HubDriverKey, HubDevicePath, HubDeviceDesc; - internal bool HubIsBusPowered, HubIsRootHub; - internal string HubManufacturer, HubProduct, HubSerialNumber, HubInstanceId; - internal int HubPortCount; + var hubName = new UsbRootHubName(); + int nBytes = Marshal.SizeOf(hubName); + IntPtr ptrHubName = Marshal.AllocHGlobal(nBytes); - /// a simple default constructor - internal UsbHub() + // get the Hub Name + if(DeviceIoControl(h, IOCTL_USB_GET_ROOT_HUB_NAME, ptrHubName, nBytes, ptrHubName, nBytes, out _, + IntPtr.Zero)) { - HubPortCount = 0; - HubDevicePath = ""; - HubDeviceDesc = ""; - HubDriverKey = ""; - HubIsBusPowered = false; - HubIsRootHub = false; - HubManufacturer = ""; - HubProduct = ""; - HubSerialNumber = ""; - HubInstanceId = ""; + hubName = (UsbRootHubName)Marshal.PtrToStructure(ptrHubName, typeof(UsbRootHubName)); + root.HubDevicePath = @"\\.\" + hubName.RootHubName; } - /// return Port Count - internal int PortCount => HubPortCount; + // TODO: Get DriverKeyName for Root Hub - /// - /// return the Device Path, such as "\\?\pci#ven_10de&dev_005a&subsys_815a1043&rev_a2#3&267a616a&0& - /// 58#{3abf6f2d-71c4-462a-8a92-1e6861e6af27}" - /// - internal string DevicePath => HubDevicePath; + // Now let's open the Hub (based upon the HubName we got above) + h2 = CreateFile(root.HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); - /// The DriverKey may be useful as a search key - internal string DriverKey => HubDriverKey; - - /// return the Friendly Name, such as "VIA USB Enhanced Host Controller" - internal string Name => HubDeviceDesc; - - /// the device path of this device - internal string InstanceId => HubInstanceId; - - /// is is this a self-powered hub? - internal bool IsBusPowered => HubIsBusPowered; - - /// is this a root hub? - internal bool IsRootHub => HubIsRootHub; - - internal string Manufacturer => HubManufacturer; - - internal string Product => HubProduct; - - internal string SerialNumber => HubSerialNumber; - - /// return a list of the down stream ports - /// List of downstream ports - internal IEnumerable GetPorts() + if(h2 != INVALID_HANDLE_VALUE) { - List portList = new List(); - - // Open a handle to the Hub device - IntPtr h = CreateFile(HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, - IntPtr.Zero); - - if(h == INVALID_HANDLE_VALUE) - return new ReadOnlyCollection(portList); - - int nBytes = Marshal.SizeOf(typeof(UsbNodeConnectionInformationEx)); - IntPtr ptrNodeConnection = Marshal.AllocHGlobal(nBytes); - - // loop thru all of the ports on the hub - // BTW: Ports are numbered starting at 1 - for(int i = 1; i <= HubPortCount; i++) + var nodeInfo = new UsbNodeInformation { - var nodeConnection = new UsbNodeConnectionInformationEx - { - ConnectionIndex = i - }; - - Marshal.StructureToPtr(nodeConnection, ptrNodeConnection, true); - - if(!DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, ptrNodeConnection, nBytes, - ptrNodeConnection, nBytes, out _, IntPtr.Zero)) - continue; - - nodeConnection = - (UsbNodeConnectionInformationEx)Marshal.PtrToStructure(ptrNodeConnection, - typeof(UsbNodeConnectionInformationEx)); - - // load up the USBPort class - var port = new UsbPort - { - PortPortNumber = i, - PortHubDevicePath = HubDevicePath, - PortStatus = ((UsbConnectionStatus)nodeConnection.ConnectionStatus).ToString(), - PortSpeed = ((UsbDeviceSpeed)nodeConnection.Speed).ToString(), - PortIsDeviceConnected = - nodeConnection.ConnectionStatus == (int)UsbConnectionStatus.DeviceConnected, - PortIsHub = Convert.ToBoolean(nodeConnection.DeviceIsHub), - PortDeviceDescriptor = nodeConnection.DeviceDescriptor - }; - - // add it to the list - portList.Add(port); - } - - Marshal.FreeHGlobal(ptrNodeConnection); - CloseHandle(h); - - // convert it into a Collection - return new ReadOnlyCollection(portList); - } - } - - /// Represents an USB port - internal class UsbPort - { - internal UsbDeviceDescriptor PortDeviceDescriptor; - internal bool PortIsHub, PortIsDeviceConnected; - internal int PortPortNumber; - internal string PortStatus, PortHubDevicePath, PortSpeed; - - /// a simple default constructor - internal UsbPort() - { - PortPortNumber = 0; - PortStatus = ""; - PortHubDevicePath = ""; - PortSpeed = ""; - PortIsHub = false; - PortIsDeviceConnected = false; - } - - /// return Port Index of the Hub - internal int PortNumber => PortPortNumber; - - /// return the Device Path of the Hub - internal string HubDevicePath => PortHubDevicePath; - - /// the status (see USB_CONNECTION_STATUS above) - internal string Status => PortStatus; - - /// the speed of the connection (see USB_DEVICE_SPEED above) - internal string Speed => PortSpeed; - - /// is this a downstream external hub? - internal bool IsHub => PortIsHub; - - /// is anybody home? - internal bool IsDeviceConnected => PortIsDeviceConnected; - - /// return a down stream external hub - /// Downstream external hub - internal UsbDevice GetDevice() - { - if(!PortIsDeviceConnected) - return null; - - // Copy over some values from the Port class - // Ya know, I've given some thought about making Device a derived class... - var device = new UsbDevice - { - DevicePortNumber = PortPortNumber, - DeviceHubDevicePath = PortHubDevicePath, - DeviceDescriptor = PortDeviceDescriptor + NodeType = (int)UsbHubNode.UsbHub }; - // Open a handle to the Hub device - IntPtr h = CreateFile(PortHubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, - IntPtr.Zero); + nBytes = Marshal.SizeOf(nodeInfo); + IntPtr ptrNodeInfo = Marshal.AllocHGlobal(nBytes); + Marshal.StructureToPtr(nodeInfo, ptrNodeInfo, true); - if(h == INVALID_HANDLE_VALUE) - return device; - - int nBytesReturned; - int nBytes = BUFFER_SIZE; - - // We use this to zero fill a buffer - string nullString = new string((char)0, BUFFER_SIZE / Marshal.SystemDefaultCharSize); - - // The iManufacturer, iProduct and iSerialNumber entries in the - // Device Descriptor are really just indexes. So, we have to - // request a String Descriptor to get the values for those strings. - - if(PortDeviceDescriptor.iManufacturer > 0) + // get the Hub Information + if(DeviceIoControl(h2, IOCTL_USB_GET_NODE_INFORMATION, ptrNodeInfo, nBytes, ptrNodeInfo, nBytes, + out _, IntPtr.Zero)) { - // build a request for string descriptor - var request = new UsbDescriptorRequest - { - ConnectionIndex = PortPortNumber, - SetupPacket = - { - // Language Code - wIndex = 0x409, - wValue = (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iManufacturer) - } - }; + nodeInfo = (UsbNodeInformation)Marshal.PtrToStructure(ptrNodeInfo, typeof(UsbNodeInformation)); - request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(request)); - - // Geez, I wish C# had a Marshal.MemSet() method - IntPtr ptrRequest = Marshal.StringToHGlobalAuto(nullString); - Marshal.StructureToPtr(request, ptrRequest, true); - - // Use an IOCTL call to request the String Descriptor - if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, ptrRequest, - nBytes, out nBytesReturned, IntPtr.Zero)) - { - // The location of the string descriptor is immediately after - // the Request structure. Because this location is not "covered" - // by the structure allocation, we're forced to zero out this - // chunk of memory by using the StringToHGlobalAuto() hack above - var ptrStringDesc = IntPtr.Add(ptrRequest, Marshal.SizeOf(request)); - - var stringDesc = - (UsbStringDescriptor)Marshal.PtrToStructure(ptrStringDesc, typeof(UsbStringDescriptor)); - - device.DeviceManufacturer = stringDesc.bString; - } - - Marshal.FreeHGlobal(ptrRequest); + root.HubIsBusPowered = Convert.ToBoolean(nodeInfo.HubInformation.HubIsBusPowered); + root.HubPortCount = nodeInfo.HubInformation.HubDescriptor.bNumberOfPorts; } - if(PortDeviceDescriptor.iProduct > 0) + Marshal.FreeHGlobal(ptrNodeInfo); + CloseHandle(h2); + } + + Marshal.FreeHGlobal(ptrHubName); + CloseHandle(h); + + return root; + } + } + + /// The Hub class + internal class UsbHub + { + internal string HubDriverKey, HubDevicePath, HubDeviceDesc; + internal bool HubIsBusPowered, HubIsRootHub; + internal string HubManufacturer, HubProduct, HubSerialNumber, HubInstanceId; + internal int HubPortCount; + + /// a simple default constructor + internal UsbHub() + { + HubPortCount = 0; + HubDevicePath = ""; + HubDeviceDesc = ""; + HubDriverKey = ""; + HubIsBusPowered = false; + HubIsRootHub = false; + HubManufacturer = ""; + HubProduct = ""; + HubSerialNumber = ""; + HubInstanceId = ""; + } + + /// return Port Count + internal int PortCount => HubPortCount; + + /// + /// return the Device Path, such as "\\?\pci#ven_10de&dev_005a&subsys_815a1043&rev_a2#3&267a616a&0& + /// 58#{3abf6f2d-71c4-462a-8a92-1e6861e6af27}" + /// + internal string DevicePath => HubDevicePath; + + /// The DriverKey may be useful as a search key + internal string DriverKey => HubDriverKey; + + /// return the Friendly Name, such as "VIA USB Enhanced Host Controller" + internal string Name => HubDeviceDesc; + + /// the device path of this device + internal string InstanceId => HubInstanceId; + + /// is is this a self-powered hub? + internal bool IsBusPowered => HubIsBusPowered; + + /// is this a root hub? + internal bool IsRootHub => HubIsRootHub; + + internal string Manufacturer => HubManufacturer; + + internal string Product => HubProduct; + + internal string SerialNumber => HubSerialNumber; + + /// return a list of the down stream ports + /// List of downstream ports + internal IEnumerable GetPorts() + { + List portList = new List(); + + // Open a handle to the Hub device + IntPtr h = CreateFile(HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + + if(h == INVALID_HANDLE_VALUE) + return new ReadOnlyCollection(portList); + + int nBytes = Marshal.SizeOf(typeof(UsbNodeConnectionInformationEx)); + IntPtr ptrNodeConnection = Marshal.AllocHGlobal(nBytes); + + // loop thru all of the ports on the hub + // BTW: Ports are numbered starting at 1 + for(int i = 1; i <= HubPortCount; i++) + { + var nodeConnection = new UsbNodeConnectionInformationEx { - // build a request for string descriptor - var request = new UsbDescriptorRequest - { - ConnectionIndex = PortPortNumber, - SetupPacket = - { - // Language Code - wIndex = 0x409, - wValue = (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iProduct) - } - }; + ConnectionIndex = i + }; - request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(request)); + Marshal.StructureToPtr(nodeConnection, ptrNodeConnection, true); - // Geez, I wish C# had a Marshal.MemSet() method - IntPtr ptrRequest = Marshal.StringToHGlobalAuto(nullString); - Marshal.StructureToPtr(request, ptrRequest, true); + if(!DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX, ptrNodeConnection, nBytes, + ptrNodeConnection, nBytes, out _, IntPtr.Zero)) + continue; - // Use an IOCTL call to request the String Descriptor - if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, ptrRequest, - nBytes, out nBytesReturned, IntPtr.Zero)) - { - // the location of the string descriptor is immediately after the Request structure - var ptrStringDesc = IntPtr.Add(ptrRequest, Marshal.SizeOf(request)); + nodeConnection = + (UsbNodeConnectionInformationEx)Marshal.PtrToStructure(ptrNodeConnection, + typeof(UsbNodeConnectionInformationEx)); - var stringDesc = - (UsbStringDescriptor)Marshal.PtrToStructure(ptrStringDesc, typeof(UsbStringDescriptor)); - - device.DeviceProduct = stringDesc.bString; - } - - Marshal.FreeHGlobal(ptrRequest); - } - - if(PortDeviceDescriptor.iSerialNumber > 0) + // load up the USBPort class + var port = new UsbPort { - // build a request for string descriptor - var request = new UsbDescriptorRequest - { - ConnectionIndex = PortPortNumber, - SetupPacket = - { - // Language Code - wIndex = 0x409, - wValue = (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iSerialNumber) - } - }; + PortPortNumber = i, + PortHubDevicePath = HubDevicePath, + PortStatus = ((UsbConnectionStatus)nodeConnection.ConnectionStatus).ToString(), + PortSpeed = ((UsbDeviceSpeed)nodeConnection.Speed).ToString(), + PortIsDeviceConnected = + nodeConnection.ConnectionStatus == (int)UsbConnectionStatus.DeviceConnected, + PortIsHub = Convert.ToBoolean(nodeConnection.DeviceIsHub), + PortDeviceDescriptor = nodeConnection.DeviceDescriptor + }; - request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(request)); + // add it to the list + portList.Add(port); + } - // Geez, I wish C# had a Marshal.MemSet() method - IntPtr ptrRequest = Marshal.StringToHGlobalAuto(nullString); - Marshal.StructureToPtr(request, ptrRequest, true); + Marshal.FreeHGlobal(ptrNodeConnection); + CloseHandle(h); - // Use an IOCTL call to request the String Descriptor - if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, ptrRequest, - nBytes, out nBytesReturned, IntPtr.Zero)) - { - // the location of the string descriptor is immediately after the Request structure - var ptrStringDesc = IntPtr.Add(ptrRequest, Marshal.SizeOf(request)); + // convert it into a Collection + return new ReadOnlyCollection(portList); + } + } - var stringDesc = - (UsbStringDescriptor)Marshal.PtrToStructure(ptrStringDesc, typeof(UsbStringDescriptor)); + /// Represents an USB port + internal class UsbPort + { + internal UsbDeviceDescriptor PortDeviceDescriptor; + internal bool PortIsHub, PortIsDeviceConnected; + internal int PortPortNumber; + internal string PortStatus, PortHubDevicePath, PortSpeed; - device.DeviceSerialNumber = stringDesc.bString; - } + /// a simple default constructor + internal UsbPort() + { + PortPortNumber = 0; + PortStatus = ""; + PortHubDevicePath = ""; + PortSpeed = ""; + PortIsHub = false; + PortIsDeviceConnected = false; + } - Marshal.FreeHGlobal(ptrRequest); - } + /// return Port Index of the Hub + internal int PortNumber => PortPortNumber; - // build a request for configuration descriptor - var dcrRequest = new UsbDescriptorRequest + /// return the Device Path of the Hub + internal string HubDevicePath => PortHubDevicePath; + + /// the status (see USB_CONNECTION_STATUS above) + internal string Status => PortStatus; + + /// the speed of the connection (see USB_DEVICE_SPEED above) + internal string Speed => PortSpeed; + + /// is this a downstream external hub? + internal bool IsHub => PortIsHub; + + /// is anybody home? + internal bool IsDeviceConnected => PortIsDeviceConnected; + + /// return a down stream external hub + /// Downstream external hub + internal UsbDevice GetDevice() + { + if(!PortIsDeviceConnected) + return null; + + // Copy over some values from the Port class + // Ya know, I've given some thought about making Device a derived class... + var device = new UsbDevice + { + DevicePortNumber = PortPortNumber, + DeviceHubDevicePath = PortHubDevicePath, + DeviceDescriptor = PortDeviceDescriptor + }; + + // Open a handle to the Hub device + IntPtr h = CreateFile(PortHubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + + if(h == INVALID_HANDLE_VALUE) + return device; + + int nBytesReturned; + int nBytes = BUFFER_SIZE; + + // We use this to zero fill a buffer + string nullString = new string((char)0, BUFFER_SIZE / Marshal.SystemDefaultCharSize); + + // The iManufacturer, iProduct and iSerialNumber entries in the + // Device Descriptor are really just indexes. So, we have to + // request a String Descriptor to get the values for those strings. + + if(PortDeviceDescriptor.iManufacturer > 0) + { + // build a request for string descriptor + var request = new UsbDescriptorRequest { ConnectionIndex = PortPortNumber, SetupPacket = { - wIndex = 0, - wValue = USB_CONFIGURATION_DESCRIPTOR_TYPE << 8 + // Language Code + wIndex = 0x409, + wValue = (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iManufacturer) } }; - dcrRequest.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(dcrRequest)); + request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(request)); // Geez, I wish C# had a Marshal.MemSet() method - IntPtr dcrPtrRequest = Marshal.StringToHGlobalAuto(nullString); - Marshal.StructureToPtr(dcrRequest, dcrPtrRequest, true); + IntPtr ptrRequest = Marshal.StringToHGlobalAuto(nullString); + Marshal.StructureToPtr(request, ptrRequest, true); // Use an IOCTL call to request the String Descriptor - if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, dcrPtrRequest, nBytes, - dcrPtrRequest, nBytes, out nBytesReturned, IntPtr.Zero)) - { - var ptrStringDesc = IntPtr.Add(dcrPtrRequest, Marshal.SizeOf(dcrRequest)); - device.BinaryDeviceDescriptors = new byte[nBytesReturned]; - Marshal.Copy(ptrStringDesc, device.BinaryDeviceDescriptors, 0, nBytesReturned); - } - - Marshal.FreeHGlobal(dcrPtrRequest); - - // Get the Driver Key Name (usefull in locating a device) - var driverKey = new UsbNodeConnectionDriverkeyName - { - ConnectionIndex = PortPortNumber - }; - - nBytes = Marshal.SizeOf(driverKey); - IntPtr ptrDriverKey = Marshal.AllocHGlobal(nBytes); - Marshal.StructureToPtr(driverKey, ptrDriverKey, true); - - // Use an IOCTL call to request the Driver Key Name - if(DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, ptrDriverKey, nBytes, ptrDriverKey, + if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, ptrRequest, nBytes, out nBytesReturned, IntPtr.Zero)) { - driverKey = (UsbNodeConnectionDriverkeyName)Marshal.PtrToStructure(ptrDriverKey, - typeof(UsbNodeConnectionDriverkeyName)); + // The location of the string descriptor is immediately after + // the Request structure. Because this location is not "covered" + // by the structure allocation, we're forced to zero out this + // chunk of memory by using the StringToHGlobalAuto() hack above + var ptrStringDesc = IntPtr.Add(ptrRequest, Marshal.SizeOf(request)); - device.DeviceDriverKey = driverKey.DriverKeyName; + var stringDesc = + (UsbStringDescriptor)Marshal.PtrToStructure(ptrStringDesc, typeof(UsbStringDescriptor)); - // use the DriverKeyName to get the Device Description and Instance ID - device.DeviceName = GetDescriptionByKeyName(device.DeviceDriverKey); - device.DeviceInstanceId = GetInstanceIdByKeyName(device.DeviceDriverKey); + device.DeviceManufacturer = stringDesc.bString; } - Marshal.FreeHGlobal(ptrDriverKey); - CloseHandle(h); - - return device; + Marshal.FreeHGlobal(ptrRequest); } - /// return a down stream external hub - /// Downstream external hub - internal UsbHub GetHub() + if(PortDeviceDescriptor.iProduct > 0) { - if(!PortIsHub) - return null; - - var hub = new UsbHub(); - IntPtr h, h2; - hub.HubIsRootHub = false; - hub.HubDeviceDesc = "External Hub"; - - // Open a handle to the Host Controller - h = CreateFile(PortHubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, - IntPtr.Zero); - - if(h == INVALID_HANDLE_VALUE) - return hub; - - // Get the DevicePath for downstream hub - var nodeName = new UsbNodeConnectionName + // build a request for string descriptor + var request = new UsbDescriptorRequest { - ConnectionIndex = PortPortNumber + ConnectionIndex = PortPortNumber, + SetupPacket = + { + // Language Code + wIndex = 0x409, + wValue = (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iProduct) + } }; - int nBytes = Marshal.SizeOf(nodeName); - IntPtr ptrNodeName = Marshal.AllocHGlobal(nBytes); - Marshal.StructureToPtr(nodeName, ptrNodeName, true); + request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(request)); - // Use an IOCTL call to request the Node Name - if(DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_NAME, ptrNodeName, nBytes, ptrNodeName, nBytes, + // Geez, I wish C# had a Marshal.MemSet() method + IntPtr ptrRequest = Marshal.StringToHGlobalAuto(nullString); + Marshal.StructureToPtr(request, ptrRequest, true); + + // Use an IOCTL call to request the String Descriptor + if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, ptrRequest, + nBytes, out nBytesReturned, IntPtr.Zero)) + { + // the location of the string descriptor is immediately after the Request structure + var ptrStringDesc = IntPtr.Add(ptrRequest, Marshal.SizeOf(request)); + + var stringDesc = + (UsbStringDescriptor)Marshal.PtrToStructure(ptrStringDesc, typeof(UsbStringDescriptor)); + + device.DeviceProduct = stringDesc.bString; + } + + Marshal.FreeHGlobal(ptrRequest); + } + + if(PortDeviceDescriptor.iSerialNumber > 0) + { + // build a request for string descriptor + var request = new UsbDescriptorRequest + { + ConnectionIndex = PortPortNumber, + SetupPacket = + { + // Language Code + wIndex = 0x409, + wValue = (short)((USB_STRING_DESCRIPTOR_TYPE << 8) + PortDeviceDescriptor.iSerialNumber) + } + }; + + request.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(request)); + + // Geez, I wish C# had a Marshal.MemSet() method + IntPtr ptrRequest = Marshal.StringToHGlobalAuto(nullString); + Marshal.StructureToPtr(request, ptrRequest, true); + + // Use an IOCTL call to request the String Descriptor + if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, ptrRequest, nBytes, ptrRequest, + nBytes, out nBytesReturned, IntPtr.Zero)) + { + // the location of the string descriptor is immediately after the Request structure + var ptrStringDesc = IntPtr.Add(ptrRequest, Marshal.SizeOf(request)); + + var stringDesc = + (UsbStringDescriptor)Marshal.PtrToStructure(ptrStringDesc, typeof(UsbStringDescriptor)); + + device.DeviceSerialNumber = stringDesc.bString; + } + + Marshal.FreeHGlobal(ptrRequest); + } + + // build a request for configuration descriptor + var dcrRequest = new UsbDescriptorRequest + { + ConnectionIndex = PortPortNumber, + SetupPacket = + { + wIndex = 0, + wValue = USB_CONFIGURATION_DESCRIPTOR_TYPE << 8 + } + }; + + dcrRequest.SetupPacket.wLength = (short)(nBytes - Marshal.SizeOf(dcrRequest)); + + // Geez, I wish C# had a Marshal.MemSet() method + IntPtr dcrPtrRequest = Marshal.StringToHGlobalAuto(nullString); + Marshal.StructureToPtr(dcrRequest, dcrPtrRequest, true); + + // Use an IOCTL call to request the String Descriptor + if(DeviceIoControl(h, IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION, dcrPtrRequest, nBytes, + dcrPtrRequest, nBytes, out nBytesReturned, IntPtr.Zero)) + { + var ptrStringDesc = IntPtr.Add(dcrPtrRequest, Marshal.SizeOf(dcrRequest)); + device.BinaryDeviceDescriptors = new byte[nBytesReturned]; + Marshal.Copy(ptrStringDesc, device.BinaryDeviceDescriptors, 0, nBytesReturned); + } + + Marshal.FreeHGlobal(dcrPtrRequest); + + // Get the Driver Key Name (usefull in locating a device) + var driverKey = new UsbNodeConnectionDriverkeyName + { + ConnectionIndex = PortPortNumber + }; + + nBytes = Marshal.SizeOf(driverKey); + IntPtr ptrDriverKey = Marshal.AllocHGlobal(nBytes); + Marshal.StructureToPtr(driverKey, ptrDriverKey, true); + + // Use an IOCTL call to request the Driver Key Name + if(DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME, ptrDriverKey, nBytes, ptrDriverKey, + nBytes, out nBytesReturned, IntPtr.Zero)) + { + driverKey = (UsbNodeConnectionDriverkeyName)Marshal.PtrToStructure(ptrDriverKey, + typeof(UsbNodeConnectionDriverkeyName)); + + device.DeviceDriverKey = driverKey.DriverKeyName; + + // use the DriverKeyName to get the Device Description and Instance ID + device.DeviceName = GetDescriptionByKeyName(device.DeviceDriverKey); + device.DeviceInstanceId = GetInstanceIdByKeyName(device.DeviceDriverKey); + } + + Marshal.FreeHGlobal(ptrDriverKey); + CloseHandle(h); + + return device; + } + + /// return a down stream external hub + /// Downstream external hub + internal UsbHub GetHub() + { + if(!PortIsHub) + return null; + + var hub = new UsbHub(); + IntPtr h, h2; + hub.HubIsRootHub = false; + hub.HubDeviceDesc = "External Hub"; + + // Open a handle to the Host Controller + h = CreateFile(PortHubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + + if(h == INVALID_HANDLE_VALUE) + return hub; + + // Get the DevicePath for downstream hub + var nodeName = new UsbNodeConnectionName + { + ConnectionIndex = PortPortNumber + }; + + int nBytes = Marshal.SizeOf(nodeName); + IntPtr ptrNodeName = Marshal.AllocHGlobal(nBytes); + Marshal.StructureToPtr(nodeName, ptrNodeName, true); + + // Use an IOCTL call to request the Node Name + if(DeviceIoControl(h, IOCTL_USB_GET_NODE_CONNECTION_NAME, ptrNodeName, nBytes, ptrNodeName, nBytes, + out _, IntPtr.Zero)) + { + nodeName = (UsbNodeConnectionName)Marshal.PtrToStructure(ptrNodeName, + typeof(UsbNodeConnectionName)); + + hub.HubDevicePath = @"\\.\" + nodeName.NodeName; + } + + // Now let's open the Hub (based upon the HubName we got above) + h2 = CreateFile(hub.HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, + IntPtr.Zero); + + if(h2 != INVALID_HANDLE_VALUE) + { + var nodeInfo = new UsbNodeInformation + { + NodeType = (int)UsbHubNode.UsbHub + }; + + nBytes = Marshal.SizeOf(nodeInfo); + IntPtr ptrNodeInfo = Marshal.AllocHGlobal(nBytes); + Marshal.StructureToPtr(nodeInfo, ptrNodeInfo, true); + + // get the Hub Information + if(DeviceIoControl(h2, IOCTL_USB_GET_NODE_INFORMATION, ptrNodeInfo, nBytes, ptrNodeInfo, nBytes, out _, IntPtr.Zero)) { - nodeName = (UsbNodeConnectionName)Marshal.PtrToStructure(ptrNodeName, - typeof(UsbNodeConnectionName)); + nodeInfo = (UsbNodeInformation)Marshal.PtrToStructure(ptrNodeInfo, typeof(UsbNodeInformation)); - hub.HubDevicePath = @"\\.\" + nodeName.NodeName; + hub.HubIsBusPowered = Convert.ToBoolean(nodeInfo.HubInformation.HubIsBusPowered); + hub.HubPortCount = nodeInfo.HubInformation.HubDescriptor.bNumberOfPorts; } - // Now let's open the Hub (based upon the HubName we got above) - h2 = CreateFile(hub.HubDevicePath, GENERIC_WRITE, FILE_SHARE_WRITE, IntPtr.Zero, OPEN_EXISTING, 0, - IntPtr.Zero); - - if(h2 != INVALID_HANDLE_VALUE) - { - var nodeInfo = new UsbNodeInformation - { - NodeType = (int)UsbHubNode.UsbHub - }; - - nBytes = Marshal.SizeOf(nodeInfo); - IntPtr ptrNodeInfo = Marshal.AllocHGlobal(nBytes); - Marshal.StructureToPtr(nodeInfo, ptrNodeInfo, true); - - // get the Hub Information - if(DeviceIoControl(h2, IOCTL_USB_GET_NODE_INFORMATION, ptrNodeInfo, nBytes, ptrNodeInfo, nBytes, - out _, IntPtr.Zero)) - { - nodeInfo = (UsbNodeInformation)Marshal.PtrToStructure(ptrNodeInfo, typeof(UsbNodeInformation)); - - hub.HubIsBusPowered = Convert.ToBoolean(nodeInfo.HubInformation.HubIsBusPowered); - hub.HubPortCount = nodeInfo.HubInformation.HubDescriptor.bNumberOfPorts; - } - - Marshal.FreeHGlobal(ptrNodeInfo); - CloseHandle(h2); - } - - // Fill in the missing Manufacture, Product, and SerialNumber values - // values by just creating a Device instance and copying the values - UsbDevice device = GetDevice(); - hub.HubInstanceId = device.DeviceInstanceId; - hub.HubManufacturer = device.Manufacturer; - hub.HubProduct = device.Product; - hub.HubSerialNumber = device.SerialNumber; - hub.HubDriverKey = device.DriverKey; - - Marshal.FreeHGlobal(ptrNodeName); - CloseHandle(h); - - return hub; - } - } - - /// Represents an USB device - internal class UsbDevice - { - internal byte[] BinaryDeviceDescriptors; - internal UsbDeviceDescriptor DeviceDescriptor; - internal string DeviceDriverKey, DeviceHubDevicePath, DeviceInstanceId, DeviceName; - internal string DeviceManufacturer, DeviceProduct, DeviceSerialNumber; - internal int DevicePortNumber; - - /// a simple default constructor - internal UsbDevice() - { - DevicePortNumber = 0; - DeviceHubDevicePath = ""; - DeviceDriverKey = ""; - DeviceManufacturer = ""; - DeviceProduct = "Unknown USB Device"; - DeviceSerialNumber = ""; - DeviceName = ""; - DeviceInstanceId = ""; - BinaryDeviceDescriptors = null; + Marshal.FreeHGlobal(ptrNodeInfo); + CloseHandle(h2); } - /// return Port Index of the Hub - internal int PortNumber => DevicePortNumber; + // Fill in the missing Manufacture, Product, and SerialNumber values + // values by just creating a Device instance and copying the values + UsbDevice device = GetDevice(); + hub.HubInstanceId = device.DeviceInstanceId; + hub.HubManufacturer = device.Manufacturer; + hub.HubProduct = device.Product; + hub.HubSerialNumber = device.SerialNumber; + hub.HubDriverKey = device.DriverKey; - /// return the Device Path of the Hub (the parent device) - internal string HubDevicePath => DeviceHubDevicePath; + Marshal.FreeHGlobal(ptrNodeName); + CloseHandle(h); - /// useful as a search key - internal string DriverKey => DeviceDriverKey; - - /// the device path of this device - internal string InstanceId => DeviceInstanceId; - - /// the friendly name - internal string Name => DeviceName; - - internal string Manufacturer => DeviceManufacturer; - - internal string Product => DeviceProduct; - - internal string SerialNumber => DeviceSerialNumber; - - internal byte[] BinaryDescriptors => BinaryDeviceDescriptors; + return hub; } - - #region "API Region" - // ********************** Constants ************************ - - const int GENERIC_WRITE = 0x40000000; - const int FILE_SHARE_READ = 0x1; - const int FILE_SHARE_WRITE = 0x2; - const int OPEN_EXISTING = 0x3; - static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); - - const int IOCTL_GET_HCD_DRIVERKEY_NAME = 0x220424; - const int IOCTL_USB_GET_ROOT_HUB_NAME = 0x220408; - const int IOCTL_USB_GET_NODE_INFORMATION = 0x220408; // same as above... strange, eh? - const int IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX = 0x220448; - const int IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = 0x220410; - const int IOCTL_USB_GET_NODE_CONNECTION_NAME = 0x220414; - const int IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME = 0x220420; - - const int USB_DEVICE_DESCRIPTOR_TYPE = 0x1; - const int USB_CONFIGURATION_DESCRIPTOR_TYPE = 0x2; - const int USB_STRING_DESCRIPTOR_TYPE = 0x3; - - const int BUFFER_SIZE = 2048; - const int MAXIMUM_USB_STRING_LENGTH = 255; - - const string GUID_DEVINTERFACE_HUBCONTROLLER = "3abf6f2d-71c4-462a-8a92-1e6861e6af27"; - const string REGSTR_KEY_USB = "USB"; - const int DIGCF_PRESENT = 0x2; - const int DIGCF_ALLCLASSES = 0x4; - const int DIGCF_DEVICEINTERFACE = 0x10; - const int SPDRP_DRIVER = 0x9; - const int SPDRP_DEVICEDESC = 0x0; - const int REG_SZ = 1; - - // ********************** Enumerations ************************ - - enum UsbHubNode - { - UsbHub, UsbMiParent - } - - enum UsbConnectionStatus - { - NoDeviceConnected, DeviceConnected, DeviceFailedEnumeration, - DeviceGeneralFailure, DeviceCausedOvercurrent, DeviceNotEnoughPower, - DeviceNotEnoughBandwidth, DeviceHubNestedTooDeeply, DeviceInLegacyHub - } - - enum UsbDeviceSpeed : byte - { - UsbLowSpeed, UsbFullSpeed, UsbHighSpeed - } - - // ********************** Stuctures ************************ - - [StructLayout(LayoutKind.Sequential)] - struct SpDevinfoData - { - internal int cbSize; - internal readonly Guid ClassGuid; - internal readonly IntPtr DevInst; - internal readonly IntPtr Reserved; - } - - [StructLayout(LayoutKind.Sequential)] - struct SpDeviceInterfaceData - { - internal int cbSize; - internal readonly Guid InterfaceClassGuid; - internal readonly int Flags; - internal readonly IntPtr Reserved; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - struct SpDeviceInterfaceDetailData - { - internal int cbSize; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] - internal readonly string DevicePath; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - struct UsbHcdDriverkeyName - { - internal readonly int ActualLength; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] - internal readonly string DriverKeyName; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - struct UsbRootHubName - { - internal readonly int ActualLength; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] - internal readonly string RootHubName; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct UsbHubDescriptor - { - internal readonly byte bDescriptorLength; - internal readonly byte bDescriptorType; - internal readonly byte bNumberOfPorts; - internal readonly short wHubCharacteristics; - internal readonly byte bPowerOnToPowerGood; - internal readonly byte bHubControlCurrent; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - internal readonly byte[] bRemoveAndPowerMask; - } - - [StructLayout(LayoutKind.Sequential)] - struct UsbHubInformation - { - internal readonly UsbHubDescriptor HubDescriptor; - internal readonly byte HubIsBusPowered; - } - - [StructLayout(LayoutKind.Sequential)] - struct UsbNodeInformation - { - internal int NodeType; - internal readonly UsbHubInformation HubInformation; // Yeah, I'm assuming we'll just use the first form - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct UsbNodeConnectionInformationEx - { - internal int ConnectionIndex; - internal readonly UsbDeviceDescriptor DeviceDescriptor; - internal readonly byte CurrentConfigurationValue; - internal readonly byte Speed; - internal readonly byte DeviceIsHub; - internal readonly short DeviceAddress; - internal readonly int NumberOfOpenPipes; - - internal readonly int ConnectionStatus; - - //internal IntPtr PipeList; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] - internal struct UsbDeviceDescriptor - { - internal byte bLength; - internal byte bDescriptorType; - internal short bcdUSB; - internal byte bDeviceClass; - internal byte bDeviceSubClass; - internal byte bDeviceProtocol; - internal byte bMaxPacketSize0; - internal short idVendor; - internal short idProduct; - internal short bcdDevice; - internal byte iManufacturer; - internal byte iProduct; - internal byte iSerialNumber; - internal byte bNumConfigurations; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - struct UsbStringDescriptor - { - internal readonly byte bLength; - internal readonly byte bDescriptorType; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXIMUM_USB_STRING_LENGTH)] - internal readonly string bString; - } - - [StructLayout(LayoutKind.Sequential)] - struct UsbSetupPacket - { - internal readonly byte bmRequest; - internal readonly byte bRequest; - internal short wValue; - internal short wIndex; - internal short wLength; - } - - [StructLayout(LayoutKind.Sequential)] - struct UsbDescriptorRequest - { - internal int ConnectionIndex; - - internal UsbSetupPacket SetupPacket; - - //internal byte[] Data; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - struct UsbNodeConnectionName - { - internal int ConnectionIndex; - internal readonly int ActualLength; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] - internal readonly string NodeName; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - struct UsbNodeConnectionDriverkeyName // Yes, this is the same as the structure above... - { - internal int ConnectionIndex; - internal readonly int ActualLength; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] - internal readonly string DriverKeyName; - } - - // ********************** API Definitions ************************ - - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] - static extern IntPtr SetupDiGetClassDevs( // 1st form using a ClassGUID - ref Guid classGuid, int enumerator, IntPtr hwndParent, int flags); - - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] // 2nd form uses an Enumerator - static extern IntPtr SetupDiGetClassDevs(int classGuid, string enumerator, IntPtr hwndParent, int flags); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, IntPtr deviceInfoData, - ref Guid interfaceClassGuid, int memberIndex, - ref SpDeviceInterfaceData deviceInterfaceData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, - ref SpDeviceInterfaceData deviceInterfaceData, - ref SpDeviceInterfaceDetailData deviceInterfaceDetailData, - int deviceInterfaceDetailDataSize, ref int requiredSize, - ref SpDevinfoData deviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, ref SpDevinfoData deviceInfoData, - int iProperty, ref int propertyRegDataType, - IntPtr propertyBuffer, int propertyBufferSize, - ref int requiredSize); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet, int memberIndex, - ref SpDevinfoData deviceInfoData); - - [DllImport("setupapi.dll", SetLastError = true)] - static extern bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); - - [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern bool SetupDiGetDeviceInstanceId(IntPtr deviceInfoSet, ref SpDevinfoData deviceInfoData, - StringBuilder deviceInstanceId, int deviceInstanceIdSize, - out int requiredSize); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize, - IntPtr lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, - IntPtr lpOverlapped); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, - IntPtr lpSecurityAttributes, int dwCreationDisposition, - int dwFlagsAndAttributes, IntPtr hTemplateFile); - - [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] - static extern bool CloseHandle(IntPtr hObject); - #endregion } + + /// Represents an USB device + internal class UsbDevice + { + internal byte[] BinaryDeviceDescriptors; + internal UsbDeviceDescriptor DeviceDescriptor; + internal string DeviceDriverKey, DeviceHubDevicePath, DeviceInstanceId, DeviceName; + internal string DeviceManufacturer, DeviceProduct, DeviceSerialNumber; + internal int DevicePortNumber; + + /// a simple default constructor + internal UsbDevice() + { + DevicePortNumber = 0; + DeviceHubDevicePath = ""; + DeviceDriverKey = ""; + DeviceManufacturer = ""; + DeviceProduct = "Unknown USB Device"; + DeviceSerialNumber = ""; + DeviceName = ""; + DeviceInstanceId = ""; + BinaryDeviceDescriptors = null; + } + + /// return Port Index of the Hub + internal int PortNumber => DevicePortNumber; + + /// return the Device Path of the Hub (the parent device) + internal string HubDevicePath => DeviceHubDevicePath; + + /// useful as a search key + internal string DriverKey => DeviceDriverKey; + + /// the device path of this device + internal string InstanceId => DeviceInstanceId; + + /// the friendly name + internal string Name => DeviceName; + + internal string Manufacturer => DeviceManufacturer; + + internal string Product => DeviceProduct; + + internal string SerialNumber => DeviceSerialNumber; + + internal byte[] BinaryDescriptors => BinaryDeviceDescriptors; + } + + #region "API Region" + // ********************** Constants ************************ + + const int GENERIC_WRITE = 0x40000000; + const int FILE_SHARE_READ = 0x1; + const int FILE_SHARE_WRITE = 0x2; + const int OPEN_EXISTING = 0x3; + static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + + const int IOCTL_GET_HCD_DRIVERKEY_NAME = 0x220424; + const int IOCTL_USB_GET_ROOT_HUB_NAME = 0x220408; + const int IOCTL_USB_GET_NODE_INFORMATION = 0x220408; // same as above... strange, eh? + const int IOCTL_USB_GET_NODE_CONNECTION_INFORMATION_EX = 0x220448; + const int IOCTL_USB_GET_DESCRIPTOR_FROM_NODE_CONNECTION = 0x220410; + const int IOCTL_USB_GET_NODE_CONNECTION_NAME = 0x220414; + const int IOCTL_USB_GET_NODE_CONNECTION_DRIVERKEY_NAME = 0x220420; + + const int USB_DEVICE_DESCRIPTOR_TYPE = 0x1; + const int USB_CONFIGURATION_DESCRIPTOR_TYPE = 0x2; + const int USB_STRING_DESCRIPTOR_TYPE = 0x3; + + const int BUFFER_SIZE = 2048; + const int MAXIMUM_USB_STRING_LENGTH = 255; + + const string GUID_DEVINTERFACE_HUBCONTROLLER = "3abf6f2d-71c4-462a-8a92-1e6861e6af27"; + const string REGSTR_KEY_USB = "USB"; + const int DIGCF_PRESENT = 0x2; + const int DIGCF_ALLCLASSES = 0x4; + const int DIGCF_DEVICEINTERFACE = 0x10; + const int SPDRP_DRIVER = 0x9; + const int SPDRP_DEVICEDESC = 0x0; + const int REG_SZ = 1; + + // ********************** Enumerations ************************ + + enum UsbHubNode + { + UsbHub, UsbMiParent + } + + enum UsbConnectionStatus + { + NoDeviceConnected, DeviceConnected, DeviceFailedEnumeration, + DeviceGeneralFailure, DeviceCausedOvercurrent, DeviceNotEnoughPower, + DeviceNotEnoughBandwidth, DeviceHubNestedTooDeeply, DeviceInLegacyHub + } + + enum UsbDeviceSpeed : byte + { + UsbLowSpeed, UsbFullSpeed, UsbHighSpeed + } + + // ********************** Stuctures ************************ + + [StructLayout(LayoutKind.Sequential)] + struct SpDevinfoData + { + internal int cbSize; + internal readonly Guid ClassGuid; + internal readonly IntPtr DevInst; + internal readonly IntPtr Reserved; + } + + [StructLayout(LayoutKind.Sequential)] + struct SpDeviceInterfaceData + { + internal int cbSize; + internal readonly Guid InterfaceClassGuid; + internal readonly int Flags; + internal readonly IntPtr Reserved; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct SpDeviceInterfaceDetailData + { + internal int cbSize; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] + internal readonly string DevicePath; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct UsbHcdDriverkeyName + { + internal readonly int ActualLength; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] + internal readonly string DriverKeyName; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct UsbRootHubName + { + internal readonly int ActualLength; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] + internal readonly string RootHubName; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct UsbHubDescriptor + { + internal readonly byte bDescriptorLength; + internal readonly byte bDescriptorType; + internal readonly byte bNumberOfPorts; + internal readonly short wHubCharacteristics; + internal readonly byte bPowerOnToPowerGood; + internal readonly byte bHubControlCurrent; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + internal readonly byte[] bRemoveAndPowerMask; + } + + [StructLayout(LayoutKind.Sequential)] + struct UsbHubInformation + { + internal readonly UsbHubDescriptor HubDescriptor; + internal readonly byte HubIsBusPowered; + } + + [StructLayout(LayoutKind.Sequential)] + struct UsbNodeInformation + { + internal int NodeType; + internal readonly UsbHubInformation HubInformation; // Yeah, I'm assuming we'll just use the first form + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct UsbNodeConnectionInformationEx + { + internal int ConnectionIndex; + internal readonly UsbDeviceDescriptor DeviceDescriptor; + internal readonly byte CurrentConfigurationValue; + internal readonly byte Speed; + internal readonly byte DeviceIsHub; + internal readonly short DeviceAddress; + internal readonly int NumberOfOpenPipes; + + internal readonly int ConnectionStatus; + + //internal IntPtr PipeList; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "MemberCanBePrivate.Global")] + internal struct UsbDeviceDescriptor + { + internal byte bLength; + internal byte bDescriptorType; + internal short bcdUSB; + internal byte bDeviceClass; + internal byte bDeviceSubClass; + internal byte bDeviceProtocol; + internal byte bMaxPacketSize0; + internal short idVendor; + internal short idProduct; + internal short bcdDevice; + internal byte iManufacturer; + internal byte iProduct; + internal byte iSerialNumber; + internal byte bNumConfigurations; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct UsbStringDescriptor + { + internal readonly byte bLength; + internal readonly byte bDescriptorType; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = MAXIMUM_USB_STRING_LENGTH)] + internal readonly string bString; + } + + [StructLayout(LayoutKind.Sequential)] + struct UsbSetupPacket + { + internal readonly byte bmRequest; + internal readonly byte bRequest; + internal short wValue; + internal short wIndex; + internal short wLength; + } + + [StructLayout(LayoutKind.Sequential)] + struct UsbDescriptorRequest + { + internal int ConnectionIndex; + + internal UsbSetupPacket SetupPacket; + + //internal byte[] Data; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct UsbNodeConnectionName + { + internal int ConnectionIndex; + internal readonly int ActualLength; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] + internal readonly string NodeName; + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] + struct UsbNodeConnectionDriverkeyName // Yes, this is the same as the structure above... + { + internal int ConnectionIndex; + internal readonly int ActualLength; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = BUFFER_SIZE)] + internal readonly string DriverKeyName; + } + + // ********************** API Definitions ************************ + + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + static extern IntPtr SetupDiGetClassDevs( // 1st form using a ClassGUID + ref Guid classGuid, int enumerator, IntPtr hwndParent, int flags); + + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] // 2nd form uses an Enumerator + static extern IntPtr SetupDiGetClassDevs(int classGuid, string enumerator, IntPtr hwndParent, int flags); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiEnumDeviceInterfaces(IntPtr deviceInfoSet, IntPtr deviceInfoData, + ref Guid interfaceClassGuid, int memberIndex, + ref SpDeviceInterfaceData deviceInterfaceData); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiGetDeviceInterfaceDetail(IntPtr deviceInfoSet, + ref SpDeviceInterfaceData deviceInterfaceData, + ref SpDeviceInterfaceDetailData deviceInterfaceDetailData, + int deviceInterfaceDetailDataSize, ref int requiredSize, + ref SpDevinfoData deviceInfoData); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiGetDeviceRegistryProperty(IntPtr deviceInfoSet, ref SpDevinfoData deviceInfoData, + int iProperty, ref int propertyRegDataType, + IntPtr propertyBuffer, int propertyBufferSize, + ref int requiredSize); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiEnumDeviceInfo(IntPtr deviceInfoSet, int memberIndex, + ref SpDevinfoData deviceInfoData); + + [DllImport("setupapi.dll", SetLastError = true)] + static extern bool SetupDiDestroyDeviceInfoList(IntPtr deviceInfoSet); + + [DllImport("setupapi.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool SetupDiGetDeviceInstanceId(IntPtr deviceInfoSet, ref SpDevinfoData deviceInfoData, + StringBuilder deviceInstanceId, int deviceInstanceIdSize, + out int requiredSize); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool DeviceIoControl(IntPtr hDevice, int dwIoControlCode, IntPtr lpInBuffer, int nInBufferSize, + IntPtr lpOutBuffer, int nOutBufferSize, out int lpBytesReturned, + IntPtr lpOverlapped); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, + IntPtr lpSecurityAttributes, int dwCreationDisposition, + int dwFlagsAndAttributes, IntPtr hTemplateFile); + + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] + static extern bool CloseHandle(IntPtr hObject); + #endregion } \ No newline at end of file diff --git a/Aaru.Devices/Windows/UsbFunctions.cs b/Aaru.Devices/Windows/UsbFunctions.cs index a455c64e1..a62500e5b 100644 --- a/Aaru.Devices/Windows/UsbFunctions.cs +++ b/Aaru.Devices/Windows/UsbFunctions.cs @@ -35,289 +35,288 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; -namespace Aaru.Devices.Windows +namespace Aaru.Devices.Windows; + +// +// A place for "higher level" related functions +// You might not want to keep these in the USB class... your choice +// +// TODO: Even after cleaning, refactoring and xml-documenting, this code needs some love +/// Implements functions for getting and accessing information from the USB bus +internal static partial class Usb { - // - // A place for "higher level" related functions - // You might not want to keep these in the USB class... your choice - // - // TODO: Even after cleaning, refactoring and xml-documenting, this code needs some love - /// Implements functions for getting and accessing information from the USB bus - internal static partial class Usb + const int IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080; + internal const string GuidDevinterfaceDisk = "53f56307-b6bf-11d0-94f2-00a0c91efb8b"; + internal const string GuidDevinterfaceCdrom = "53f56308-b6bf-11d0-94f2-00a0c91efb8b"; + internal const string GuidDevinterfaceFloppy = "53f56311-b6bf-11d0-94f2-00a0c91efb8b"; + internal const string GuidDevinterfaceTape = "53f5630b-b6bf-11d0-94f2-00a0c91efb8b"; + + /// Get a list of all connected devices + /// List of usb devices + internal static List GetConnectedDevices() { - const int IOCTL_STORAGE_GET_DEVICE_NUMBER = 0x2D1080; - internal const string GuidDevinterfaceDisk = "53f56307-b6bf-11d0-94f2-00a0c91efb8b"; - internal const string GuidDevinterfaceCdrom = "53f56308-b6bf-11d0-94f2-00a0c91efb8b"; - internal const string GuidDevinterfaceFloppy = "53f56311-b6bf-11d0-94f2-00a0c91efb8b"; - internal const string GuidDevinterfaceTape = "53f5630b-b6bf-11d0-94f2-00a0c91efb8b"; + List devList = new List(); - /// Get a list of all connected devices - /// List of usb devices - internal static List GetConnectedDevices() - { - List devList = new List(); + foreach(UsbController controller in GetHostControllers()) + ListHub(controller.GetRootHub(), devList); - foreach(UsbController controller in GetHostControllers()) - ListHub(controller.GetRootHub(), devList); + return devList; + } - return devList; - } - - /// private routine for enumerating a hub - /// Hub - /// Device list - static void ListHub(UsbHub hub, ICollection devList) - { - foreach(UsbPort port in hub.GetPorts()) - if(port.IsHub) - { - ListHub(port.GetHub(), devList); - } - else - { - if(port.IsDeviceConnected) - devList.Add(port.GetDevice()); - } - } - - /// Find a device based upon it's DriverKeyName - /// DriverKeyName - /// USB device - internal static UsbDevice FindDeviceByDriverKeyName(string driverKeyName) - { - UsbDevice foundDevice = null; - - foreach(UsbController controller in GetHostControllers()) + /// private routine for enumerating a hub + /// Hub + /// Device list + static void ListHub(UsbHub hub, ICollection devList) + { + foreach(UsbPort port in hub.GetPorts()) + if(port.IsHub) { - SearchHubDriverKeyName(controller.GetRootHub(), ref foundDevice, driverKeyName); - - if(foundDevice != null) - break; + ListHub(port.GetHub(), devList); } - - return foundDevice; - } - - /// Finds a device connected to a specified hub by it's DriverKeyName - /// Hub - /// UsbDevice - /// DriverKeyName - static void SearchHubDriverKeyName(UsbHub hub, ref UsbDevice foundDevice, string driverKeyName) - { - foreach(UsbPort port in hub.GetPorts()) - if(port.IsHub) - { - SearchHubDriverKeyName(port.GetHub(), ref foundDevice, driverKeyName); - } - else - { - if(!port.IsDeviceConnected) - continue; - - UsbDevice device = port.GetDevice(); - - if(device.DeviceDriverKey != driverKeyName) - continue; - - foundDevice = device; - - break; - } - } - - /// Find a device based upon it's Instance ID - /// Device instance ID - /// USB device - static UsbDevice FindDeviceByInstanceId(string instanceId) - { - UsbDevice foundDevice = null; - - foreach(UsbController controller in GetHostControllers()) + else { - SearchHubInstanceId(controller.GetRootHub(), ref foundDevice, instanceId); - - if(foundDevice != null) - break; + if(port.IsDeviceConnected) + devList.Add(port.GetDevice()); } + } - return foundDevice; + /// Find a device based upon it's DriverKeyName + /// DriverKeyName + /// USB device + internal static UsbDevice FindDeviceByDriverKeyName(string driverKeyName) + { + UsbDevice foundDevice = null; + + foreach(UsbController controller in GetHostControllers()) + { + SearchHubDriverKeyName(controller.GetRootHub(), ref foundDevice, driverKeyName); + + if(foundDevice != null) + break; } - /// private routine for enumerating a hub - /// Hub - /// USB device - /// Device instance ID - static void SearchHubInstanceId(UsbHub hub, ref UsbDevice foundDevice, string instanceId) - { - foreach(UsbPort port in hub.GetPorts()) - if(port.IsHub) - { - SearchHubInstanceId(port.GetHub(), ref foundDevice, instanceId); - } - else - { - if(!port.IsDeviceConnected) - continue; + return foundDevice; + } - UsbDevice device = port.GetDevice(); - - if(device.InstanceId != instanceId) - continue; - - foundDevice = device; - - break; - } - } - - [DllImport("setupapi.dll")] - static extern int CM_Get_Parent(out IntPtr pdnDevInst, IntPtr dnDevInst, int ulFlags); - - [DllImport("setupapi.dll", CharSet = CharSet.Auto)] - static extern int CM_Get_Device_ID(IntPtr dnDevInst, IntPtr buffer, int bufferLen, int ulFlags); - - /// Find a device based upon a Drive Letter - /// Drive letter - /// Device GUID - /// USB device - internal static UsbDevice FindDriveLetter(string driveLetter, string deviceGuid) - { - // We start by getting the unique DeviceNumber of the given - // DriveLetter. We'll use this later to find a matching - // DevicePath "symbolic name" - int devNum = GetDeviceNumber(@"\\.\" + driveLetter.TrimEnd('\\')); - - return devNum < 0 ? null : FindDeviceNumber(devNum, deviceGuid); - } - - /// Find a device based upon a Drive Path - /// Drive path - /// Device GUID - /// USB device - internal static UsbDevice FindDrivePath(string drivePath, string deviceGuid) - { - // We start by getting the unique DeviceNumber of the given - // DriveLetter. We'll use this later to find a matching - // DevicePath "symbolic name" - int devNum = GetDeviceNumber(drivePath); - - return devNum < 0 ? null : FindDeviceNumber(devNum, deviceGuid); - } - - /// Find a device based upon a Device Number - /// Device Number - /// Device GUID - /// USB device - static UsbDevice FindDeviceNumber(int devNum, string deviceGuid) - { - UsbDevice foundDevice = null; - string instanceId = ""; - - var diskGuid = new Guid(deviceGuid); - - // We start at the "root" of the device tree and look for all - // devices that match the interface GUID of a disk - IntPtr h = SetupDiGetClassDevs(ref diskGuid, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); - - if(h != INVALID_HANDLE_VALUE) + /// Finds a device connected to a specified hub by it's DriverKeyName + /// Hub + /// UsbDevice + /// DriverKeyName + static void SearchHubDriverKeyName(UsbHub hub, ref UsbDevice foundDevice, string driverKeyName) + { + foreach(UsbPort port in hub.GetPorts()) + if(port.IsHub) { - bool success; - int i = 0; + SearchHubDriverKeyName(port.GetHub(), ref foundDevice, driverKeyName); + } + else + { + if(!port.IsDeviceConnected) + continue; - do + UsbDevice device = port.GetDevice(); + + if(device.DeviceDriverKey != driverKeyName) + continue; + + foundDevice = device; + + break; + } + } + + /// Find a device based upon it's Instance ID + /// Device instance ID + /// USB device + static UsbDevice FindDeviceByInstanceId(string instanceId) + { + UsbDevice foundDevice = null; + + foreach(UsbController controller in GetHostControllers()) + { + SearchHubInstanceId(controller.GetRootHub(), ref foundDevice, instanceId); + + if(foundDevice != null) + break; + } + + return foundDevice; + } + + /// private routine for enumerating a hub + /// Hub + /// USB device + /// Device instance ID + static void SearchHubInstanceId(UsbHub hub, ref UsbDevice foundDevice, string instanceId) + { + foreach(UsbPort port in hub.GetPorts()) + if(port.IsHub) + { + SearchHubInstanceId(port.GetHub(), ref foundDevice, instanceId); + } + else + { + if(!port.IsDeviceConnected) + continue; + + UsbDevice device = port.GetDevice(); + + if(device.InstanceId != instanceId) + continue; + + foundDevice = device; + + break; + } + } + + [DllImport("setupapi.dll")] + static extern int CM_Get_Parent(out IntPtr pdnDevInst, IntPtr dnDevInst, int ulFlags); + + [DllImport("setupapi.dll", CharSet = CharSet.Auto)] + static extern int CM_Get_Device_ID(IntPtr dnDevInst, IntPtr buffer, int bufferLen, int ulFlags); + + /// Find a device based upon a Drive Letter + /// Drive letter + /// Device GUID + /// USB device + internal static UsbDevice FindDriveLetter(string driveLetter, string deviceGuid) + { + // We start by getting the unique DeviceNumber of the given + // DriveLetter. We'll use this later to find a matching + // DevicePath "symbolic name" + int devNum = GetDeviceNumber(@"\\.\" + driveLetter.TrimEnd('\\')); + + return devNum < 0 ? null : FindDeviceNumber(devNum, deviceGuid); + } + + /// Find a device based upon a Drive Path + /// Drive path + /// Device GUID + /// USB device + internal static UsbDevice FindDrivePath(string drivePath, string deviceGuid) + { + // We start by getting the unique DeviceNumber of the given + // DriveLetter. We'll use this later to find a matching + // DevicePath "symbolic name" + int devNum = GetDeviceNumber(drivePath); + + return devNum < 0 ? null : FindDeviceNumber(devNum, deviceGuid); + } + + /// Find a device based upon a Device Number + /// Device Number + /// Device GUID + /// USB device + static UsbDevice FindDeviceNumber(int devNum, string deviceGuid) + { + UsbDevice foundDevice = null; + string instanceId = ""; + + var diskGuid = new Guid(deviceGuid); + + // We start at the "root" of the device tree and look for all + // devices that match the interface GUID of a disk + IntPtr h = SetupDiGetClassDevs(ref diskGuid, 0, IntPtr.Zero, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE); + + if(h != INVALID_HANDLE_VALUE) + { + bool success; + int i = 0; + + do + { + // create a Device Interface Data structure + var dia = new SpDeviceInterfaceData(); + dia.cbSize = Marshal.SizeOf(dia); + + // start the enumeration + success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref diskGuid, i, ref dia); + + if(success) { - // create a Device Interface Data structure - var dia = new SpDeviceInterfaceData(); - dia.cbSize = Marshal.SizeOf(dia); + // build a DevInfo Data structure + var da = new SpDevinfoData(); + da.cbSize = Marshal.SizeOf(da); - // start the enumeration - success = SetupDiEnumDeviceInterfaces(h, IntPtr.Zero, ref diskGuid, i, ref dia); - - if(success) + // build a Device Interface Detail Data structure + var didd = new SpDeviceInterfaceDetailData { - // build a DevInfo Data structure - var da = new SpDevinfoData(); - da.cbSize = Marshal.SizeOf(da); + cbSize = 4 + Marshal.SystemDefaultCharSize + }; // trust me :) - // build a Device Interface Detail Data structure - var didd = new SpDeviceInterfaceDetailData + // now we can get some more detailed information + int nRequiredSize = 0; + const int N_BYTES = BUFFER_SIZE; + + if(SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, N_BYTES, ref nRequiredSize, ref da)) + if(GetDeviceNumber(didd.DevicePath) == devNum) { - cbSize = 4 + Marshal.SystemDefaultCharSize - }; // trust me :) + // current InstanceID is at the "USBSTOR" level, so we + // need up "move up" one level to get to the "USB" level + CM_Get_Parent(out IntPtr ptrPrevious, da.DevInst, 0); - // now we can get some more detailed information - int nRequiredSize = 0; - const int N_BYTES = BUFFER_SIZE; + // Now we get the InstanceID of the USB level device + IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(N_BYTES); + CM_Get_Device_ID(ptrPrevious, ptrInstanceBuf, N_BYTES, 0); + instanceId = Marshal.PtrToStringAuto(ptrInstanceBuf); - if(SetupDiGetDeviceInterfaceDetail(h, ref dia, ref didd, N_BYTES, ref nRequiredSize, ref da)) - if(GetDeviceNumber(didd.DevicePath) == devNum) - { - // current InstanceID is at the "USBSTOR" level, so we - // need up "move up" one level to get to the "USB" level - CM_Get_Parent(out IntPtr ptrPrevious, da.DevInst, 0); + Marshal.FreeHGlobal(ptrInstanceBuf); - // Now we get the InstanceID of the USB level device - IntPtr ptrInstanceBuf = Marshal.AllocHGlobal(N_BYTES); - CM_Get_Device_ID(ptrPrevious, ptrInstanceBuf, N_BYTES, 0); - instanceId = Marshal.PtrToStringAuto(ptrInstanceBuf); + //System.Console.WriteLine("InstanceId: {0}", instanceId); + //break; + } + } - Marshal.FreeHGlobal(ptrInstanceBuf); + i++; + } while(success); - //System.Console.WriteLine("InstanceId: {0}", instanceId); - //break; - } - } - - i++; - } while(success); - - SetupDiDestroyDeviceInfoList(h); - } - - // Did we find an InterfaceID of a USB device? - if(instanceId?.StartsWith("USB\\", StringComparison.Ordinal) == true) - foundDevice = FindDeviceByInstanceId(instanceId); - - return foundDevice; + SetupDiDestroyDeviceInfoList(h); } - /// return a unique device number for the given device path - /// Device path - /// Device number - static int GetDeviceNumber(string devicePath) - { - int ans = -1; + // Did we find an InterfaceID of a USB device? + if(instanceId?.StartsWith("USB\\", StringComparison.Ordinal) == true) + foundDevice = FindDeviceByInstanceId(instanceId); - IntPtr h = CreateFile(devicePath.TrimEnd('\\'), 0, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); + return foundDevice; + } - if(h == INVALID_HANDLE_VALUE) - return ans; + /// return a unique device number for the given device path + /// Device path + /// Device number + static int GetDeviceNumber(string devicePath) + { + int ans = -1; - var sdn = new StorageDeviceNumber(); - int nBytes = Marshal.SizeOf(sdn); - IntPtr ptrSdn = Marshal.AllocHGlobal(nBytes); - - if(DeviceIoControl(h, IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, 0, ptrSdn, nBytes, out _, IntPtr.Zero)) - { - sdn = (StorageDeviceNumber)Marshal.PtrToStructure(ptrSdn, typeof(StorageDeviceNumber)); - - // just my way of combining the relevant parts of the - // STORAGE_DEVICE_NUMBER into a single number - ans = (sdn.DeviceType << 8) + sdn.DeviceNumber; - } - - Marshal.FreeHGlobal(ptrSdn); - CloseHandle(h); + IntPtr h = CreateFile(devicePath.TrimEnd('\\'), 0, 0, IntPtr.Zero, OPEN_EXISTING, 0, IntPtr.Zero); + if(h == INVALID_HANDLE_VALUE) return ans; + + var sdn = new StorageDeviceNumber(); + int nBytes = Marshal.SizeOf(sdn); + IntPtr ptrSdn = Marshal.AllocHGlobal(nBytes); + + if(DeviceIoControl(h, IOCTL_STORAGE_GET_DEVICE_NUMBER, IntPtr.Zero, 0, ptrSdn, nBytes, out _, IntPtr.Zero)) + { + sdn = (StorageDeviceNumber)Marshal.PtrToStructure(ptrSdn, typeof(StorageDeviceNumber)); + + // just my way of combining the relevant parts of the + // STORAGE_DEVICE_NUMBER into a single number + ans = (sdn.DeviceType << 8) + sdn.DeviceNumber; } - [StructLayout(LayoutKind.Sequential)] - readonly struct StorageDeviceNumber - { - internal readonly int DeviceType; - internal readonly int DeviceNumber; - internal readonly int PartitionNumber; - } + Marshal.FreeHGlobal(ptrSdn); + CloseHandle(h); + + return ans; + } + + [StructLayout(LayoutKind.Sequential)] + readonly struct StorageDeviceNumber + { + internal readonly int DeviceType; + internal readonly int DeviceNumber; + internal readonly int PartitionNumber; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AODOS.cs b/Aaru.Filesystems/AODOS.cs index 8c8e0d606..1e41e5683 100644 --- a/Aaru.Filesystems/AODOS.cs +++ b/Aaru.Filesystems/AODOS.cs @@ -41,113 +41,112 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information has been extracted looking at available disk images +// This may be missing fields, or not, I don't know russian so any help is appreciated +/// +/// Implements detection of the AO-DOS filesystem +public sealed class AODOS : IFilesystem { - // Information has been extracted looking at available disk images - // This may be missing fields, or not, I don't know russian so any help is appreciated - /// - /// Implements detection of the AO-DOS filesystem - public sealed class AODOS : IFilesystem + readonly byte[] _identifier = { - readonly byte[] _identifier = + 0x20, 0x41, 0x4F, 0x2D, 0x44, 0x4F, 0x53, 0x20 + }; + /// + public FileSystemType XmlFsType { get; private set; } + /// + public string Name => "Alexander Osipov DOS file system"; + /// + public Guid Id => new("668E5039-9DDD-442A-BE1B-A315D6E38E26"); + /// + public Encoding Encoding { get; private set; } + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + // Does AO-DOS support hard disks? + if(partition.Start > 0) + return false; + + // How is it really? + if(imagePlugin.Info.SectorSize != 512) + return false; + + // Does AO-DOS support any other kind of disk? + if(imagePlugin.Info.Sectors != 800 && + imagePlugin.Info.Sectors != 1600) + return false; + + ErrorNumber errno = imagePlugin.ReadSector(0, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + BootBlock bb = Marshal.ByteArrayToStructureLittleEndian(sector); + + return bb.identifier.SequenceEqual(_identifier); + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + information = ""; + Encoding = Encoding.GetEncoding("koi8-r"); + ErrorNumber errno = imagePlugin.ReadSector(0, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + BootBlock bb = Marshal.ByteArrayToStructureLittleEndian(sector); + + var sbInformation = new StringBuilder(); + + sbInformation.AppendLine("Alexander Osipov DOS file system"); + + XmlFsType = new FileSystemType { - 0x20, 0x41, 0x4F, 0x2D, 0x44, 0x4F, 0x53, 0x20 + Type = "Alexander Osipov DOS file system", + Clusters = imagePlugin.Info.Sectors, + ClusterSize = imagePlugin.Info.SectorSize, + Files = bb.files, + FilesSpecified = true, + FreeClusters = imagePlugin.Info.Sectors - bb.usedSectors, + FreeClustersSpecified = true, + VolumeName = StringHandlers.SpacePaddedToString(bb.volumeLabel, Encoding), + Bootable = true }; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Name => "Alexander Osipov DOS file system"; - /// - public Guid Id => new("668E5039-9DDD-442A-BE1B-A315D6E38E26"); - /// - public Encoding Encoding { get; private set; } - /// - public string Author => "Natalia Portillo"; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - // Does AO-DOS support hard disks? - if(partition.Start > 0) - return false; + sbInformation.AppendFormat("{0} files on volume", bb.files).AppendLine(); + sbInformation.AppendFormat("{0} used sectors on volume", bb.usedSectors).AppendLine(); - // How is it really? - if(imagePlugin.Info.SectorSize != 512) - return false; + sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(bb.volumeLabel, Encoding)). + AppendLine(); - // Does AO-DOS support any other kind of disk? - if(imagePlugin.Info.Sectors != 800 && - imagePlugin.Info.Sectors != 1600) - return false; + information = sbInformation.ToString(); + } - ErrorNumber errno = imagePlugin.ReadSector(0, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - BootBlock bb = Marshal.ByteArrayToStructureLittleEndian(sector); - - return bb.identifier.SequenceEqual(_identifier); - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - information = ""; - Encoding = Encoding.GetEncoding("koi8-r"); - ErrorNumber errno = imagePlugin.ReadSector(0, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - BootBlock bb = Marshal.ByteArrayToStructureLittleEndian(sector); - - var sbInformation = new StringBuilder(); - - sbInformation.AppendLine("Alexander Osipov DOS file system"); - - XmlFsType = new FileSystemType - { - Type = "Alexander Osipov DOS file system", - Clusters = imagePlugin.Info.Sectors, - ClusterSize = imagePlugin.Info.SectorSize, - Files = bb.files, - FilesSpecified = true, - FreeClusters = imagePlugin.Info.Sectors - bb.usedSectors, - FreeClustersSpecified = true, - VolumeName = StringHandlers.SpacePaddedToString(bb.volumeLabel, Encoding), - Bootable = true - }; - - sbInformation.AppendFormat("{0} files on volume", bb.files).AppendLine(); - sbInformation.AppendFormat("{0} used sectors on volume", bb.usedSectors).AppendLine(); - - sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(bb.volumeLabel, Encoding)). - AppendLine(); - - information = sbInformation.ToString(); - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BootBlock - { - /// A NOP opcode - public readonly byte nop; - /// A branch to real bootloader - public readonly ushort branch; - /// Unused - public readonly byte unused; - /// " AO-DOS " - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] identifier; - /// Volume label - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] volumeLabel; - /// How many files are present in disk - public readonly ushort files; - /// How many sectors are used - public readonly ushort usedSectors; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BootBlock + { + /// A NOP opcode + public readonly byte nop; + /// A branch to real bootloader + public readonly ushort branch; + /// Unused + public readonly byte unused; + /// " AO-DOS " + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] identifier; + /// Volume label + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] volumeLabel; + /// How many files are present in disk + public readonly ushort files; + /// How many sectors are used + public readonly ushort usedSectors; } } \ No newline at end of file diff --git a/Aaru.Filesystems/APFS.cs b/Aaru.Filesystems/APFS.cs index 0430ad0c6..877371fee 100644 --- a/Aaru.Filesystems/APFS.cs +++ b/Aaru.Filesystems/APFS.cs @@ -40,111 +40,110 @@ using Aaru.CommonTypes.Interfaces; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Apple File System (APFS) +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class APFS : IFilesystem { + const uint APFS_CONTAINER_MAGIC = 0x4253584E; // "NXSB" + const uint APFS_VOLUME_MAGIC = 0x42535041; // "APSB" + /// - /// Implements detection of the Apple File System (APFS) - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class APFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Apple File System"; + /// + public Guid Id => new("A4060F9D-2909-42E2-9D95-DB31FA7EA797"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint APFS_CONTAINER_MAGIC = 0x4253584E; // "NXSB" - const uint APFS_VOLUME_MAGIC = 0x42535041; // "APSB" + if(partition.Start >= partition.End) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Apple File System"; - /// - public Guid Id => new("A4060F9D-2909-42E2-9D95-DB31FA7EA797"); - /// - public string Author => "Natalia Portillo"; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(errno != ErrorNumber.NoError) + return false; + + ContainerSuperBlock nxSb; + + try { - if(partition.Start >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - ContainerSuperBlock nxSb; - - try - { - nxSb = Marshal.ByteArrayToStructureLittleEndian(sector); - } - catch - { - return false; - } - - return nxSb.magic == APFS_CONTAINER_MAGIC; + nxSb = Marshal.ByteArrayToStructureLittleEndian(sector); + } + catch + { + return false; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + return nxSb.magic == APFS_CONTAINER_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = Encoding.UTF8; + var sbInformation = new StringBuilder(); + XmlFsType = new FileSystemType(); + information = ""; + + if(partition.Start >= partition.End) + return; + + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + ContainerSuperBlock nxSb; + + try { - Encoding = Encoding.UTF8; - var sbInformation = new StringBuilder(); - XmlFsType = new FileSystemType(); - information = ""; - - if(partition.Start >= partition.End) - return; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - ContainerSuperBlock nxSb; - - try - { - nxSb = Marshal.ByteArrayToStructureLittleEndian(sector); - } - catch - { - return; - } - - if(nxSb.magic != APFS_CONTAINER_MAGIC) - return; - - sbInformation.AppendLine("Apple File System"); - sbInformation.AppendLine(); - sbInformation.AppendFormat("{0} bytes per block", nxSb.blockSize).AppendLine(); - - sbInformation.AppendFormat("Container has {0} bytes in {1} blocks", nxSb.containerBlocks * nxSb.blockSize, - nxSb.containerBlocks).AppendLine(); - - information = sbInformation.ToString(); - - XmlFsType = new FileSystemType - { - Bootable = false, - Clusters = nxSb.containerBlocks, - ClusterSize = nxSb.blockSize, - Type = "Apple File System" - }; + nxSb = Marshal.ByteArrayToStructureLittleEndian(sector); + } + catch + { + return; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ContainerSuperBlock + if(nxSb.magic != APFS_CONTAINER_MAGIC) + return; + + sbInformation.AppendLine("Apple File System"); + sbInformation.AppendLine(); + sbInformation.AppendFormat("{0} bytes per block", nxSb.blockSize).AppendLine(); + + sbInformation.AppendFormat("Container has {0} bytes in {1} blocks", nxSb.containerBlocks * nxSb.blockSize, + nxSb.containerBlocks).AppendLine(); + + information = sbInformation.ToString(); + + XmlFsType = new FileSystemType { - public readonly ulong unknown1; // Varies between copies of the superblock - public readonly ulong unknown2; - public readonly ulong unknown3; // Varies by 1 between copies of the superblock - public readonly ulong unknown4; - public readonly uint magic; - public readonly uint blockSize; - public readonly ulong containerBlocks; - } + Bootable = false, + Clusters = nxSb.containerBlocks, + ClusterSize = nxSb.blockSize, + Type = "Apple File System" + }; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ContainerSuperBlock + { + public readonly ulong unknown1; // Varies between copies of the superblock + public readonly ulong unknown2; + public readonly ulong unknown3; // Varies by 1 between copies of the superblock + public readonly ulong unknown4; + public readonly uint magic; + public readonly uint blockSize; + public readonly ulong containerBlocks; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Acorn.cs b/Aaru.Filesystems/Acorn.cs index 89dd0c04d..ef78fff63 100644 --- a/Aaru.Filesystems/Acorn.cs +++ b/Aaru.Filesystems/Acorn.cs @@ -42,102 +42,334 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of Acorn's Advanced Data Filing System (ADFS) +public sealed class AcornADFS : IFilesystem { + /// Location for boot block, in bytes + const ulong BOOT_BLOCK_LOCATION = 0xC00; + /// Size of boot block, in bytes + const uint BOOT_BLOCK_SIZE = 0x200; + /// Location of new directory, in bytes + const ulong NEW_DIRECTORY_LOCATION = 0x400; + /// Location of old directory, in bytes + const ulong OLD_DIRECTORY_LOCATION = 0x200; + /// Size of old directory + const uint OLD_DIRECTORY_SIZE = 1280; + /// Size of new directory + const uint NEW_DIRECTORY_SIZE = 2048; + + /// New directory format magic number, "Nick" + const uint NEW_DIR_MAGIC = 0x6B63694E; + /// Old directory format magic number, "Hugo" + const uint OLD_DIR_MAGIC = 0x6F677548; + /// - /// Implements detection of Acorn's Advanced Data Filing System (ADFS) - public sealed class AcornADFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public string Name => "Acorn Advanced Disc Filing System"; + /// + public Guid Id => new("BAFC1E50-9C64-4CD3-8400-80628CC27AFA"); + /// + public Encoding Encoding { get; private set; } + /// + public string Author => "Natalia Portillo"; + + // TODO: BBC Master hard disks are untested... + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Location for boot block, in bytes - const ulong BOOT_BLOCK_LOCATION = 0xC00; - /// Size of boot block, in bytes - const uint BOOT_BLOCK_SIZE = 0x200; - /// Location of new directory, in bytes - const ulong NEW_DIRECTORY_LOCATION = 0x400; - /// Location of old directory, in bytes - const ulong OLD_DIRECTORY_LOCATION = 0x200; - /// Size of old directory - const uint OLD_DIRECTORY_SIZE = 1280; - /// Size of new directory - const uint NEW_DIRECTORY_SIZE = 2048; + if(partition.Start >= partition.End) + return false; - /// New directory format magic number, "Nick" - const uint NEW_DIR_MAGIC = 0x6B63694E; - /// Old directory format magic number, "Hugo" - const uint OLD_DIR_MAGIC = 0x6F677548; + ulong sbSector; + uint sectorsToRead; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Name => "Acorn Advanced Disc Filing System"; - /// - public Guid Id => new("BAFC1E50-9C64-4CD3-8400-80628CC27AFA"); - /// - public Encoding Encoding { get; private set; } - /// - public string Author => "Natalia Portillo"; + if(imagePlugin.Info.SectorSize < 256) + return false; - // TODO: BBC Master hard disks are untested... - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + byte[] sector; + ErrorNumber errno; + + // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions + if(partition.Start == 0) { - if(partition.Start >= partition.End) + errno = imagePlugin.ReadSector(0, out sector); + + if(errno != ErrorNumber.NoError) return false; - ulong sbSector; - uint sectorsToRead; + byte oldChk0 = AcornMapChecksum(sector, 255); + OldMapSector0 oldMap0 = Marshal.ByteArrayToStructureLittleEndian(sector); - if(imagePlugin.Info.SectorSize < 256) + errno = imagePlugin.ReadSector(1, out sector); + + if(errno != ErrorNumber.NoError) return false; - byte[] sector; - ErrorNumber errno; + byte oldChk1 = AcornMapChecksum(sector, 255); + OldMapSector1 oldMap1 = Marshal.ByteArrayToStructureLittleEndian(sector); - // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions - if(partition.Start == 0) + AaruConsole.DebugWriteLine("ADFS Plugin", "oldMap0.checksum = {0}", oldMap0.checksum); + AaruConsole.DebugWriteLine("ADFS Plugin", "oldChk0 = {0}", oldChk0); + + // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400) + if(oldMap0.checksum == oldChk0 && + oldMap1.checksum != oldChk1 && + sector.Length >= 512) { errno = imagePlugin.ReadSector(0, out sector); if(errno != ErrorNumber.NoError) return false; - byte oldChk0 = AcornMapChecksum(sector, 255); - OldMapSector0 oldMap0 = Marshal.ByteArrayToStructureLittleEndian(sector); + byte[] tmp = new byte[256]; + Array.Copy(sector, 256, tmp, 0, 256); + oldChk1 = AcornMapChecksum(tmp, 255); + oldMap1 = Marshal.ByteArrayToStructureLittleEndian(tmp); + } - errno = imagePlugin.ReadSector(1, out sector); + AaruConsole.DebugWriteLine("ADFS Plugin", "oldMap1.checksum = {0}", oldMap1.checksum); + AaruConsole.DebugWriteLine("ADFS Plugin", "oldChk1 = {0}", oldChk1); + + if(oldMap0.checksum == oldChk0 && + oldMap1.checksum == oldChk1 && + oldMap0.checksum != 0 && + oldMap1.checksum != 0) + { + sbSector = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; + sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; + + if(OLD_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) + sectorsToRead++; + + errno = imagePlugin.ReadSectors(sbSector, sectorsToRead, out sector); if(errno != ErrorNumber.NoError) return false; - byte oldChk1 = AcornMapChecksum(sector, 255); - OldMapSector1 oldMap1 = Marshal.ByteArrayToStructureLittleEndian(sector); - - AaruConsole.DebugWriteLine("ADFS Plugin", "oldMap0.checksum = {0}", oldMap0.checksum); - AaruConsole.DebugWriteLine("ADFS Plugin", "oldChk0 = {0}", oldChk0); - - // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400) - if(oldMap0.checksum == oldChk0 && - oldMap1.checksum != oldChk1 && - sector.Length >= 512) + if(sector.Length > OLD_DIRECTORY_SIZE) { - errno = imagePlugin.ReadSector(0, out sector); - - if(errno != ErrorNumber.NoError) - return false; - - byte[] tmp = new byte[256]; - Array.Copy(sector, 256, tmp, 0, 256); - oldChk1 = AcornMapChecksum(tmp, 255); - oldMap1 = Marshal.ByteArrayToStructureLittleEndian(tmp); + byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; + Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); + Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); + sector = tmp; } - AaruConsole.DebugWriteLine("ADFS Plugin", "oldMap1.checksum = {0}", oldMap1.checksum); - AaruConsole.DebugWriteLine("ADFS Plugin", "oldChk1 = {0}", oldChk1); + OldDirectory oldRoot = Marshal.ByteArrayToStructureLittleEndian(sector); + byte dirChk = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1); - if(oldMap0.checksum == oldChk0 && - oldMap1.checksum == oldChk1 && - oldMap0.checksum != 0 && - oldMap1.checksum != 0) + AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x200 = {0}", + oldRoot.header.magic); + + AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x200 = {0}", oldRoot.tail.magic); + + AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x200 = {0}", + oldRoot.tail.checkByte); + + AaruConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x200 = {0}", dirChk); + + if((oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) || + (oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC)) + return true; + + // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that... + sbSector = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; + sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; + + if(NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) + sectorsToRead++; + + errno = imagePlugin.ReadSectors(sbSector, sectorsToRead, out sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length > OLD_DIRECTORY_SIZE) + { + byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; + Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); + Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); + sector = tmp; + } + + oldRoot = Marshal.ByteArrayToStructureLittleEndian(sector); + dirChk = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1); + + AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x400 = {0}", + oldRoot.header.magic); + + AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x400 = {0}", oldRoot.tail.magic); + + AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x400 = {0}", + oldRoot.tail.checkByte); + + AaruConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x400 = {0}", dirChk); + + if((oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) || + (oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC)) + return true; + } + } + + // Partitioning or not, new formats follow: + DiscRecord drSb; + + errno = imagePlugin.ReadSector(partition.Start, out sector); + + if(errno != ErrorNumber.NoError) + return false; + + byte newChk = NewMapChecksum(sector); + AaruConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk); + AaruConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]); + + sbSector = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize; + sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize; + + if(BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0) + sectorsToRead++; + + if(sbSector + partition.Start + sectorsToRead >= partition.End) + return false; + + errno = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead, out byte[] bootSector); + + if(errno != ErrorNumber.NoError) + return false; + + int bootChk = 0; + + if(bootSector.Length < 512) + return false; + + for(int i = 0; i < 0x1FF; i++) + bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i]; + + AaruConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk); + AaruConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]); + + if(newChk == sector[0] && + newChk != 0) + { + NewMap nmap = Marshal.ByteArrayToStructureLittleEndian(sector); + drSb = nmap.discRecord; + } + else if(bootChk == bootSector[0x1FF]) + { + BootBlock bBlock = Marshal.ByteArrayToStructureLittleEndian(bootSector); + drSb = bBlock.discRecord; + } + else + return false; + + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size); + + AaruConsole.DebugWriteLine("ADFS Plugin", "IsNullOrEmpty(drSb.reserved) = {0}", + ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)); + + if(drSb.log2secsize < 8 || + drSb.log2secsize > 10) + return false; + + if(drSb.idlen < drSb.log2secsize + 3 || + drSb.idlen > 19) + return false; + + if(drSb.disc_size_high >> drSb.log2secsize != 0) + return false; + + if(!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)) + return false; + + ulong bytes = drSb.disc_size_high; + bytes *= 0x100000000; + bytes += drSb.disc_size; + + return bytes <= imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize; + } + + // TODO: Find root directory on volumes with DiscRecord + // TODO: Support big directories (ADFS-G?) + // TODO: Find the real freemap on volumes with DiscRecord, as DiscRecord's discid may be empty but this one isn't + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); + var sbInformation = new StringBuilder(); + XmlFsType = new FileSystemType(); + information = ""; + ErrorNumber errno; + + ulong sbSector; + byte[] sector; + uint sectorsToRead; + ulong bytes; + + // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions + if(partition.Start == 0) + { + errno = imagePlugin.ReadSector(0, out sector); + + if(errno != ErrorNumber.NoError) + return; + + byte oldChk0 = AcornMapChecksum(sector, 255); + OldMapSector0 oldMap0 = Marshal.ByteArrayToStructureLittleEndian(sector); + + errno = imagePlugin.ReadSector(1, out sector); + + if(errno != ErrorNumber.NoError) + return; + + byte oldChk1 = AcornMapChecksum(sector, 255); + OldMapSector1 oldMap1 = Marshal.ByteArrayToStructureLittleEndian(sector); + + // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400) + if(oldMap0.checksum == oldChk0 && + oldMap1.checksum != oldChk1 && + sector.Length >= 512) + { + errno = imagePlugin.ReadSector(0, out sector); + + if(errno != ErrorNumber.NoError) + return; + + byte[] tmp = new byte[256]; + Array.Copy(sector, 256, tmp, 0, 256); + oldChk1 = AcornMapChecksum(tmp, 255); + oldMap1 = Marshal.ByteArrayToStructureLittleEndian(tmp); + } + + if(oldMap0.checksum == oldChk0 && + oldMap1.checksum == oldChk1 && + oldMap0.checksum != 0 && + oldMap1.checksum != 0) + { + bytes = (ulong)((oldMap0.size[2] << 16) + (oldMap0.size[1] << 8) + oldMap0.size[0]) * 256; + byte[] namebytes = new byte[10]; + + for(int i = 0; i < 5; i++) + { + namebytes[i * 2] = oldMap0.name[i]; + namebytes[(i * 2) + 1] = oldMap1.name[i]; + } + + XmlFsType = new FileSystemType + { + Bootable = oldMap1.boot != 0, // Or not? + Clusters = bytes / imagePlugin.Info.SectorSize, + ClusterSize = imagePlugin.Info.SectorSize, + Type = "Acorn Advanced Disc Filing System" + }; + + if(ArrayHelpers.ArrayIsNullOrEmpty(namebytes)) { sbSector = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; @@ -148,7 +380,7 @@ namespace Aaru.Filesystems errno = imagePlugin.ReadSectors(sbSector, sectorsToRead, out sector); if(errno != ErrorNumber.NoError) - return false; + return; if(sector.Length > OLD_DIRECTORY_SIZE) { @@ -159,222 +391,17 @@ namespace Aaru.Filesystems } OldDirectory oldRoot = Marshal.ByteArrayToStructureLittleEndian(sector); - byte dirChk = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1); - AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x200 = {0}", - oldRoot.header.magic); - - AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x200 = {0}", oldRoot.tail.magic); - - AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x200 = {0}", - oldRoot.tail.checkByte); - - AaruConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x200 = {0}", dirChk); - - if((oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) || - (oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC)) - return true; - - // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that... - sbSector = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; - sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; - - if(NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) - sectorsToRead++; - - errno = imagePlugin.ReadSectors(sbSector, sectorsToRead, out sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length > OLD_DIRECTORY_SIZE) + if(oldRoot.header.magic == OLD_DIR_MAGIC && + oldRoot.tail.magic == OLD_DIR_MAGIC) + namebytes = oldRoot.tail.name; + else { - byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; - Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); - Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); - sector = tmp; - } + // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that... + sbSector = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; + sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; - oldRoot = Marshal.ByteArrayToStructureLittleEndian(sector); - dirChk = AcornDirectoryChecksum(sector, (int)OLD_DIRECTORY_SIZE - 1); - - AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.header.magic at 0x400 = {0}", - oldRoot.header.magic); - - AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.magic at 0x400 = {0}", oldRoot.tail.magic); - - AaruConsole.DebugWriteLine("ADFS Plugin", "oldRoot.tail.checkByte at 0x400 = {0}", - oldRoot.tail.checkByte); - - AaruConsole.DebugWriteLine("ADFS Plugin", "dirChk at 0x400 = {0}", dirChk); - - if((oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) || - (oldRoot.header.magic == NEW_DIR_MAGIC && oldRoot.tail.magic == NEW_DIR_MAGIC)) - return true; - } - } - - // Partitioning or not, new formats follow: - DiscRecord drSb; - - errno = imagePlugin.ReadSector(partition.Start, out sector); - - if(errno != ErrorNumber.NoError) - return false; - - byte newChk = NewMapChecksum(sector); - AaruConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk); - AaruConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]); - - sbSector = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize; - sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize; - - if(BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0) - sectorsToRead++; - - if(sbSector + partition.Start + sectorsToRead >= partition.End) - return false; - - errno = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead, out byte[] bootSector); - - if(errno != ErrorNumber.NoError) - return false; - - int bootChk = 0; - - if(bootSector.Length < 512) - return false; - - for(int i = 0; i < 0x1FF; i++) - bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i]; - - AaruConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk); - AaruConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]); - - if(newChk == sector[0] && - newChk != 0) - { - NewMap nmap = Marshal.ByteArrayToStructureLittleEndian(sector); - drSb = nmap.discRecord; - } - else if(bootChk == bootSector[0x1FF]) - { - BootBlock bBlock = Marshal.ByteArrayToStructureLittleEndian(bootSector); - drSb = bBlock.discRecord; - } - else - return false; - - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size); - - AaruConsole.DebugWriteLine("ADFS Plugin", "IsNullOrEmpty(drSb.reserved) = {0}", - ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)); - - if(drSb.log2secsize < 8 || - drSb.log2secsize > 10) - return false; - - if(drSb.idlen < drSb.log2secsize + 3 || - drSb.idlen > 19) - return false; - - if(drSb.disc_size_high >> drSb.log2secsize != 0) - return false; - - if(!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)) - return false; - - ulong bytes = drSb.disc_size_high; - bytes *= 0x100000000; - bytes += drSb.disc_size; - - return bytes <= imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize; - } - - // TODO: Find root directory on volumes with DiscRecord - // TODO: Support big directories (ADFS-G?) - // TODO: Find the real freemap on volumes with DiscRecord, as DiscRecord's discid may be empty but this one isn't - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); - var sbInformation = new StringBuilder(); - XmlFsType = new FileSystemType(); - information = ""; - ErrorNumber errno; - - ulong sbSector; - byte[] sector; - uint sectorsToRead; - ulong bytes; - - // ADFS-S, ADFS-M, ADFS-L, ADFS-D without partitions - if(partition.Start == 0) - { - errno = imagePlugin.ReadSector(0, out sector); - - if(errno != ErrorNumber.NoError) - return; - - byte oldChk0 = AcornMapChecksum(sector, 255); - OldMapSector0 oldMap0 = Marshal.ByteArrayToStructureLittleEndian(sector); - - errno = imagePlugin.ReadSector(1, out sector); - - if(errno != ErrorNumber.NoError) - return; - - byte oldChk1 = AcornMapChecksum(sector, 255); - OldMapSector1 oldMap1 = Marshal.ByteArrayToStructureLittleEndian(sector); - - // According to documentation map1 MUST start on sector 1. On ADFS-D it starts at 0x100, not on sector 1 (0x400) - if(oldMap0.checksum == oldChk0 && - oldMap1.checksum != oldChk1 && - sector.Length >= 512) - { - errno = imagePlugin.ReadSector(0, out sector); - - if(errno != ErrorNumber.NoError) - return; - - byte[] tmp = new byte[256]; - Array.Copy(sector, 256, tmp, 0, 256); - oldChk1 = AcornMapChecksum(tmp, 255); - oldMap1 = Marshal.ByteArrayToStructureLittleEndian(tmp); - } - - if(oldMap0.checksum == oldChk0 && - oldMap1.checksum == oldChk1 && - oldMap0.checksum != 0 && - oldMap1.checksum != 0) - { - bytes = (ulong)((oldMap0.size[2] << 16) + (oldMap0.size[1] << 8) + oldMap0.size[0]) * 256; - byte[] namebytes = new byte[10]; - - for(int i = 0; i < 5; i++) - { - namebytes[i * 2] = oldMap0.name[i]; - namebytes[(i * 2) + 1] = oldMap1.name[i]; - } - - XmlFsType = new FileSystemType - { - Bootable = oldMap1.boot != 0, // Or not? - Clusters = bytes / imagePlugin.Info.SectorSize, - ClusterSize = imagePlugin.Info.SectorSize, - Type = "Acorn Advanced Disc Filing System" - }; - - if(ArrayHelpers.ArrayIsNullOrEmpty(namebytes)) - { - sbSector = OLD_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; - sectorsToRead = OLD_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; - - if(OLD_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) + if(NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) sectorsToRead++; errno = imagePlugin.ReadSectors(sbSector, sectorsToRead, out sector); @@ -386,461 +413,433 @@ namespace Aaru.Filesystems { byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); - Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); + + Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); + sector = tmp; } - OldDirectory oldRoot = Marshal.ByteArrayToStructureLittleEndian(sector); + oldRoot = Marshal.ByteArrayToStructureLittleEndian(sector); if(oldRoot.header.magic == OLD_DIR_MAGIC && oldRoot.tail.magic == OLD_DIR_MAGIC) namebytes = oldRoot.tail.name; else { - // RISC OS says the old directory can't be in the new location, hard disks created by RISC OS 3.10 do that... - sbSector = NEW_DIRECTORY_LOCATION / imagePlugin.Info.SectorSize; - sectorsToRead = NEW_DIRECTORY_SIZE / imagePlugin.Info.SectorSize; - - if(NEW_DIRECTORY_SIZE % imagePlugin.Info.SectorSize > 0) - sectorsToRead++; - errno = imagePlugin.ReadSectors(sbSector, sectorsToRead, out sector); if(errno != ErrorNumber.NoError) return; - if(sector.Length > OLD_DIRECTORY_SIZE) + if(sector.Length > NEW_DIRECTORY_SIZE) { - byte[] tmp = new byte[OLD_DIRECTORY_SIZE]; - Array.Copy(sector, 0, tmp, 0, OLD_DIRECTORY_SIZE - 53); + byte[] tmp = new byte[NEW_DIRECTORY_SIZE]; + Array.Copy(sector, 0, tmp, 0, NEW_DIRECTORY_SIZE - 41); - Array.Copy(sector, sector.Length - 54, tmp, OLD_DIRECTORY_SIZE - 54, 53); + Array.Copy(sector, sector.Length - 42, tmp, NEW_DIRECTORY_SIZE - 42, 41); sector = tmp; } - oldRoot = Marshal.ByteArrayToStructureLittleEndian(sector); + NewDirectory newRoot = Marshal.ByteArrayToStructureLittleEndian(sector); - if(oldRoot.header.magic == OLD_DIR_MAGIC && - oldRoot.tail.magic == OLD_DIR_MAGIC) - namebytes = oldRoot.tail.name; - else - { - errno = imagePlugin.ReadSectors(sbSector, sectorsToRead, out sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length > NEW_DIRECTORY_SIZE) - { - byte[] tmp = new byte[NEW_DIRECTORY_SIZE]; - Array.Copy(sector, 0, tmp, 0, NEW_DIRECTORY_SIZE - 41); - - Array.Copy(sector, sector.Length - 42, tmp, NEW_DIRECTORY_SIZE - 42, 41); - - sector = tmp; - } - - NewDirectory newRoot = Marshal.ByteArrayToStructureLittleEndian(sector); - - if(newRoot.header.magic == NEW_DIR_MAGIC && - newRoot.tail.magic == NEW_DIR_MAGIC) - namebytes = newRoot.tail.title; - } + if(newRoot.header.magic == NEW_DIR_MAGIC && + newRoot.tail.magic == NEW_DIR_MAGIC) + namebytes = newRoot.tail.title; } } - - sbInformation.AppendLine("Acorn Advanced Disc Filing System"); - sbInformation.AppendLine(); - sbInformation.AppendFormat("{0} bytes per sector", imagePlugin.Info.SectorSize).AppendLine(); - sbInformation.AppendFormat("Volume has {0} bytes", bytes).AppendLine(); - - sbInformation.AppendFormat("Volume name: {0}", StringHandlers.CToString(namebytes, Encoding)). - AppendLine(); - - if(oldMap1.discId > 0) - { - XmlFsType.VolumeSerial = $"{oldMap1.discId:X4}"; - sbInformation.AppendFormat("Volume ID: {0:X4}", oldMap1.discId).AppendLine(); - } - - if(!ArrayHelpers.ArrayIsNullOrEmpty(namebytes)) - XmlFsType.VolumeName = StringHandlers.CToString(namebytes, Encoding); - - information = sbInformation.ToString(); - - return; } - } - // Partitioning or not, new formats follow: - DiscRecord drSb; + sbInformation.AppendLine("Acorn Advanced Disc Filing System"); + sbInformation.AppendLine(); + sbInformation.AppendFormat("{0} bytes per sector", imagePlugin.Info.SectorSize).AppendLine(); + sbInformation.AppendFormat("Volume has {0} bytes", bytes).AppendLine(); - errno = imagePlugin.ReadSector(partition.Start, out sector); + sbInformation.AppendFormat("Volume name: {0}", StringHandlers.CToString(namebytes, Encoding)). + AppendLine(); + + if(oldMap1.discId > 0) + { + XmlFsType.VolumeSerial = $"{oldMap1.discId:X4}"; + sbInformation.AppendFormat("Volume ID: {0:X4}", oldMap1.discId).AppendLine(); + } + + if(!ArrayHelpers.ArrayIsNullOrEmpty(namebytes)) + XmlFsType.VolumeName = StringHandlers.CToString(namebytes, Encoding); + + information = sbInformation.ToString(); - if(errno != ErrorNumber.NoError) return; - - byte newChk = NewMapChecksum(sector); - AaruConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk); - AaruConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]); - - sbSector = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize; - sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize; - - if(BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0) - sectorsToRead++; - - errno = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead, out byte[] bootSector); - - if(errno != ErrorNumber.NoError) - return; - - int bootChk = 0; - - for(int i = 0; i < 0x1FF; i++) - bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i]; - - AaruConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk); - AaruConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]); - - if(newChk == sector[0] && - newChk != 0) - { - NewMap nmap = Marshal.ByteArrayToStructureLittleEndian(sector); - drSb = nmap.discRecord; } - else if(bootChk == bootSector[0x1FF]) + } + + // Partitioning or not, new formats follow: + DiscRecord drSb; + + errno = imagePlugin.ReadSector(partition.Start, out sector); + + if(errno != ErrorNumber.NoError) + return; + + byte newChk = NewMapChecksum(sector); + AaruConsole.DebugWriteLine("ADFS Plugin", "newChk = {0}", newChk); + AaruConsole.DebugWriteLine("ADFS Plugin", "map.zoneChecksum = {0}", sector[0]); + + sbSector = BOOT_BLOCK_LOCATION / imagePlugin.Info.SectorSize; + sectorsToRead = BOOT_BLOCK_SIZE / imagePlugin.Info.SectorSize; + + if(BOOT_BLOCK_SIZE % imagePlugin.Info.SectorSize > 0) + sectorsToRead++; + + errno = imagePlugin.ReadSectors(sbSector + partition.Start, sectorsToRead, out byte[] bootSector); + + if(errno != ErrorNumber.NoError) + return; + + int bootChk = 0; + + for(int i = 0; i < 0x1FF; i++) + bootChk = (bootChk & 0xFF) + (bootChk >> 8) + bootSector[i]; + + AaruConsole.DebugWriteLine("ADFS Plugin", "bootChk = {0}", bootChk); + AaruConsole.DebugWriteLine("ADFS Plugin", "bBlock.checksum = {0}", bootSector[0x1FF]); + + if(newChk == sector[0] && + newChk != 0) + { + NewMap nmap = Marshal.ByteArrayToStructureLittleEndian(sector); + drSb = nmap.discRecord; + } + else if(bootChk == bootSector[0x1FF]) + { + BootBlock bBlock = Marshal.ByteArrayToStructureLittleEndian(bootSector); + drSb = bBlock.discRecord; + } + else + return; + + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.spt = {0}", drSb.spt); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.heads = {0}", drSb.heads); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.density = {0}", drSb.density); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2bpmb = {0}", drSb.log2bpmb); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.skew = {0}", drSb.skew); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.bootoption = {0}", drSb.bootoption); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.lowsector = {0}", drSb.lowsector); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones = {0}", drSb.nzones); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.zone_spare = {0}", drSb.zone_spare); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.root = {0}", drSb.root); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_id = {0}", drSb.disc_id); + + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_name = {0}", + StringHandlers.CToString(drSb.disc_name, Encoding)); + + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_type = {0}", drSb.disc_type); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.flags = {0}", drSb.flags); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones_high = {0}", drSb.nzones_high); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.format_version = {0}", drSb.format_version); + AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.root_size = {0}", drSb.root_size); + + if(drSb.log2secsize < 8 || + drSb.log2secsize > 10) + return; + + if(drSb.idlen < drSb.log2secsize + 3 || + drSb.idlen > 19) + return; + + if(drSb.disc_size_high >> drSb.log2secsize != 0) + return; + + if(!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)) + return; + + bytes = drSb.disc_size_high; + bytes *= 0x100000000; + bytes += drSb.disc_size; + + ulong zones = drSb.nzones_high; + zones *= 0x100000000; + zones += drSb.nzones; + + if(bytes > imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize) + return; + + XmlFsType = new FileSystemType(); + + sbInformation.AppendLine("Acorn Advanced Disc Filing System"); + sbInformation.AppendLine(); + sbInformation.AppendFormat("Version {0}", drSb.format_version).AppendLine(); + sbInformation.AppendFormat("{0} bytes per sector", 1 << drSb.log2secsize).AppendLine(); + sbInformation.AppendFormat("{0} sectors per track", drSb.spt).AppendLine(); + sbInformation.AppendFormat("{0} heads", drSb.heads).AppendLine(); + sbInformation.AppendFormat("Density code: {0}", drSb.density).AppendLine(); + sbInformation.AppendFormat("Skew: {0}", drSb.skew).AppendLine(); + sbInformation.AppendFormat("Boot option: {0}", drSb.bootoption).AppendLine(); + + // TODO: What the hell is this field refering to? + sbInformation.AppendFormat("Root starts at frag {0}", drSb.root).AppendLine(); + + //sbInformation.AppendFormat("Root is {0} bytes long", drSb.root_size).AppendLine(); + sbInformation.AppendFormat("Volume has {0} bytes in {1} zones", bytes, zones).AppendLine(); + sbInformation.AppendFormat("Volume flags: 0x{0:X4}", drSb.flags).AppendLine(); + + if(drSb.disc_id > 0) + { + XmlFsType.VolumeSerial = $"{drSb.disc_id:X4}"; + sbInformation.AppendFormat("Volume ID: {0:X4}", drSb.disc_id).AppendLine(); + } + + if(!ArrayHelpers.ArrayIsNullOrEmpty(drSb.disc_name)) + { + string discname = StringHandlers.CToString(drSb.disc_name, Encoding); + XmlFsType.VolumeName = discname; + sbInformation.AppendFormat("Volume name: {0}", discname).AppendLine(); + } + + information = sbInformation.ToString(); + + XmlFsType.Bootable |= drSb.bootoption != 0; // Or not? + XmlFsType.Clusters = bytes / (ulong)(1 << drSb.log2secsize); + XmlFsType.ClusterSize = (uint)(1 << drSb.log2secsize); + XmlFsType.Type = "Acorn Advanced Disc Filing System"; + } + + byte AcornMapChecksum(byte[] data, int length) + { + int sum = 0; + int carry = 0; + + if(length > data.Length) + length = data.Length; + + // ADC r0, r0, r1 + // MOVS r0, r0, LSL #24 + // MOV r0, r0, LSR #24 + for(int i = length - 1; i >= 0; i--) + { + sum += data[i] + carry; + + if(sum > 0xFF) { - BootBlock bBlock = Marshal.ByteArrayToStructureLittleEndian(bootSector); - drSb = bBlock.discRecord; + carry = 1; + sum &= 0xFF; } else - return; - - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2secsize = {0}", drSb.log2secsize); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.spt = {0}", drSb.spt); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.heads = {0}", drSb.heads); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.density = {0}", drSb.density); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.idlen = {0}", drSb.idlen); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.log2bpmb = {0}", drSb.log2bpmb); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.skew = {0}", drSb.skew); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.bootoption = {0}", drSb.bootoption); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.lowsector = {0}", drSb.lowsector); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones = {0}", drSb.nzones); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.zone_spare = {0}", drSb.zone_spare); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.root = {0}", drSb.root); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size = {0}", drSb.disc_size); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_id = {0}", drSb.disc_id); - - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_name = {0}", - StringHandlers.CToString(drSb.disc_name, Encoding)); - - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_type = {0}", drSb.disc_type); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.disc_size_high = {0}", drSb.disc_size_high); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.flags = {0}", drSb.flags); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.nzones_high = {0}", drSb.nzones_high); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.format_version = {0}", drSb.format_version); - AaruConsole.DebugWriteLine("ADFS Plugin", "drSb.root_size = {0}", drSb.root_size); - - if(drSb.log2secsize < 8 || - drSb.log2secsize > 10) - return; - - if(drSb.idlen < drSb.log2secsize + 3 || - drSb.idlen > 19) - return; - - if(drSb.disc_size_high >> drSb.log2secsize != 0) - return; - - if(!ArrayHelpers.ArrayIsNullOrEmpty(drSb.reserved)) - return; - - bytes = drSb.disc_size_high; - bytes *= 0x100000000; - bytes += drSb.disc_size; - - ulong zones = drSb.nzones_high; - zones *= 0x100000000; - zones += drSb.nzones; - - if(bytes > imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize) - return; - - XmlFsType = new FileSystemType(); - - sbInformation.AppendLine("Acorn Advanced Disc Filing System"); - sbInformation.AppendLine(); - sbInformation.AppendFormat("Version {0}", drSb.format_version).AppendLine(); - sbInformation.AppendFormat("{0} bytes per sector", 1 << drSb.log2secsize).AppendLine(); - sbInformation.AppendFormat("{0} sectors per track", drSb.spt).AppendLine(); - sbInformation.AppendFormat("{0} heads", drSb.heads).AppendLine(); - sbInformation.AppendFormat("Density code: {0}", drSb.density).AppendLine(); - sbInformation.AppendFormat("Skew: {0}", drSb.skew).AppendLine(); - sbInformation.AppendFormat("Boot option: {0}", drSb.bootoption).AppendLine(); - - // TODO: What the hell is this field refering to? - sbInformation.AppendFormat("Root starts at frag {0}", drSb.root).AppendLine(); - - //sbInformation.AppendFormat("Root is {0} bytes long", drSb.root_size).AppendLine(); - sbInformation.AppendFormat("Volume has {0} bytes in {1} zones", bytes, zones).AppendLine(); - sbInformation.AppendFormat("Volume flags: 0x{0:X4}", drSb.flags).AppendLine(); - - if(drSb.disc_id > 0) - { - XmlFsType.VolumeSerial = $"{drSb.disc_id:X4}"; - sbInformation.AppendFormat("Volume ID: {0:X4}", drSb.disc_id).AppendLine(); - } - - if(!ArrayHelpers.ArrayIsNullOrEmpty(drSb.disc_name)) - { - string discname = StringHandlers.CToString(drSb.disc_name, Encoding); - XmlFsType.VolumeName = discname; - sbInformation.AppendFormat("Volume name: {0}", discname).AppendLine(); - } - - information = sbInformation.ToString(); - - XmlFsType.Bootable |= drSb.bootoption != 0; // Or not? - XmlFsType.Clusters = bytes / (ulong)(1 << drSb.log2secsize); - XmlFsType.ClusterSize = (uint)(1 << drSb.log2secsize); - XmlFsType.Type = "Acorn Advanced Disc Filing System"; + carry = 0; } - byte AcornMapChecksum(byte[] data, int length) + return (byte)(sum & 0xFF); + } + + static byte NewMapChecksum(byte[] mapBase) + { + uint rover; + uint sumVector0 = 0; + uint sumVector1 = 0; + uint sumVector2 = 0; + uint sumVector3 = 0; + + for(rover = (uint)(mapBase.Length - 4); rover > 0; rover -= 4) { - int sum = 0; - int carry = 0; - - if(length > data.Length) - length = data.Length; - - // ADC r0, r0, r1 - // MOVS r0, r0, LSL #24 - // MOV r0, r0, LSR #24 - for(int i = length - 1; i >= 0; i--) - { - sum += data[i] + carry; - - if(sum > 0xFF) - { - carry = 1; - sum &= 0xFF; - } - else - carry = 0; - } - - return (byte)(sum & 0xFF); - } - - static byte NewMapChecksum(byte[] mapBase) - { - uint rover; - uint sumVector0 = 0; - uint sumVector1 = 0; - uint sumVector2 = 0; - uint sumVector3 = 0; - - for(rover = (uint)(mapBase.Length - 4); rover > 0; rover -= 4) - { - sumVector0 += mapBase[rover + 0] + (sumVector3 >> 8); - sumVector3 &= 0xff; - sumVector1 += mapBase[rover + 1] + (sumVector0 >> 8); - sumVector0 &= 0xff; - sumVector2 += mapBase[rover + 2] + (sumVector1 >> 8); - sumVector1 &= 0xff; - sumVector3 += mapBase[rover + 3] + (sumVector2 >> 8); - sumVector2 &= 0xff; - } - - /* - Don't add the check byte when calculating its value - */ - sumVector0 += sumVector3 >> 8; + sumVector0 += mapBase[rover + 0] + (sumVector3 >> 8); + sumVector3 &= 0xff; sumVector1 += mapBase[rover + 1] + (sumVector0 >> 8); + sumVector0 &= 0xff; sumVector2 += mapBase[rover + 2] + (sumVector1 >> 8); + sumVector1 &= 0xff; sumVector3 += mapBase[rover + 3] + (sumVector2 >> 8); - - return (byte)((sumVector0 ^ sumVector1 ^ sumVector2 ^ sumVector3) & 0xff); + sumVector2 &= 0xff; } - // TODO: This is not correct... - static byte AcornDirectoryChecksum(IList data, int length) + /* + Don't add the check byte when calculating its value + */ + sumVector0 += sumVector3 >> 8; + sumVector1 += mapBase[rover + 1] + (sumVector0 >> 8); + sumVector2 += mapBase[rover + 2] + (sumVector1 >> 8); + sumVector3 += mapBase[rover + 3] + (sumVector2 >> 8); + + return (byte)((sumVector0 ^ sumVector1 ^ sumVector2 ^ sumVector3) & 0xff); + } + + // TODO: This is not correct... + static byte AcornDirectoryChecksum(IList data, int length) + { + uint sum = 0; + + if(length > data.Count) + length = data.Count; + + // EOR r0, r1, r0, ROR #13 + for(int i = 0; i < length; i++) { - uint sum = 0; - - if(length > data.Count) - length = data.Count; - - // EOR r0, r1, r0, ROR #13 - for(int i = 0; i < length; i++) - { - uint carry = sum & 0x1FFF; - sum >>= 13; - sum ^= data[i]; - sum += carry << 19; - } - - return (byte)(((sum & 0xFF000000) >> 24) ^ ((sum & 0xFF0000) >> 16) ^ ((sum & 0xFF00) >> 8) ^ (sum & 0xFF)); + uint carry = sum & 0x1FFF; + sum >>= 13; + sum ^= data[i]; + sum += carry << 19; } - /// Boot block, used in hard disks and ADFS-F and higher. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BootBlock - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1C0)] - public readonly byte[] spare; - public readonly DiscRecord discRecord; - public readonly byte flags; - public readonly ushort startCylinder; - public readonly byte checksum; - } + return (byte)(((sum & 0xFF000000) >> 24) ^ ((sum & 0xFF0000) >> 16) ^ ((sum & 0xFF00) >> 8) ^ (sum & 0xFF)); + } - /// Disc record, used in hard disks and ADFS-E and higher. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DiscRecord - { - public readonly byte log2secsize; - public readonly byte spt; - public readonly byte heads; - public readonly byte density; - public readonly byte idlen; - public readonly byte log2bpmb; - public readonly byte skew; - public readonly byte bootoption; - public readonly byte lowsector; - public readonly byte nzones; - public readonly ushort zone_spare; - public readonly uint root; - public readonly uint disc_size; - public readonly ushort disc_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly byte[] disc_name; - public readonly uint disc_type; - public readonly uint disc_size_high; - public readonly byte flags; - public readonly byte nzones_high; - public readonly uint format_version; - public readonly uint root_size; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] reserved; - } + /// Boot block, used in hard disks and ADFS-F and higher. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BootBlock + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1C0)] + public readonly byte[] spare; + public readonly DiscRecord discRecord; + public readonly byte flags; + public readonly ushort startCylinder; + public readonly byte checksum; + } - /// Free block map, sector 0, used in ADFS-S, ADFS-L, ADFS-M and ADFS-D - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct OldMapSector0 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 82 * 3)] - public readonly byte[] freeStart; - public readonly byte reserved; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] name; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] size; - public readonly byte checksum; - } + /// Disc record, used in hard disks and ADFS-E and higher. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DiscRecord + { + public readonly byte log2secsize; + public readonly byte spt; + public readonly byte heads; + public readonly byte density; + public readonly byte idlen; + public readonly byte log2bpmb; + public readonly byte skew; + public readonly byte bootoption; + public readonly byte lowsector; + public readonly byte nzones; + public readonly ushort zone_spare; + public readonly uint root; + public readonly uint disc_size; + public readonly ushort disc_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly byte[] disc_name; + public readonly uint disc_type; + public readonly uint disc_size_high; + public readonly byte flags; + public readonly byte nzones_high; + public readonly uint format_version; + public readonly uint root_size; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] reserved; + } - /// Free block map, sector 1, used in ADFS-S, ADFS-L, ADFS-M and ADFS-D - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct OldMapSector1 - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 82 * 3)] - public readonly byte[] freeStart; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] name; - public readonly ushort discId; - public readonly byte boot; - public readonly byte freeEnd; - public readonly byte checksum; - } + /// Free block map, sector 0, used in ADFS-S, ADFS-L, ADFS-M and ADFS-D + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct OldMapSector0 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 82 * 3)] + public readonly byte[] freeStart; + public readonly byte reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] name; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] size; + public readonly byte checksum; + } - /// Free block map, sector 0, used in ADFS-E - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct NewMap - { - public readonly byte zoneChecksum; - public readonly ushort freeLink; - public readonly byte crossChecksum; - public readonly DiscRecord discRecord; - } + /// Free block map, sector 1, used in ADFS-S, ADFS-L, ADFS-M and ADFS-D + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct OldMapSector1 + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 82 * 3)] + public readonly byte[] freeStart; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] name; + public readonly ushort discId; + public readonly byte boot; + public readonly byte freeEnd; + public readonly byte checksum; + } - /// Directory header, common to "old" and "new" directories - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryHeader - { - public readonly byte masterSequence; - public readonly uint magic; - } + /// Free block map, sector 0, used in ADFS-E + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct NewMap + { + public readonly byte zoneChecksum; + public readonly ushort freeLink; + public readonly byte crossChecksum; + public readonly DiscRecord discRecord; + } - /// Directory header, common to "old" and "new" directories - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryEntry - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly byte[] name; - public readonly uint load; - public readonly uint exec; - public readonly uint length; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] address; - public readonly byte atts; - } + /// Directory header, common to "old" and "new" directories + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryHeader + { + public readonly byte masterSequence; + public readonly uint magic; + } - /// Directory tail, new format - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct NewDirectoryTail - { - public readonly byte lastMark; - public readonly ushort reserved; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] parent; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)] - public readonly byte[] title; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly byte[] name; - public readonly byte endMasSeq; - public readonly uint magic; - public readonly byte checkByte; - } + /// Directory header, common to "old" and "new" directories + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryEntry + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly byte[] name; + public readonly uint load; + public readonly uint exec; + public readonly uint length; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] address; + public readonly byte atts; + } - /// Directory tail, old format - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct OldDirectoryTail - { - public readonly byte lastMark; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly byte[] name; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] parent; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)] - public readonly byte[] title; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] - public readonly byte[] reserved; - public readonly byte endMasSeq; - public readonly uint magic; - public readonly byte checkByte; - } + /// Directory tail, new format + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct NewDirectoryTail + { + public readonly byte lastMark; + public readonly ushort reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] parent; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)] + public readonly byte[] title; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly byte[] name; + public readonly byte endMasSeq; + public readonly uint magic; + public readonly byte checkByte; + } - /// Directory, old format - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct OldDirectory - { - public readonly DirectoryHeader header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 47)] - public readonly DirectoryEntry[] entries; - public readonly OldDirectoryTail tail; - } + /// Directory tail, old format + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct OldDirectoryTail + { + public readonly byte lastMark; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly byte[] name; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] parent; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)] + public readonly byte[] title; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] + public readonly byte[] reserved; + public readonly byte endMasSeq; + public readonly uint magic; + public readonly byte checkByte; + } - /// Directory, new format - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct NewDirectory - { - public readonly DirectoryHeader header; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 77)] - public readonly DirectoryEntry[] entries; - public readonly NewDirectoryTail tail; - } + /// Directory, old format + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct OldDirectory + { + public readonly DirectoryHeader header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 47)] + public readonly DirectoryEntry[] entries; + public readonly OldDirectoryTail tail; + } + + /// Directory, new format + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct NewDirectory + { + public readonly DirectoryHeader header; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 77)] + public readonly DirectoryEntry[] entries; + public readonly NewDirectoryTail tail; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AmigaDOS.cs b/Aaru.Filesystems/AmigaDOS.cs index 0c67bfc0e..087ed8cea 100644 --- a/Aaru.Filesystems/AmigaDOS.cs +++ b/Aaru.Filesystems/AmigaDOS.cs @@ -43,485 +43,484 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of Amiga Fast File System (AFFS) +public sealed class AmigaDOSPlugin : IFilesystem { + const uint FFS_MASK = 0x444F5300; + const uint MUFS_MASK = 0x6D754600; + + const uint TYPE_HEADER = 2; + const uint SUBTYPE_ROOT = 1; + /// - /// Implements detection of Amiga Fast File System (AFFS) - public sealed class AmigaDOSPlugin : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public string Name => "Amiga DOS filesystem"; + /// + public Guid Id => new("3c882400-208c-427d-a086-9119852a1bc7"); + /// + public Encoding Encoding { get; private set; } + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint FFS_MASK = 0x444F5300; - const uint MUFS_MASK = 0x6D754600; + if(partition.Start + 4 >= partition.End) + return false; - const uint TYPE_HEADER = 2; - const uint SUBTYPE_ROOT = 1; + // Boot block is unless defined otherwise, 2 blocks + // Funny, you may need boot block to find root block if it's not in standard place just to know size of + // block size and then read the whole boot block. + // However while you can set a block size different from the sector size on formatting, the bootblock block + // size for floppies is the sector size, and for RDB is usually is the hard disk sector size, + // so this is not entirely wrong... + ErrorNumber errno = imagePlugin.ReadSectors(0 + partition.Start, 2, out byte[] sector); - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Name => "Amiga DOS filesystem"; - /// - public Guid Id => new("3c882400-208c-427d-a086-9119852a1bc7"); - /// - public Encoding Encoding { get; private set; } - /// - public string Author => "Natalia Portillo"; + if(errno != ErrorNumber.NoError) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + BootBlock bblk = Marshal.ByteArrayToStructureBigEndian(sector); + + // AROS boot floppies... + if(sector.Length >= 512 && + sector[510] == 0x55 && + sector[511] == 0xAA && + (bblk.diskType & FFS_MASK) != FFS_MASK && + (bblk.diskType & MUFS_MASK) != MUFS_MASK) { - if(partition.Start + 4 >= partition.End) - return false; - - // Boot block is unless defined otherwise, 2 blocks - // Funny, you may need boot block to find root block if it's not in standard place just to know size of - // block size and then read the whole boot block. - // However while you can set a block size different from the sector size on formatting, the bootblock block - // size for floppies is the sector size, and for RDB is usually is the hard disk sector size, - // so this is not entirely wrong... - ErrorNumber errno = imagePlugin.ReadSectors(0 + partition.Start, 2, out byte[] sector); + errno = imagePlugin.ReadSectors(1 + partition.Start, 2, out sector); if(errno != ErrorNumber.NoError) return false; - BootBlock bblk = Marshal.ByteArrayToStructureBigEndian(sector); + bblk = Marshal.ByteArrayToStructureBigEndian(sector); + } - // AROS boot floppies... - if(sector.Length >= 512 && - sector[510] == 0x55 && - sector[511] == 0xAA && - (bblk.diskType & FFS_MASK) != FFS_MASK && - (bblk.diskType & MUFS_MASK) != MUFS_MASK) - { - errno = imagePlugin.ReadSectors(1 + partition.Start, 2, out sector); + // Not FFS or MuFS? + if((bblk.diskType & FFS_MASK) != FFS_MASK && + (bblk.diskType & MUFS_MASK) != MUFS_MASK) + return false; - if(errno != ErrorNumber.NoError) - return false; + // Clear checksum on sector + sector[4] = sector[5] = sector[6] = sector[7] = 0; + uint bsum = AmigaBootChecksum(sector); - bblk = Marshal.ByteArrayToStructureBigEndian(sector); - } + AaruConsole.DebugWriteLine("AmigaDOS plugin", "bblk.checksum = 0x{0:X8}", bblk.checksum); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "bsum = 0x{0:X8}", bsum); - // Not FFS or MuFS? - if((bblk.diskType & FFS_MASK) != FFS_MASK && - (bblk.diskType & MUFS_MASK) != MUFS_MASK) - return false; + ulong bRootPtr = 0; + + // If bootblock is correct, let's take its rootblock pointer + if(bsum == bblk.checksum) + { + bRootPtr = bblk.root_ptr + partition.Start; + AaruConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); + } + + ulong[] rootPtrs = + { + bRootPtr + partition.Start, ((partition.End - partition.Start + 1) / 2) + partition.Start - 2, + ((partition.End - partition.Start + 1) / 2) + partition.Start - 1, + ((partition.End - partition.Start + 1) / 2) + partition.Start, + ((partition.End - partition.Start + 1) / 2) + partition.Start + 4 + }; + + var rblk = new RootBlock(); + + // So to handle even number of sectors + foreach(ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) + { + AaruConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); + + errno = imagePlugin.ReadSector(rootPtr, out sector); + + if(errno != ErrorNumber.NoError) + continue; + + rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.type = {0}", rblk.type); + + if(rblk.type != TYPE_HEADER) + continue; + + rblk.hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C); + + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.hashTableSize = {0}", rblk.hashTableSize); + + uint blockSize = (rblk.hashTableSize + 56) * 4; + uint sectorsPerBlock = (uint)(blockSize / sector.Length); + + AaruConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); + + if(blockSize % sector.Length > 0) + sectorsPerBlock++; + + if(rootPtr + sectorsPerBlock >= partition.End) + continue; + + errno = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock, out sector); + + if(errno != ErrorNumber.NoError) + continue; // Clear checksum on sector - sector[4] = sector[5] = sector[6] = sector[7] = 0; - uint bsum = AmigaBootChecksum(sector); + rblk.checksum = BigEndianBitConverter.ToUInt32(sector, 20); + sector[20] = sector[21] = sector[22] = sector[23] = 0; + uint rsum = AmigaChecksum(sector); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "bblk.checksum = 0x{0:X8}", bblk.checksum); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "bsum = 0x{0:X8}", bsum); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.checksum = 0x{0:X8}", rblk.checksum); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); - ulong bRootPtr = 0; + rblk.sec_type = BigEndianBitConverter.ToUInt32(sector, sector.Length - 4); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.sec_type = {0}", rblk.sec_type); - // If bootblock is correct, let's take its rootblock pointer - if(bsum == bblk.checksum) - { - bRootPtr = bblk.root_ptr + partition.Start; - AaruConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); - } - - ulong[] rootPtrs = - { - bRootPtr + partition.Start, ((partition.End - partition.Start + 1) / 2) + partition.Start - 2, - ((partition.End - partition.Start + 1) / 2) + partition.Start - 1, - ((partition.End - partition.Start + 1) / 2) + partition.Start, - ((partition.End - partition.Start + 1) / 2) + partition.Start + 4 - }; - - var rblk = new RootBlock(); - - // So to handle even number of sectors - foreach(ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) - { - AaruConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); - - errno = imagePlugin.ReadSector(rootPtr, out sector); - - if(errno != ErrorNumber.NoError) - continue; - - rblk.type = BigEndianBitConverter.ToUInt32(sector, 0x00); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.type = {0}", rblk.type); - - if(rblk.type != TYPE_HEADER) - continue; - - rblk.hashTableSize = BigEndianBitConverter.ToUInt32(sector, 0x0C); - - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.hashTableSize = {0}", rblk.hashTableSize); - - uint blockSize = (rblk.hashTableSize + 56) * 4; - uint sectorsPerBlock = (uint)(blockSize / sector.Length); - - AaruConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); - - if(blockSize % sector.Length > 0) - sectorsPerBlock++; - - if(rootPtr + sectorsPerBlock >= partition.End) - continue; - - errno = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock, out sector); - - if(errno != ErrorNumber.NoError) - continue; - - // Clear checksum on sector - rblk.checksum = BigEndianBitConverter.ToUInt32(sector, 20); - sector[20] = sector[21] = sector[22] = sector[23] = 0; - uint rsum = AmigaChecksum(sector); - - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.checksum = 0x{0:X8}", rblk.checksum); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); - - rblk.sec_type = BigEndianBitConverter.ToUInt32(sector, sector.Length - 4); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rblk.sec_type = {0}", rblk.sec_type); - - if(rblk.sec_type == SUBTYPE_ROOT && - rblk.checksum == rsum) - return true; - } - - return false; + if(rblk.sec_type == SUBTYPE_ROOT && + rblk.checksum == rsum) + return true; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + return false; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); + var sbInformation = new StringBuilder(); + XmlFsType = new FileSystemType(); + information = null; + ErrorNumber errno = imagePlugin.ReadSectors(0 + partition.Start, 2, out byte[] bootBlockSectors); + + if(errno != ErrorNumber.NoError) + return; + + BootBlock bootBlk = Marshal.ByteArrayToStructureBigEndian(bootBlockSectors); + bootBlk.bootCode = new byte[bootBlockSectors.Length - 12]; + Array.Copy(bootBlockSectors, 12, bootBlk.bootCode, 0, bootBlk.bootCode.Length); + bootBlockSectors[4] = bootBlockSectors[5] = bootBlockSectors[6] = bootBlockSectors[7] = 0; + uint bsum = AmigaBootChecksum(bootBlockSectors); + + ulong bRootPtr = 0; + + // If bootblock is correct, let's take its rootblock pointer + if(bsum == bootBlk.checksum) { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); - var sbInformation = new StringBuilder(); - XmlFsType = new FileSystemType(); - information = null; - ErrorNumber errno = imagePlugin.ReadSectors(0 + partition.Start, 2, out byte[] bootBlockSectors); + bRootPtr = bootBlk.root_ptr + partition.Start; + AaruConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); + } + + ulong[] rootPtrs = + { + bRootPtr + partition.Start, ((partition.End - partition.Start + 1) / 2) + partition.Start - 2, + ((partition.End - partition.Start + 1) / 2) + partition.Start - 1, + ((partition.End - partition.Start + 1) / 2) + partition.Start, + ((partition.End - partition.Start + 1) / 2) + partition.Start + 4 + }; + + var rootBlk = new RootBlock(); + byte[] rootBlockSector = null; + + bool rootFound = false; + uint blockSize = 0; + + // So to handle even number of sectors + foreach(ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) + { + AaruConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); + + errno = imagePlugin.ReadSector(rootPtr, out rootBlockSector); if(errno != ErrorNumber.NoError) - return; + continue; - BootBlock bootBlk = Marshal.ByteArrayToStructureBigEndian(bootBlockSectors); - bootBlk.bootCode = new byte[bootBlockSectors.Length - 12]; - Array.Copy(bootBlockSectors, 12, bootBlk.bootCode, 0, bootBlk.bootCode.Length); - bootBlockSectors[4] = bootBlockSectors[5] = bootBlockSectors[6] = bootBlockSectors[7] = 0; - uint bsum = AmigaBootChecksum(bootBlockSectors); + rootBlk.type = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x00); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type); - ulong bRootPtr = 0; + if(rootBlk.type != TYPE_HEADER) + continue; - // If bootblock is correct, let's take its rootblock pointer - if(bsum == bootBlk.checksum) - { - bRootPtr = bootBlk.root_ptr + partition.Start; - AaruConsole.DebugWriteLine("AmigaDOS plugin", "Bootblock points to {0} as Rootblock", bRootPtr); - } + rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x0C); - ulong[] rootPtrs = - { - bRootPtr + partition.Start, ((partition.End - partition.Start + 1) / 2) + partition.Start - 2, - ((partition.End - partition.Start + 1) / 2) + partition.Start - 1, - ((partition.End - partition.Start + 1) / 2) + partition.Start, - ((partition.End - partition.Start + 1) / 2) + partition.Start + 4 - }; + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize); - var rootBlk = new RootBlock(); - byte[] rootBlockSector = null; + blockSize = (rootBlk.hashTableSize + 56) * 4; + uint sectorsPerBlock = (uint)(blockSize / rootBlockSector.Length); - bool rootFound = false; - uint blockSize = 0; + AaruConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); - // So to handle even number of sectors - foreach(ulong rootPtr in rootPtrs.Where(rootPtr => rootPtr < partition.End && rootPtr >= partition.Start)) - { - AaruConsole.DebugWriteLine("AmigaDOS plugin", "Searching for Rootblock in sector {0}", rootPtr); + if(blockSize % rootBlockSector.Length > 0) + sectorsPerBlock++; - errno = imagePlugin.ReadSector(rootPtr, out rootBlockSector); + if(rootPtr + sectorsPerBlock >= partition.End) + continue; - if(errno != ErrorNumber.NoError) - continue; + errno = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock, out rootBlockSector); - rootBlk.type = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x00); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.type = {0}", rootBlk.type); + if(errno != ErrorNumber.NoError) + continue; - if(rootBlk.type != TYPE_HEADER) - continue; + // Clear checksum on sector + rootBlk.checksum = BigEndianBitConverter.ToUInt32(rootBlockSector, 20); + rootBlockSector[20] = rootBlockSector[21] = rootBlockSector[22] = rootBlockSector[23] = 0; + uint rsum = AmigaChecksum(rootBlockSector); - rootBlk.hashTableSize = BigEndianBitConverter.ToUInt32(rootBlockSector, 0x0C); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.checksum = 0x{0:X8}", rootBlk.checksum); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.hashTableSize = {0}", rootBlk.hashTableSize); + rootBlk.sec_type = BigEndianBitConverter.ToUInt32(rootBlockSector, rootBlockSector.Length - 4); + AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.sec_type = {0}", rootBlk.sec_type); - blockSize = (rootBlk.hashTableSize + 56) * 4; - uint sectorsPerBlock = (uint)(blockSize / rootBlockSector.Length); + if(rootBlk.sec_type != SUBTYPE_ROOT || + rootBlk.checksum != rsum) + continue; - AaruConsole.DebugWriteLine("AmigaDOS plugin", "blockSize = {0}", blockSize); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "sectorsPerBlock = {0}", sectorsPerBlock); + errno = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock, out rootBlockSector); - if(blockSize % rootBlockSector.Length > 0) - sectorsPerBlock++; + if(errno != ErrorNumber.NoError) + continue; - if(rootPtr + sectorsPerBlock >= partition.End) - continue; + rootFound = true; - errno = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock, out rootBlockSector); + break; + } - if(errno != ErrorNumber.NoError) - continue; + if(!rootFound) + return; - // Clear checksum on sector - rootBlk.checksum = BigEndianBitConverter.ToUInt32(rootBlockSector, 20); - rootBlockSector[20] = rootBlockSector[21] = rootBlockSector[22] = rootBlockSector[23] = 0; - uint rsum = AmigaChecksum(rootBlockSector); + rootBlk = MarshalRootBlock(rootBlockSector); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.checksum = 0x{0:X8}", rootBlk.checksum); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rsum = 0x{0:X8}", rsum); + string diskName = StringHandlers.PascalToString(rootBlk.diskName, Encoding); - rootBlk.sec_type = BigEndianBitConverter.ToUInt32(rootBlockSector, rootBlockSector.Length - 4); - AaruConsole.DebugWriteLine("AmigaDOS plugin", "rootBlk.sec_type = {0}", rootBlk.sec_type); - - if(rootBlk.sec_type != SUBTYPE_ROOT || - rootBlk.checksum != rsum) - continue; - - errno = imagePlugin.ReadSectors(rootPtr, sectorsPerBlock, out rootBlockSector); - - if(errno != ErrorNumber.NoError) - continue; - - rootFound = true; + switch(bootBlk.diskType & 0xFF) + { + case 0: + sbInformation.Append("Amiga Original File System"); + XmlFsType.Type = "Amiga OFS"; break; - } + case 1: + sbInformation.Append("Amiga Fast File System"); + XmlFsType.Type = "Amiga FFS"; - if(!rootFound) - return; + break; + case 2: + sbInformation.Append("Amiga Original File System with international characters"); + XmlFsType.Type = "Amiga OFS"; - rootBlk = MarshalRootBlock(rootBlockSector); + break; + case 3: + sbInformation.Append("Amiga Fast File System with international characters"); + XmlFsType.Type = "Amiga FFS"; - string diskName = StringHandlers.PascalToString(rootBlk.diskName, Encoding); + break; + case 4: + sbInformation.Append("Amiga Original File System with directory cache"); + XmlFsType.Type = "Amiga OFS"; - switch(bootBlk.diskType & 0xFF) - { - case 0: - sbInformation.Append("Amiga Original File System"); - XmlFsType.Type = "Amiga OFS"; + break; + case 5: + sbInformation.Append("Amiga Fast File System with directory cache"); + XmlFsType.Type = "Amiga FFS"; - break; - case 1: - sbInformation.Append("Amiga Fast File System"); - XmlFsType.Type = "Amiga FFS"; + break; + case 6: + sbInformation.Append("Amiga Original File System with long filenames"); + XmlFsType.Type = "Amiga OFS2"; - break; - case 2: - sbInformation.Append("Amiga Original File System with international characters"); - XmlFsType.Type = "Amiga OFS"; + break; + case 7: + sbInformation.Append("Amiga Fast File System with long filenames"); + XmlFsType.Type = "Amiga FFS2"; - break; - case 3: - sbInformation.Append("Amiga Fast File System with international characters"); - XmlFsType.Type = "Amiga FFS"; - - break; - case 4: - sbInformation.Append("Amiga Original File System with directory cache"); - XmlFsType.Type = "Amiga OFS"; - - break; - case 5: - sbInformation.Append("Amiga Fast File System with directory cache"); - XmlFsType.Type = "Amiga FFS"; - - break; - case 6: - sbInformation.Append("Amiga Original File System with long filenames"); - XmlFsType.Type = "Amiga OFS2"; - - break; - case 7: - sbInformation.Append("Amiga Fast File System with long filenames"); - XmlFsType.Type = "Amiga FFS2"; - - break; - } - - if((bootBlk.diskType & 0x6D754600) == 0x6D754600) - sbInformation.Append(", with multi-user patches"); - - sbInformation.AppendLine(); - - sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine(); - - if(bootBlk.checksum == bsum) - { - var sha1Ctx = new Sha1Context(); - sha1Ctx.Update(bootBlk.bootCode); - sbInformation.AppendLine("Volume is bootable"); - sbInformation.AppendFormat("Boot code SHA1 is {0}", sha1Ctx.End()).AppendLine(); - } - - if(rootBlk.bitmapFlag == 0xFFFFFFFF) - sbInformation.AppendLine("Volume bitmap is valid"); - - if(rootBlk.bitmapExtensionBlock != 0x00000000 && - rootBlk.bitmapExtensionBlock != 0xFFFFFFFF) - sbInformation.AppendFormat("Bitmap extension at block {0}", rootBlk.bitmapExtensionBlock).AppendLine(); - - if((bootBlk.diskType & 0xFF) == 4 || - (bootBlk.diskType & 0xFF) == 5) - sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine(); - - ulong blocks = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / blockSize; - - sbInformation.AppendFormat("Volume block size is {0} bytes", blockSize).AppendLine(); - sbInformation.AppendFormat("Volume has {0} blocks", blocks).AppendLine(); - - sbInformation.AppendFormat("Volume created on {0}", - DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks)). - AppendLine(); - - sbInformation.AppendFormat("Volume last modified on {0}", - DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks)). - AppendLine(); - - sbInformation.AppendFormat("Volume root directory last modified on on {0}", - DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks)). - AppendLine(); - - sbInformation.AppendFormat("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine(); - information = sbInformation.ToString(); - - XmlFsType.CreationDate = DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks); - - XmlFsType.CreationDateSpecified = true; - - XmlFsType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks); - - XmlFsType.ModificationDateSpecified = true; - XmlFsType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF; - XmlFsType.Clusters = blocks; - XmlFsType.ClusterSize = blockSize; - XmlFsType.VolumeName = diskName; - XmlFsType.Bootable = bsum == bootBlk.checksum; - - // Useful as a serial - XmlFsType.VolumeSerial = $"{rootBlk.checksum:X8}"; + break; } - static RootBlock MarshalRootBlock(byte[] block) + if((bootBlk.diskType & 0x6D754600) == 0x6D754600) + sbInformation.Append(", with multi-user patches"); + + sbInformation.AppendLine(); + + sbInformation.AppendFormat("Volume name: {0}", diskName).AppendLine(); + + if(bootBlk.checksum == bsum) { - byte[] tmp = new byte[228]; - Array.Copy(block, 0, tmp, 0, 24); - Array.Copy(block, block.Length - 200, tmp, 28, 200); - RootBlock root = Marshal.ByteArrayToStructureBigEndian(tmp); - root.hashTable = new uint[(block.Length - 224) / 4]; - - for(int i = 0; i < root.hashTable.Length; i++) - root.hashTable[i] = BigEndianBitConverter.ToUInt32(block, 24 + (i * 4)); - - return root; + var sha1Ctx = new Sha1Context(); + sha1Ctx.Update(bootBlk.bootCode); + sbInformation.AppendLine("Volume is bootable"); + sbInformation.AppendFormat("Boot code SHA1 is {0}", sha1Ctx.End()).AppendLine(); } - static uint AmigaChecksum(byte[] data) + if(rootBlk.bitmapFlag == 0xFFFFFFFF) + sbInformation.AppendLine("Volume bitmap is valid"); + + if(rootBlk.bitmapExtensionBlock != 0x00000000 && + rootBlk.bitmapExtensionBlock != 0xFFFFFFFF) + sbInformation.AppendFormat("Bitmap extension at block {0}", rootBlk.bitmapExtensionBlock).AppendLine(); + + if((bootBlk.diskType & 0xFF) == 4 || + (bootBlk.diskType & 0xFF) == 5) + sbInformation.AppendFormat("Directory cache starts at block {0}", rootBlk.extension).AppendLine(); + + ulong blocks = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / blockSize; + + sbInformation.AppendFormat("Volume block size is {0} bytes", blockSize).AppendLine(); + sbInformation.AppendFormat("Volume has {0} blocks", blocks).AppendLine(); + + sbInformation.AppendFormat("Volume created on {0}", + DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks)). + AppendLine(); + + sbInformation.AppendFormat("Volume last modified on {0}", + DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks)). + AppendLine(); + + sbInformation.AppendFormat("Volume root directory last modified on on {0}", + DateHandlers.AmigaToDateTime(rootBlk.rDays, rootBlk.rMins, rootBlk.rTicks)). + AppendLine(); + + sbInformation.AppendFormat("Root block checksum is 0x{0:X8}", rootBlk.checksum).AppendLine(); + information = sbInformation.ToString(); + + XmlFsType.CreationDate = DateHandlers.AmigaToDateTime(rootBlk.cDays, rootBlk.cMins, rootBlk.cTicks); + + XmlFsType.CreationDateSpecified = true; + + XmlFsType.ModificationDate = DateHandlers.AmigaToDateTime(rootBlk.vDays, rootBlk.vMins, rootBlk.vTicks); + + XmlFsType.ModificationDateSpecified = true; + XmlFsType.Dirty = rootBlk.bitmapFlag != 0xFFFFFFFF; + XmlFsType.Clusters = blocks; + XmlFsType.ClusterSize = blockSize; + XmlFsType.VolumeName = diskName; + XmlFsType.Bootable = bsum == bootBlk.checksum; + + // Useful as a serial + XmlFsType.VolumeSerial = $"{rootBlk.checksum:X8}"; + } + + static RootBlock MarshalRootBlock(byte[] block) + { + byte[] tmp = new byte[228]; + Array.Copy(block, 0, tmp, 0, 24); + Array.Copy(block, block.Length - 200, tmp, 28, 200); + RootBlock root = Marshal.ByteArrayToStructureBigEndian(tmp); + root.hashTable = new uint[(block.Length - 224) / 4]; + + for(int i = 0; i < root.hashTable.Length; i++) + root.hashTable[i] = BigEndianBitConverter.ToUInt32(block, 24 + (i * 4)); + + return root; + } + + static uint AmigaChecksum(byte[] data) + { + uint sum = 0; + + for(int i = 0; i < data.Length; i += 4) + sum += (uint)((data[i] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3]); + + return (uint)-sum; + } + + static uint AmigaBootChecksum(byte[] data) + { + uint sum = 0; + + for(int i = 0; i < data.Length; i += 4) { - uint sum = 0; + uint psum = sum; - for(int i = 0; i < data.Length; i += 4) - sum += (uint)((data[i] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3]); - - return (uint)-sum; + if((sum += (uint)((data[i] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3])) < psum) + sum++; } - static uint AmigaBootChecksum(byte[] data) - { - uint sum = 0; + return ~sum; + } - for(int i = 0; i < data.Length; i += 4) - { - uint psum = sum; + /// Boot block, first 2 sectors + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BootBlock + { + /// Offset 0x00, "DOSx" disk type + public readonly uint diskType; + /// Offset 0x04, Checksum + public readonly uint checksum; + /// Offset 0x08, Pointer to root block, mostly invalid + public readonly uint root_ptr; + /// Offset 0x0C, Boot code, til completion. Size is intentionally incorrect to allow marshaling to work. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public byte[] bootCode; + } - if((sum += (uint)((data[i] << 24) + (data[i + 1] << 16) + (data[i + 2] << 8) + data[i + 3])) < psum) - sum++; - } - - return ~sum; - } - - /// Boot block, first 2 sectors - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BootBlock - { - /// Offset 0x00, "DOSx" disk type - public readonly uint diskType; - /// Offset 0x04, Checksum - public readonly uint checksum; - /// Offset 0x08, Pointer to root block, mostly invalid - public readonly uint root_ptr; - /// Offset 0x0C, Boot code, til completion. Size is intentionally incorrect to allow marshaling to work. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] - public byte[] bootCode; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct RootBlock - { - /// Offset 0x00, block type, value = T_HEADER (2) - public uint type; - /// Offset 0x04, unused - public readonly uint headerKey; - /// Offset 0x08, unused - public readonly uint highSeq; - /// Offset 0x0C, longs used by hash table - public uint hashTableSize; - /// Offset 0x10, unused - public readonly uint firstData; - /// Offset 0x14, Rootblock checksum - public uint checksum; - /// - /// Offset 0x18, Hashtable, size = (block size / 4) - 56 or size = hashTableSize. Size intentionally bad to allow - /// marshalling to work. - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] - public uint[] hashTable; - /// Offset 0x18+hashTableSize*4+0, bitmap flag, 0xFFFFFFFF if valid - public readonly uint bitmapFlag; - /// Offset 0x18+hashTableSize*4+4, bitmap pages, 25 entries - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)] - public readonly uint[] bitmapPages; - /// Offset 0x18+hashTableSize*4+104, pointer to bitmap extension block - public readonly uint bitmapExtensionBlock; - /// Offset 0x18+hashTableSize*4+108, last root alteration days since 1978/01/01 - public readonly uint rDays; - /// Offset 0x18+hashTableSize*4+112, last root alteration minutes past midnight - public readonly uint rMins; - /// Offset 0x18+hashTableSize*4+116, last root alteration ticks (1/50 secs) - public readonly uint rTicks; - /// Offset 0x18+hashTableSize*4+120, disk name, pascal string, 31 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)] - public readonly byte[] diskName; - /// Offset 0x18+hashTableSize*4+151, unused - public readonly byte padding; - /// Offset 0x18+hashTableSize*4+152, unused - public readonly uint reserved1; - /// Offset 0x18+hashTableSize*4+156, unused - public readonly uint reserved2; - /// Offset 0x18+hashTableSize*4+160, last disk alteration days since 1978/01/01 - public readonly uint vDays; - /// Offset 0x18+hashTableSize*4+164, last disk alteration minutes past midnight - public readonly uint vMins; - /// Offset 0x18+hashTableSize*4+168, last disk alteration ticks (1/50 secs) - public readonly uint vTicks; - /// Offset 0x18+hashTableSize*4+172, filesystem creation days since 1978/01/01 - public readonly uint cDays; - /// Offset 0x18+hashTableSize*4+176, filesystem creation minutes since 1978/01/01 - public readonly uint cMins; - /// Offset 0x18+hashTableSize*4+180, filesystem creation ticks since 1978/01/01 - public readonly uint cTicks; - /// Offset 0x18+hashTableSize*4+184, unused - public readonly uint nextHash; - /// Offset 0x18+hashTableSize*4+188, unused - public readonly uint parentDir; - /// Offset 0x18+hashTableSize*4+192, first directory cache block - public readonly uint extension; - /// Offset 0x18+hashTableSize*4+196, block secondary type = ST_ROOT (1) - public uint sec_type; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct RootBlock + { + /// Offset 0x00, block type, value = T_HEADER (2) + public uint type; + /// Offset 0x04, unused + public readonly uint headerKey; + /// Offset 0x08, unused + public readonly uint highSeq; + /// Offset 0x0C, longs used by hash table + public uint hashTableSize; + /// Offset 0x10, unused + public readonly uint firstData; + /// Offset 0x14, Rootblock checksum + public uint checksum; + /// + /// Offset 0x18, Hashtable, size = (block size / 4) - 56 or size = hashTableSize. Size intentionally bad to allow + /// marshalling to work. + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public uint[] hashTable; + /// Offset 0x18+hashTableSize*4+0, bitmap flag, 0xFFFFFFFF if valid + public readonly uint bitmapFlag; + /// Offset 0x18+hashTableSize*4+4, bitmap pages, 25 entries + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 25)] + public readonly uint[] bitmapPages; + /// Offset 0x18+hashTableSize*4+104, pointer to bitmap extension block + public readonly uint bitmapExtensionBlock; + /// Offset 0x18+hashTableSize*4+108, last root alteration days since 1978/01/01 + public readonly uint rDays; + /// Offset 0x18+hashTableSize*4+112, last root alteration minutes past midnight + public readonly uint rMins; + /// Offset 0x18+hashTableSize*4+116, last root alteration ticks (1/50 secs) + public readonly uint rTicks; + /// Offset 0x18+hashTableSize*4+120, disk name, pascal string, 31 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)] + public readonly byte[] diskName; + /// Offset 0x18+hashTableSize*4+151, unused + public readonly byte padding; + /// Offset 0x18+hashTableSize*4+152, unused + public readonly uint reserved1; + /// Offset 0x18+hashTableSize*4+156, unused + public readonly uint reserved2; + /// Offset 0x18+hashTableSize*4+160, last disk alteration days since 1978/01/01 + public readonly uint vDays; + /// Offset 0x18+hashTableSize*4+164, last disk alteration minutes past midnight + public readonly uint vMins; + /// Offset 0x18+hashTableSize*4+168, last disk alteration ticks (1/50 secs) + public readonly uint vTicks; + /// Offset 0x18+hashTableSize*4+172, filesystem creation days since 1978/01/01 + public readonly uint cDays; + /// Offset 0x18+hashTableSize*4+176, filesystem creation minutes since 1978/01/01 + public readonly uint cMins; + /// Offset 0x18+hashTableSize*4+180, filesystem creation ticks since 1978/01/01 + public readonly uint cTicks; + /// Offset 0x18+hashTableSize*4+184, unused + public readonly uint nextHash; + /// Offset 0x18+hashTableSize*4+188, unused + public readonly uint parentDir; + /// Offset 0x18+hashTableSize*4+192, first directory cache block + public readonly uint extension; + /// Offset 0x18+hashTableSize*4+196, block secondary type = ST_ROOT (1) + public uint sec_type; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleCommon/Consts.cs b/Aaru.Filesystems/AppleCommon/Consts.cs index 14f539538..9d766cc00 100644 --- a/Aaru.Filesystems/AppleCommon/Consts.cs +++ b/Aaru.Filesystems/AppleCommon/Consts.cs @@ -30,19 +30,18 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh +// https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf +internal static partial class AppleCommon { - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf - internal static partial class AppleCommon - { - /// "LK", HFS bootblock magic - internal const ushort BB_MAGIC = 0x4C4B; - /// "BD", HFS magic - internal const ushort HFS_MAGIC = 0x4244; - /// "H+", HFS+ magic - internal const ushort HFSP_MAGIC = 0x482B; - /// "HX", HFSX magic - internal const ushort HFSX_MAGIC = 0x4858; - } + /// "LK", HFS bootblock magic + internal const ushort BB_MAGIC = 0x4C4B; + /// "BD", HFS magic + internal const ushort HFS_MAGIC = 0x4244; + /// "H+", HFS+ magic + internal const ushort HFSP_MAGIC = 0x482B; + /// "HX", HFSX magic + internal const ushort HFSX_MAGIC = 0x4858; } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleCommon/Enums.cs b/Aaru.Filesystems/AppleCommon/Enums.cs index 6e9bd49d8..94f348159 100644 --- a/Aaru.Filesystems/AppleCommon/Enums.cs +++ b/Aaru.Filesystems/AppleCommon/Enums.cs @@ -34,68 +34,67 @@ using System; // ReSharper disable InconsistentNaming -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh +// https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf +internal static partial class AppleCommon { - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf - internal static partial class AppleCommon + [Flags] + internal enum VolumeAttributes : ushort { - [Flags] - internal enum VolumeAttributes : ushort - { - HardwareLock = 0x80, Unmounted = 0x100, SparedBadBlocks = 0x200, - DoesNotNeedCache = 0x400, BootInconsistent = 0x800, ReusedIds = 0x1000, - Journaled = 0x2000, Inconsistent = 0x4000, SoftwareLock = 0x8000 - } + HardwareLock = 0x80, Unmounted = 0x100, SparedBadBlocks = 0x200, + DoesNotNeedCache = 0x400, BootInconsistent = 0x800, ReusedIds = 0x1000, + Journaled = 0x2000, Inconsistent = 0x4000, SoftwareLock = 0x8000 + } - [Flags] - internal enum FinderFlags : ushort - { - /// Is on desktop. - kIsOnDesk = 0x0001, - /// Color mask. - kColor = 0x000E, kRequireSwitchLaunch = 0x0020, - /// If clear, the application needs to write to its resource fork, and therefore cannot be shared on a server. - kIsShared = 0x0040, - /// Extension or control panel with no INIT entries in resource fork. - kHasNoINITs = 0x0080, - /// - /// Clear if the file contains desktop database resources ('BNDL', 'FREF', 'open', 'kind'...) that have not been - /// added yet. Set only by the Finder. Reserved for folders - make sure this bit is cleared for folders. - /// - kHasBeenInited = 0x0100, - /// PowerTalk - kAOCE = 0x200, kChanged = 0x0200, - /// Has a custom icon in the resource fork. - kHasCustomIcon = 0x0400, - /// Is a stationery. - kIsStationery = 0x0800, - /// Cannot be renamed. - kNameLocked = 0x1000, - /// Indicates that a file has a BNDL resource or that a folder is displayed as a package. - kHasBundle = 0x2000, - /// Hidden. - kIsInvisible = 0x4000, - /// Is an alias - kIsAlias = 0x8000 - } + [Flags] + internal enum FinderFlags : ushort + { + /// Is on desktop. + kIsOnDesk = 0x0001, + /// Color mask. + kColor = 0x000E, kRequireSwitchLaunch = 0x0020, + /// If clear, the application needs to write to its resource fork, and therefore cannot be shared on a server. + kIsShared = 0x0040, + /// Extension or control panel with no INIT entries in resource fork. + kHasNoINITs = 0x0080, + /// + /// Clear if the file contains desktop database resources ('BNDL', 'FREF', 'open', 'kind'...) that have not been + /// added yet. Set only by the Finder. Reserved for folders - make sure this bit is cleared for folders. + /// + kHasBeenInited = 0x0100, + /// PowerTalk + kAOCE = 0x200, kChanged = 0x0200, + /// Has a custom icon in the resource fork. + kHasCustomIcon = 0x0400, + /// Is a stationery. + kIsStationery = 0x0800, + /// Cannot be renamed. + kNameLocked = 0x1000, + /// Indicates that a file has a BNDL resource or that a folder is displayed as a package. + kHasBundle = 0x2000, + /// Hidden. + kIsInvisible = 0x4000, + /// Is an alias + kIsAlias = 0x8000 + } - internal enum FinderFolder : short - { - fTrash = -3, fDesktop = -2, fDisk = 0 - } + internal enum FinderFolder : short + { + fTrash = -3, fDesktop = -2, fDisk = 0 + } - [Flags] - internal enum ExtendedFinderFlags : ushort - { - /// If set the other extended flags are ignored. - kExtendedFlagsAreInvalid = 0x8000, - /// Set if the file or folder has a badge resource. - kExtendedFlagHasCustomBadge = 0x0100, - /// Set if the object is marked as busy/incomplete. - kExtendedFlagObjectIsBusy = 0x0080, - /// Set if the file contains routing info resource. - kExtendedFlagHasRoutingInfo = 0x0004 - } + [Flags] + internal enum ExtendedFinderFlags : ushort + { + /// If set the other extended flags are ignored. + kExtendedFlagsAreInvalid = 0x8000, + /// Set if the file or folder has a badge resource. + kExtendedFlagHasCustomBadge = 0x0100, + /// Set if the object is marked as busy/incomplete. + kExtendedFlagObjectIsBusy = 0x0080, + /// Set if the file contains routing info resource. + kExtendedFlagHasRoutingInfo = 0x0004 } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleCommon/Info.cs b/Aaru.Filesystems/AppleCommon/Info.cs index 98d4df7dc..1b42af281 100644 --- a/Aaru.Filesystems/AppleCommon/Info.cs +++ b/Aaru.Filesystems/AppleCommon/Info.cs @@ -33,75 +33,74 @@ using System.Text; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh +// https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf +internal static partial class AppleCommon { - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf - internal static partial class AppleCommon + internal static string GetBootBlockInformation(byte[] bbSector, Encoding encoding) { - internal static string GetBootBlockInformation(byte[] bbSector, Encoding encoding) + if(bbSector is null || + bbSector.Length < 0x100) + return null; + + BootBlock bb = Marshal.ByteArrayToStructureBigEndian(bbSector); + + if(bb.bbID != BB_MAGIC) + return null; + + var sb = new StringBuilder(); + sb.AppendLine("Boot Block:"); + + if((bb.bbVersion & 0x8000) > 0) { - if(bbSector is null || - bbSector.Length < 0x100) - return null; + sb.AppendLine("Boot block is in new format."); - BootBlock bb = Marshal.ByteArrayToStructureBigEndian(bbSector); - - if(bb.bbID != BB_MAGIC) - return null; - - var sb = new StringBuilder(); - sb.AppendLine("Boot Block:"); - - if((bb.bbVersion & 0x8000) > 0) + if((bb.bbVersion & 0x4000) > 0) { - sb.AppendLine("Boot block is in new format."); - - if((bb.bbVersion & 0x4000) > 0) - { - sb.AppendLine("Boot block should be executed."); - - if((bb.bbVersion & 0x2000) > 0) - sb. - AppendFormat("System heap will be extended by {0} bytes and a {1} fraction of the available RAM", - bb.bbSysHeapExtra, bb.bbSysHeapFract).AppendLine(); - } - } - else if((bb.bbVersion & 0xFF) == 0x0D) sb.AppendLine("Boot block should be executed."); - if(bb.bbPageFlags > 0) - sb.AppendLine("Allocate secondary sound buffer at boot."); - else if(bb.bbPageFlags < 0) - sb.AppendLine("Allocate secondary sound and video buffers at boot."); - - sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.bbSysName, encoding)).AppendLine(); - - sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.bbShellName, encoding)). - AppendLine(); - - sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.bbDbg1Name, encoding)). - AppendLine(); - - sb.AppendFormat("Disassembler filename: {0}", StringHandlers.PascalToString(bb.bbDbg2Name, encoding)). - AppendLine(); - - sb.AppendFormat("Startup screen filename: {0}", StringHandlers.PascalToString(bb.bbScreenName, encoding)). - AppendLine(); - - sb.AppendFormat("First program to execute at boot: {0}", - StringHandlers.PascalToString(bb.bbHelloName, encoding)).AppendLine(); - - sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.bbScrapName, encoding)). - AppendLine(); - - sb.AppendFormat("Maximum opened files: {0}", bb.bbCntFCBs * 4).AppendLine(); - sb.AppendFormat("Event queue size: {0}", bb.bbCntEvts).AppendLine(); - sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.bb128KSHeap).AppendLine(); - sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.bb256KSHeap).AppendLine(); - sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.bbSysHeapSize).AppendLine(); - - return sb.ToString(); + if((bb.bbVersion & 0x2000) > 0) + sb. + AppendFormat("System heap will be extended by {0} bytes and a {1} fraction of the available RAM", + bb.bbSysHeapExtra, bb.bbSysHeapFract).AppendLine(); + } } + else if((bb.bbVersion & 0xFF) == 0x0D) + sb.AppendLine("Boot block should be executed."); + + if(bb.bbPageFlags > 0) + sb.AppendLine("Allocate secondary sound buffer at boot."); + else if(bb.bbPageFlags < 0) + sb.AppendLine("Allocate secondary sound and video buffers at boot."); + + sb.AppendFormat("System filename: {0}", StringHandlers.PascalToString(bb.bbSysName, encoding)).AppendLine(); + + sb.AppendFormat("Finder filename: {0}", StringHandlers.PascalToString(bb.bbShellName, encoding)). + AppendLine(); + + sb.AppendFormat("Debugger filename: {0}", StringHandlers.PascalToString(bb.bbDbg1Name, encoding)). + AppendLine(); + + sb.AppendFormat("Disassembler filename: {0}", StringHandlers.PascalToString(bb.bbDbg2Name, encoding)). + AppendLine(); + + sb.AppendFormat("Startup screen filename: {0}", StringHandlers.PascalToString(bb.bbScreenName, encoding)). + AppendLine(); + + sb.AppendFormat("First program to execute at boot: {0}", + StringHandlers.PascalToString(bb.bbHelloName, encoding)).AppendLine(); + + sb.AppendFormat("Clipboard filename: {0}", StringHandlers.PascalToString(bb.bbScrapName, encoding)). + AppendLine(); + + sb.AppendFormat("Maximum opened files: {0}", bb.bbCntFCBs * 4).AppendLine(); + sb.AppendFormat("Event queue size: {0}", bb.bbCntEvts).AppendLine(); + sb.AppendFormat("Heap size with 128KiB of RAM: {0} bytes", bb.bb128KSHeap).AppendLine(); + sb.AppendFormat("Heap size with 256KiB of RAM: {0} bytes", bb.bb256KSHeap).AppendLine(); + sb.AppendFormat("Heap size with 512KiB of RAM or more: {0} bytes", bb.bbSysHeapSize).AppendLine(); + + return sb.ToString(); } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleCommon/Structs.cs b/Aaru.Filesystems/AppleCommon/Structs.cs index 57cbde0ae..ba3fc09b1 100644 --- a/Aaru.Filesystems/AppleCommon/Structs.cs +++ b/Aaru.Filesystems/AppleCommon/Structs.cs @@ -34,129 +34,128 @@ using System.Runtime.InteropServices; // ReSharper disable InconsistentNaming -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh +// https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf +internal static partial class AppleCommon { - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf - internal static partial class AppleCommon + /// Should be sectors 0 and 1 in volume, followed by boot code + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BootBlock // Should be sectors 0 and 1 in volume { - /// Should be sectors 0 and 1 in volume, followed by boot code - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BootBlock // Should be sectors 0 and 1 in volume - { - /// 0x000, Signature, 0x4C4B if bootable - public readonly ushort bbID; - /// 0x002, Branch - public readonly uint bbEntry; - /// 0x007, Boot block version and flags - public readonly ushort bbVersion; - /// 0x006, Boot block page flags - public readonly short bbPageFlags; - /// 0x00A, System file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] bbSysName; - /// 0x01A, Finder file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] bbShellName; - /// 0x02A, Debugger file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] bbDbg1Name; - /// 0x03A, Disassembler file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] bbDbg2Name; - /// 0x04A, Startup screen file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] bbScreenName; - /// 0x05A, First program to execute on boot (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] bbHelloName; - /// 0x06A, Clipboard file name (16 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] bbScrapName; - /// 0x07A, 1/4 of maximum opened at a time files - public readonly ushort bbCntFCBs; - /// 0x07C, Event queue size - public readonly ushort bbCntEvts; - /// 0x07E, Heap size on a Mac with 128KiB of RAM - public readonly uint bb128KSHeap; - /// 0x082, Heap size on a Mac with 256KiB of RAM - public readonly uint bb256KSHeap; - /// 0x086, Heap size on a Mac with 512KiB of RAM or more - public readonly uint bbSysHeapSize; - /// 0x08A, Padding - public readonly ushort filler; - /// 0x08C, Additional system heap space - public readonly uint bbSysHeapExtra; - /// 0x090, Fraction of RAM for system heap - public readonly uint bbSysHeapFract; - } + /// 0x000, Signature, 0x4C4B if bootable + public readonly ushort bbID; + /// 0x002, Branch + public readonly uint bbEntry; + /// 0x007, Boot block version and flags + public readonly ushort bbVersion; + /// 0x006, Boot block page flags + public readonly short bbPageFlags; + /// 0x00A, System file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] bbSysName; + /// 0x01A, Finder file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] bbShellName; + /// 0x02A, Debugger file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] bbDbg1Name; + /// 0x03A, Disassembler file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] bbDbg2Name; + /// 0x04A, Startup screen file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] bbScreenName; + /// 0x05A, First program to execute on boot (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] bbHelloName; + /// 0x06A, Clipboard file name (16 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] bbScrapName; + /// 0x07A, 1/4 of maximum opened at a time files + public readonly ushort bbCntFCBs; + /// 0x07C, Event queue size + public readonly ushort bbCntEvts; + /// 0x07E, Heap size on a Mac with 128KiB of RAM + public readonly uint bb128KSHeap; + /// 0x082, Heap size on a Mac with 256KiB of RAM + public readonly uint bb256KSHeap; + /// 0x086, Heap size on a Mac with 512KiB of RAM or more + public readonly uint bbSysHeapSize; + /// 0x08A, Padding + public readonly ushort filler; + /// 0x08C, Additional system heap space + public readonly uint bbSysHeapExtra; + /// 0x090, Fraction of RAM for system heap + public readonly uint bbSysHeapFract; + } - internal struct Rect - { - public ushort top; - public ushort left; - public ushort bottom; - public ushort right; - } + internal struct Rect + { + public ushort top; + public ushort left; + public ushort bottom; + public ushort right; + } - internal struct Point - { - public ushort v; - public ushort h; - } + internal struct Point + { + public ushort v; + public ushort h; + } - internal struct FInfo - { - /// The type of the file. - public uint fdType; - /// The file's creator. - public uint fdCreator; - /// Flags. - public FinderFlags fdFlags; - /// File's location in the folder. - public Point fdLocation; - /// Folder file belongs to (used only in flat filesystems like MFS). - public FinderFolder fdFldr; - } + internal struct FInfo + { + /// The type of the file. + public uint fdType; + /// The file's creator. + public uint fdCreator; + /// Flags. + public FinderFlags fdFlags; + /// File's location in the folder. + public Point fdLocation; + /// Folder file belongs to (used only in flat filesystems like MFS). + public FinderFolder fdFldr; + } - internal struct FXInfo - { - /// Resource fork ID of file icon. - public ushort fdIconID; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public byte[] fdUnused; - /// Extended flags. If high-bit is set, most significant byte is script code and least significant byte are flags. - public ExtendedFinderFlags fdXFlags; - /// Resource fork ID of directory comment if high bit is clear. - public ushort fdComment; - /// Put away folder ID. - public uint fdPutAway; - } + internal struct FXInfo + { + /// Resource fork ID of file icon. + public ushort fdIconID; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public byte[] fdUnused; + /// Extended flags. If high-bit is set, most significant byte is script code and least significant byte are flags. + public ExtendedFinderFlags fdXFlags; + /// Resource fork ID of directory comment if high bit is clear. + public ushort fdComment; + /// Put away folder ID. + public uint fdPutAway; + } - internal struct DInfo - { - /// Position and dimensions of the folder's window. - public Rect frRect; - /// Flags. - public FinderFlags frFlags; - /// Folder's location in the parent folder. - public Point frLocation; - /// Finder view selected for folder. - public ushort frView; - } + internal struct DInfo + { + /// Position and dimensions of the folder's window. + public Rect frRect; + /// Flags. + public FinderFlags frFlags; + /// Folder's location in the parent folder. + public Point frLocation; + /// Finder view selected for folder. + public ushort frView; + } - internal struct DXInfo - { - /// Scroll position for icon views. - public Point frScroll; - /// Directory ID chain of open folders. - public uint frOpenChain; - /// Extended flags. If high-bit is set, most significant byte is script code and least significant byte are flags. - public ExtendedFinderFlags frXFlags; - /// Resource fork ID of directory comment if high bit is clear. - public ushort frComment; - /// Put away folder ID. - public uint frPutAway; - } + internal struct DXInfo + { + /// Scroll position for icon views. + public Point frScroll; + /// Directory ID chain of open folders. + public uint frOpenChain; + /// Extended flags. If high-bit is set, most significant byte is script code and least significant byte are flags. + public ExtendedFinderFlags frXFlags; + /// Resource fork ID of directory comment if high bit is clear. + public ushort frComment; + /// Put away folder ID. + public uint frPutAway; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleDOS/AppleDOS.cs b/Aaru.Filesystems/AppleDOS/AppleDOS.cs index 22817c141..19f7df9aa 100644 --- a/Aaru.Filesystems/AppleDOS/AppleDOS.cs +++ b/Aaru.Filesystems/AppleDOS/AppleDOS.cs @@ -36,68 +36,67 @@ using System.Text; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements the Apple DOS 3 filesystem +public sealed partial class AppleDOS : IReadOnlyFilesystem { + bool _debug; + IMediaImage _device; + bool _mounted; + int _sectorsPerTrack; + ulong _start; + ulong _totalFileEntries; + bool _track1UsedByFiles; + bool _track2UsedByFiles; + uint _usedSectors; + Vtoc _vtoc; + /// - /// Implements the Apple DOS 3 filesystem - public sealed partial class AppleDOS : IReadOnlyFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Apple DOS File System"; + /// + public Guid Id => new("8658A1E9-B2E7-4BCC-9638-157A31B0A700\n"); + /// + public string Author => "Natalia Portillo"; + + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + {}; + + /// + public Dictionary Namespaces => null; + + static Dictionary GetDefaultOptions() => new() { - bool _debug; - IMediaImage _device; - bool _mounted; - int _sectorsPerTrack; - ulong _start; - ulong _totalFileEntries; - bool _track1UsedByFiles; - bool _track2UsedByFiles; - uint _usedSectors; - Vtoc _vtoc; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Apple DOS File System"; - /// - public Guid Id => new("8658A1E9-B2E7-4BCC-9638-157A31B0A700\n"); - /// - public string Author => "Natalia Portillo"; - - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - {}; - - /// - public Dictionary Namespaces => null; - - static Dictionary GetDefaultOptions() => new() { - { - "debug", false.ToString() - } - }; + "debug", false.ToString() + } + }; - #region Caches - /// Caches track/sector lists - Dictionary _extentCache; - /// Caches files - Dictionary _fileCache; - /// Caches catalog - Dictionary _catalogCache; - /// Caches file size - Dictionary _fileSizeCache; - /// Caches VTOC - byte[] _vtocBlocks; - /// Caches catalog - byte[] _catalogBlocks; - /// Caches boot code - byte[] _bootBlocks; - /// Caches file type - Dictionary _fileTypeCache; - /// Caches locked files - List _lockedFiles; - #endregion Caches - } + #region Caches + /// Caches track/sector lists + Dictionary _extentCache; + /// Caches files + Dictionary _fileCache; + /// Caches catalog + Dictionary _catalogCache; + /// Caches file size + Dictionary _fileSizeCache; + /// Caches VTOC + byte[] _vtocBlocks; + /// Caches catalog + byte[] _catalogBlocks; + /// Caches boot code + byte[] _bootBlocks; + /// Caches file type + Dictionary _fileTypeCache; + /// Caches locked files + List _lockedFiles; + #endregion Caches } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleDOS/Dir.cs b/Aaru.Filesystems/AppleDOS/Dir.cs index da07cb19c..c68bc2b6e 100644 --- a/Aaru.Filesystems/AppleDOS/Dir.cs +++ b/Aaru.Filesystems/AppleDOS/Dir.cs @@ -37,112 +37,111 @@ using System.Linq; using Aaru.CommonTypes.Enums; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class AppleDOS { - public sealed partial class AppleDOS + /// + public ErrorNumber ReadLink(string path, out string dest) { - /// - public ErrorNumber ReadLink(string path, out string dest) - { - dest = null; + dest = null; - return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotSupported; + return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotSupported; + } + + /// + public ErrorNumber ReadDir(string path, out List contents) + { + contents = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(!string.IsNullOrEmpty(path) && + string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) + return ErrorNumber.NotSupported; + + contents = _catalogCache.Keys.ToList(); + + if(_debug) + { + contents.Add("$"); + contents.Add("$Boot"); + contents.Add("$Vtoc"); } - /// - public ErrorNumber ReadDir(string path, out List contents) + contents.Sort(); + + return ErrorNumber.NoError; + } + + ErrorNumber ReadCatalog() + { + var catalogMs = new MemoryStream(); + ulong lba = (ulong)((_vtoc.catalogTrack * _sectorsPerTrack) + _vtoc.catalogSector); + _totalFileEntries = 0; + _catalogCache = new Dictionary(); + _fileTypeCache = new Dictionary(); + _fileSizeCache = new Dictionary(); + _lockedFiles = new List(); + + if(lba == 0 || + lba > _device.Info.Sectors) + return ErrorNumber.InvalidArgument; + + while(lba != 0) { - contents = null; + _usedSectors++; + ErrorNumber errno = _device.ReadSector(lba, out byte[] catSectorB); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(errno != ErrorNumber.NoError) + return errno; - if(!string.IsNullOrEmpty(path) && - string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) - return ErrorNumber.NotSupported; - - contents = _catalogCache.Keys.ToList(); + _totalFileEntries += 7; if(_debug) + catalogMs.Write(catSectorB, 0, catSectorB.Length); + + // Read the catalog sector + CatalogSector catSector = Marshal.ByteArrayToStructureLittleEndian(catSectorB); + + foreach(FileEntry entry in catSector.entries.Where(entry => entry.extentTrack > 0)) { - contents.Add("$"); - contents.Add("$Boot"); - contents.Add("$Vtoc"); + _track1UsedByFiles |= entry.extentTrack == 1; + _track2UsedByFiles |= entry.extentTrack == 2; + + byte[] filenameB = new byte[30]; + ushort ts = (ushort)((entry.extentTrack << 8) | entry.extentSector); + + // Apple DOS has high byte set over ASCII. + for(int i = 0; i < 30; i++) + filenameB[i] = (byte)(entry.filename[i] & 0x7F); + + string filename = StringHandlers.SpacePaddedToString(filenameB, Encoding); + + if(!_catalogCache.ContainsKey(filename)) + _catalogCache.Add(filename, ts); + + if(!_fileTypeCache.ContainsKey(filename)) + _fileTypeCache.Add(filename, (byte)(entry.typeAndFlags & 0x7F)); + + if(!_fileSizeCache.ContainsKey(filename)) + _fileSizeCache.Add(filename, entry.length * _vtoc.bytesPerSector); + + if((entry.typeAndFlags & 0x80) == 0x80 && + !_lockedFiles.Contains(filename)) + _lockedFiles.Add(filename); } - contents.Sort(); + lba = (ulong)((catSector.trackOfNext * _sectorsPerTrack) + catSector.sectorOfNext); - return ErrorNumber.NoError; + if(lba > _device.Info.Sectors) + break; } - ErrorNumber ReadCatalog() - { - var catalogMs = new MemoryStream(); - ulong lba = (ulong)((_vtoc.catalogTrack * _sectorsPerTrack) + _vtoc.catalogSector); - _totalFileEntries = 0; - _catalogCache = new Dictionary(); - _fileTypeCache = new Dictionary(); - _fileSizeCache = new Dictionary(); - _lockedFiles = new List(); + if(_debug) + _catalogBlocks = catalogMs.ToArray(); - if(lba == 0 || - lba > _device.Info.Sectors) - return ErrorNumber.InvalidArgument; - - while(lba != 0) - { - _usedSectors++; - ErrorNumber errno = _device.ReadSector(lba, out byte[] catSectorB); - - if(errno != ErrorNumber.NoError) - return errno; - - _totalFileEntries += 7; - - if(_debug) - catalogMs.Write(catSectorB, 0, catSectorB.Length); - - // Read the catalog sector - CatalogSector catSector = Marshal.ByteArrayToStructureLittleEndian(catSectorB); - - foreach(FileEntry entry in catSector.entries.Where(entry => entry.extentTrack > 0)) - { - _track1UsedByFiles |= entry.extentTrack == 1; - _track2UsedByFiles |= entry.extentTrack == 2; - - byte[] filenameB = new byte[30]; - ushort ts = (ushort)((entry.extentTrack << 8) | entry.extentSector); - - // Apple DOS has high byte set over ASCII. - for(int i = 0; i < 30; i++) - filenameB[i] = (byte)(entry.filename[i] & 0x7F); - - string filename = StringHandlers.SpacePaddedToString(filenameB, Encoding); - - if(!_catalogCache.ContainsKey(filename)) - _catalogCache.Add(filename, ts); - - if(!_fileTypeCache.ContainsKey(filename)) - _fileTypeCache.Add(filename, (byte)(entry.typeAndFlags & 0x7F)); - - if(!_fileSizeCache.ContainsKey(filename)) - _fileSizeCache.Add(filename, entry.length * _vtoc.bytesPerSector); - - if((entry.typeAndFlags & 0x80) == 0x80 && - !_lockedFiles.Contains(filename)) - _lockedFiles.Add(filename); - } - - lba = (ulong)((catSector.trackOfNext * _sectorsPerTrack) + catSector.sectorOfNext); - - if(lba > _device.Info.Sectors) - break; - } - - if(_debug) - _catalogBlocks = catalogMs.ToArray(); - - return ErrorNumber.NoError; - } + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleDOS/File.cs b/Aaru.Filesystems/AppleDOS/File.cs index 6886d4f66..7faff10e0 100644 --- a/Aaru.Filesystems/AppleDOS/File.cs +++ b/Aaru.Filesystems/AppleDOS/File.cs @@ -39,269 +39,268 @@ using Aaru.CommonTypes.Structs; using Aaru.Helpers; using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class AppleDOS { - public sealed partial class AppleDOS + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) { - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - attributes = new FileAttributes(); + '/' + }, StringSplitOptions.RemoveEmptyEntries); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + string filename = pathElements[0].ToUpperInvariant(); - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + if(!_fileCache.ContainsKey(filename)) + return ErrorNumber.NoSuchFile; - string filename = pathElements[0].ToUpperInvariant(); + attributes = FileAttributes.Extents; + attributes |= FileAttributes.File; - if(!_fileCache.ContainsKey(filename)) - return ErrorNumber.NoSuchFile; + if(_lockedFiles.Contains(filename)) + attributes |= FileAttributes.ReadOnly; - attributes = FileAttributes.Extents; - attributes |= FileAttributes.File; + if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) + attributes |= FileAttributes.System; - if(_lockedFiles.Contains(filename)) - attributes |= FileAttributes.ReadOnly; + return ErrorNumber.NoError; + } - if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) - attributes |= FileAttributes.System; + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + if(!_mounted) + return ErrorNumber.AccessDenied; - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + string[] pathElements = path.Split(new[] { - if(!_mounted) - return ErrorNumber.AccessDenied; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + byte[] file; + string filename = pathElements[0].ToUpperInvariant(); - byte[] file; - string filename = pathElements[0].ToUpperInvariant(); + if(filename.Length > 30) + return ErrorNumber.NameTooLong; - if(filename.Length > 30) - return ErrorNumber.NameTooLong; - - if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) - file = _catalogBlocks; - else if(string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0) - file = _vtocBlocks; - else - file = _bootBlocks; + if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) + file = _catalogBlocks; + else if(string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0) + file = _vtocBlocks; else + file = _bootBlocks; + else + { + if(!_fileCache.TryGetValue(filename, out file)) { + ErrorNumber error = CacheFile(filename); + + if(error != ErrorNumber.NoError) + return error; + if(!_fileCache.TryGetValue(filename, out file)) - { - ErrorNumber error = CacheFile(filename); - - if(error != ErrorNumber.NoError) - return error; - - if(!_fileCache.TryGetValue(filename, out file)) - return ErrorNumber.InvalidArgument; - } + return ErrorNumber.InvalidArgument; } - - if(offset >= file.Length) - return ErrorNumber.InvalidArgument; - - if(size + offset >= file.Length) - size = file.Length - offset; - - buf = new byte[size]; - - Array.Copy(file, offset, buf, 0, size); - - return ErrorNumber.NoError; } - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) + if(offset >= file.Length) + return ErrorNumber.InvalidArgument; + + if(size + offset >= file.Length) + size = file.Length - offset; + + buf = new byte[size]; + + Array.Copy(file, offset, buf, 0, size); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - stat = null; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + string filename = pathElements[0].ToUpperInvariant(); - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + if(filename.Length > 30) + return ErrorNumber.NameTooLong; - string filename = pathElements[0].ToUpperInvariant(); + if(!_fileCache.ContainsKey(filename)) + return ErrorNumber.NoSuchFile; - if(filename.Length > 30) - return ErrorNumber.NameTooLong; + stat = new FileEntryInfo(); - if(!_fileCache.ContainsKey(filename)) - return ErrorNumber.NoSuchFile; + _fileSizeCache.TryGetValue(filename, out int fileSize); + GetAttributes(path, out FileAttributes attrs); - stat = new FileEntryInfo(); + if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) + { + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) + stat.Length = _catalogBlocks.Length; + else if(string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0) + stat.Length = _bootBlocks.Length; + else if(string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0) + stat.Length = _vtocBlocks.Length; - _fileSizeCache.TryGetValue(filename, out int fileSize); - GetAttributes(path, out FileAttributes attrs); - - if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) - { - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) - stat.Length = _catalogBlocks.Length; - else if(string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0) - stat.Length = _bootBlocks.Length; - else if(string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0) - stat.Length = _vtocBlocks.Length; - - stat.Blocks = stat.Length / _vtoc.bytesPerSector; - } - else - { - stat.Length = fileSize; - stat.Blocks = stat.Length / _vtoc.bytesPerSector; - } - - stat.Attributes = attrs; - stat.BlockSize = _vtoc.bytesPerSector; - stat.Links = 1; - - return ErrorNumber.NoError; + stat.Blocks = stat.Length / _vtoc.bytesPerSector; + } + else + { + stat.Length = fileSize; + stat.Blocks = stat.Length / _vtoc.bytesPerSector; } - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + stat.Attributes = attrs; + stat.BlockSize = _vtoc.bytesPerSector; + stat.Links = 1; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + { + deviceBlock = 0; + + // TODO: Not really important. + return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotImplemented; + } + + ErrorNumber CacheFile(string path) + { + string[] pathElements = path.Split(new[] { - deviceBlock = 0; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - // TODO: Not really important. - return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotImplemented; - } + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - ErrorNumber CacheFile(string path) + string filename = pathElements[0].ToUpperInvariant(); + + if(filename.Length > 30) + return ErrorNumber.NameTooLong; + + if(!_catalogCache.TryGetValue(filename, out ushort ts)) + return ErrorNumber.NoSuchFile; + + ulong lba = (ulong)((((ts & 0xFF00) >> 8) * _sectorsPerTrack) + (ts & 0xFF)); + var fileMs = new MemoryStream(); + var tsListMs = new MemoryStream(); + ushort expectedBlock = 0; + + while(lba != 0) { - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - string filename = pathElements[0].ToUpperInvariant(); - - if(filename.Length > 30) - return ErrorNumber.NameTooLong; - - if(!_catalogCache.TryGetValue(filename, out ushort ts)) - return ErrorNumber.NoSuchFile; - - ulong lba = (ulong)((((ts & 0xFF00) >> 8) * _sectorsPerTrack) + (ts & 0xFF)); - var fileMs = new MemoryStream(); - var tsListMs = new MemoryStream(); - ushort expectedBlock = 0; - - while(lba != 0) - { - _usedSectors++; - ErrorNumber errno = _device.ReadSector(lba, out byte[] tsSectorB); - - if(errno != ErrorNumber.NoError) - return errno; - - if(_debug) - tsListMs.Write(tsSectorB, 0, tsSectorB.Length); - - // Read the track/sector list sector - TrackSectorList tsSector = Marshal.ByteArrayToStructureLittleEndian(tsSectorB); - - if(tsSector.sectorOffset > expectedBlock) - { - byte[] hole = new byte[(tsSector.sectorOffset - expectedBlock) * _vtoc.bytesPerSector]; - fileMs.Write(hole, 0, hole.Length); - expectedBlock = tsSector.sectorOffset; - } - - foreach(TrackSectorListEntry entry in tsSector.entries) - { - _track1UsedByFiles |= entry.track == 1; - _track2UsedByFiles |= entry.track == 2; - _usedSectors++; - - ulong blockLba = (ulong)((entry.track * _sectorsPerTrack) + entry.sector); - - if(blockLba == 0) - break; - - errno = _device.ReadSector(blockLba, out byte[] fileBlock); - - if(errno != ErrorNumber.NoError) - return errno; - - fileMs.Write(fileBlock, 0, fileBlock.Length); - expectedBlock++; - } - - lba = (ulong)((tsSector.nextListTrack * _sectorsPerTrack) + tsSector.nextListSector); - } - - if(_fileCache.ContainsKey(filename)) - _fileCache.Remove(filename); - - if(_extentCache.ContainsKey(filename)) - _extentCache.Remove(filename); - - _fileCache.Add(filename, fileMs.ToArray()); - _extentCache.Add(filename, tsListMs.ToArray()); - - return ErrorNumber.NoError; - } - - ErrorNumber CacheAllFiles() - { - _fileCache = new Dictionary(); - _extentCache = new Dictionary(); - - foreach(ErrorNumber error in _catalogCache.Keys.Select(CacheFile). - Where(error => error != ErrorNumber.NoError)) - return error; - - uint tracksOnBoot = 1; - - if(!_track1UsedByFiles) - tracksOnBoot++; - - if(!_track2UsedByFiles) - tracksOnBoot++; - - ErrorNumber errno = _device.ReadSectors(0, (uint)(tracksOnBoot * _sectorsPerTrack), out _bootBlocks); + _usedSectors++; + ErrorNumber errno = _device.ReadSector(lba, out byte[] tsSectorB); if(errno != ErrorNumber.NoError) return errno; - _usedSectors += (uint)(_bootBlocks.Length / _vtoc.bytesPerSector); + if(_debug) + tsListMs.Write(tsSectorB, 0, tsSectorB.Length); - return ErrorNumber.NoError; + // Read the track/sector list sector + TrackSectorList tsSector = Marshal.ByteArrayToStructureLittleEndian(tsSectorB); + + if(tsSector.sectorOffset > expectedBlock) + { + byte[] hole = new byte[(tsSector.sectorOffset - expectedBlock) * _vtoc.bytesPerSector]; + fileMs.Write(hole, 0, hole.Length); + expectedBlock = tsSector.sectorOffset; + } + + foreach(TrackSectorListEntry entry in tsSector.entries) + { + _track1UsedByFiles |= entry.track == 1; + _track2UsedByFiles |= entry.track == 2; + _usedSectors++; + + ulong blockLba = (ulong)((entry.track * _sectorsPerTrack) + entry.sector); + + if(blockLba == 0) + break; + + errno = _device.ReadSector(blockLba, out byte[] fileBlock); + + if(errno != ErrorNumber.NoError) + return errno; + + fileMs.Write(fileBlock, 0, fileBlock.Length); + expectedBlock++; + } + + lba = (ulong)((tsSector.nextListTrack * _sectorsPerTrack) + tsSector.nextListSector); } + + if(_fileCache.ContainsKey(filename)) + _fileCache.Remove(filename); + + if(_extentCache.ContainsKey(filename)) + _extentCache.Remove(filename); + + _fileCache.Add(filename, fileMs.ToArray()); + _extentCache.Add(filename, tsListMs.ToArray()); + + return ErrorNumber.NoError; + } + + ErrorNumber CacheAllFiles() + { + _fileCache = new Dictionary(); + _extentCache = new Dictionary(); + + foreach(ErrorNumber error in _catalogCache.Keys.Select(CacheFile). + Where(error => error != ErrorNumber.NoError)) + return error; + + uint tracksOnBoot = 1; + + if(!_track1UsedByFiles) + tracksOnBoot++; + + if(!_track2UsedByFiles) + tracksOnBoot++; + + ErrorNumber errno = _device.ReadSectors(0, (uint)(tracksOnBoot * _sectorsPerTrack), out _bootBlocks); + + if(errno != ErrorNumber.NoError) + return errno; + + _usedSectors += (uint)(_bootBlocks.Length / _vtoc.bytesPerSector); + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleDOS/Info.cs b/Aaru.Filesystems/AppleDOS/Info.cs index ab1637877..f638405ab 100644 --- a/Aaru.Filesystems/AppleDOS/Info.cs +++ b/Aaru.Filesystems/AppleDOS/Info.cs @@ -39,76 +39,75 @@ using Claunia.Encoding; using Schemas; using Encoding = System.Text.Encoding; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class AppleDOS { - public sealed partial class AppleDOS + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(imagePlugin.Info.Sectors != 455 && + imagePlugin.Info.Sectors != 560) + return false; + + if(partition.Start > 0 || + imagePlugin.Info.SectorSize != 256) + return false; + + int spt = imagePlugin.Info.Sectors == 455 ? 13 : 16; + + var errno = imagePlugin.ReadSector((ulong)(17 * spt), out byte[] vtocB); + + if(errno != ErrorNumber.NoError) + return false; + + _vtoc = Marshal.ByteArrayToStructureLittleEndian(vtocB); + + return _vtoc.catalogSector < spt && _vtoc.maxTrackSectorPairsPerSector <= 122 && + _vtoc.sectorsPerTrack == spt && _vtoc.bytesPerSector == 256; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? new Apple2(); + information = ""; + var sb = new StringBuilder(); + + int spt = imagePlugin.Info.Sectors == 455 ? 13 : 16; + + var errno = imagePlugin.ReadSector((ulong)(17 * spt), out byte[] vtocB); + + if(errno != ErrorNumber.NoError) + return; + + _vtoc = Marshal.ByteArrayToStructureLittleEndian(vtocB); + + sb.AppendLine("Apple DOS File System"); + sb.AppendLine(); + + sb.AppendFormat("Catalog starts at sector {0} of track {1}", _vtoc.catalogSector, _vtoc.catalogTrack). + AppendLine(); + + sb.AppendFormat("File system initialized by DOS release {0}", _vtoc.dosRelease).AppendLine(); + sb.AppendFormat("Disk volume number {0}", _vtoc.volumeNumber).AppendLine(); + sb.AppendFormat("Sectors allocated at most in track {0}", _vtoc.lastAllocatedSector).AppendLine(); + sb.AppendFormat("{0} tracks in volume", _vtoc.tracks).AppendLine(); + sb.AppendFormat("{0} sectors per track", _vtoc.sectorsPerTrack).AppendLine(); + sb.AppendFormat("{0} bytes per sector", _vtoc.bytesPerSector).AppendLine(); + + sb.AppendFormat("Track allocation is {0}", _vtoc.allocationDirection > 0 ? "forward" : "reverse"). + AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - if(imagePlugin.Info.Sectors != 455 && - imagePlugin.Info.Sectors != 560) - return false; - - if(partition.Start > 0 || - imagePlugin.Info.SectorSize != 256) - return false; - - int spt = imagePlugin.Info.Sectors == 455 ? 13 : 16; - - var errno = imagePlugin.ReadSector((ulong)(17 * spt), out byte[] vtocB); - - if(errno != ErrorNumber.NoError) - return false; - - _vtoc = Marshal.ByteArrayToStructureLittleEndian(vtocB); - - return _vtoc.catalogSector < spt && _vtoc.maxTrackSectorPairsPerSector <= 122 && - _vtoc.sectorsPerTrack == spt && _vtoc.bytesPerSector == 256; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? new Apple2(); - information = ""; - var sb = new StringBuilder(); - - int spt = imagePlugin.Info.Sectors == 455 ? 13 : 16; - - var errno = imagePlugin.ReadSector((ulong)(17 * spt), out byte[] vtocB); - - if(errno != ErrorNumber.NoError) - return; - - _vtoc = Marshal.ByteArrayToStructureLittleEndian(vtocB); - - sb.AppendLine("Apple DOS File System"); - sb.AppendLine(); - - sb.AppendFormat("Catalog starts at sector {0} of track {1}", _vtoc.catalogSector, _vtoc.catalogTrack). - AppendLine(); - - sb.AppendFormat("File system initialized by DOS release {0}", _vtoc.dosRelease).AppendLine(); - sb.AppendFormat("Disk volume number {0}", _vtoc.volumeNumber).AppendLine(); - sb.AppendFormat("Sectors allocated at most in track {0}", _vtoc.lastAllocatedSector).AppendLine(); - sb.AppendFormat("{0} tracks in volume", _vtoc.tracks).AppendLine(); - sb.AppendFormat("{0} sectors per track", _vtoc.sectorsPerTrack).AppendLine(); - sb.AppendFormat("{0} bytes per sector", _vtoc.bytesPerSector).AppendLine(); - - sb.AppendFormat("Track allocation is {0}", _vtoc.allocationDirection > 0 ? "forward" : "reverse"). - AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Bootable = true, - Clusters = imagePlugin.Info.Sectors, - ClusterSize = imagePlugin.Info.SectorSize, - Type = "Apple DOS" - }; - } + Bootable = true, + Clusters = imagePlugin.Info.Sectors, + ClusterSize = imagePlugin.Info.SectorSize, + Type = "Apple DOS" + }; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleDOS/Structs.cs b/Aaru.Filesystems/AppleDOS/Structs.cs index e36475c06..0b6863146 100644 --- a/Aaru.Filesystems/AppleDOS/Structs.cs +++ b/Aaru.Filesystems/AppleDOS/Structs.cs @@ -32,79 +32,78 @@ using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class AppleDOS { - public sealed partial class AppleDOS + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Vtoc { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Vtoc - { - public readonly byte unused1; - public readonly byte catalogTrack; - public readonly byte catalogSector; - public readonly byte dosRelease; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] unused2; - public readonly byte volumeNumber; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] unused3; - public readonly byte maxTrackSectorPairsPerSector; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] unused4; - public readonly byte lastAllocatedSector; - public readonly sbyte allocationDirection; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] unused5; - public readonly byte tracks; - public readonly byte sectorsPerTrack; - public readonly ushort bytesPerSector; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)] - public readonly byte[] bitmap; - } + public readonly byte unused1; + public readonly byte catalogTrack; + public readonly byte catalogSector; + public readonly byte dosRelease; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] unused2; + public readonly byte volumeNumber; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] unused3; + public readonly byte maxTrackSectorPairsPerSector; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] unused4; + public readonly byte lastAllocatedSector; + public readonly sbyte allocationDirection; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] unused5; + public readonly byte tracks; + public readonly byte sectorsPerTrack; + public readonly ushort bytesPerSector; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 200)] + public readonly byte[] bitmap; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CatalogSector - { - public readonly byte unused1; - public readonly byte trackOfNext; - public readonly byte sectorOfNext; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] unused2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] - public readonly FileEntry[] entries; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CatalogSector + { + public readonly byte unused1; + public readonly byte trackOfNext; + public readonly byte sectorOfNext; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] unused2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 7)] + public readonly FileEntry[] entries; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct FileEntry - { - public readonly byte extentTrack; - public readonly byte extentSector; - public readonly byte typeAndFlags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] - public readonly byte[] filename; - public readonly ushort length; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct FileEntry + { + public readonly byte extentTrack; + public readonly byte extentSector; + public readonly byte typeAndFlags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] + public readonly byte[] filename; + public readonly ushort length; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct TrackSectorList - { - public readonly byte unused1; - public readonly byte nextListTrack; - public readonly byte nextListSector; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] unused2; - public readonly ushort sectorOffset; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] unused3; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 122)] - public readonly TrackSectorListEntry[] entries; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct TrackSectorList + { + public readonly byte unused1; + public readonly byte nextListTrack; + public readonly byte nextListSector; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] unused2; + public readonly ushort sectorOffset; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] unused3; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 122)] + public readonly TrackSectorListEntry[] entries; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct TrackSectorListEntry - { - public readonly byte track; - public readonly byte sector; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct TrackSectorListEntry + { + public readonly byte track; + public readonly byte sector; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleDOS/Super.cs b/Aaru.Filesystems/AppleDOS/Super.cs index feb2044a7..ff0d91bab 100644 --- a/Aaru.Filesystems/AppleDOS/Super.cs +++ b/Aaru.Filesystems/AppleDOS/Super.cs @@ -41,124 +41,123 @@ using Claunia.Encoding; using Schemas; using Encoding = System.Text.Encoding; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class AppleDOS { - public sealed partial class AppleDOS + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + _device = imagePlugin; + _start = partition.Start; + Encoding = encoding ?? new Apple2(); + + if(_device.Info.Sectors != 455 && + _device.Info.Sectors != 560) { - _device = imagePlugin; - _start = partition.Start; - Encoding = encoding ?? new Apple2(); + AaruConsole.DebugWriteLine("Apple DOS plugin", "Incorrect device size."); - if(_device.Info.Sectors != 455 && - _device.Info.Sectors != 560) - { - AaruConsole.DebugWriteLine("Apple DOS plugin", "Incorrect device size."); - - return ErrorNumber.InOutError; - } - - if(_start > 0) - { - AaruConsole.DebugWriteLine("Apple DOS plugin", "Partitions are not supported."); - - return ErrorNumber.InOutError; - } - - if(_device.Info.SectorSize != 256) - { - AaruConsole.DebugWriteLine("Apple DOS plugin", "Incorrect sector size."); - - return ErrorNumber.InOutError; - } - - _sectorsPerTrack = _device.Info.Sectors == 455 ? 13 : 16; - - // Read the VTOC - ErrorNumber error = _device.ReadSector((ulong)(17 * _sectorsPerTrack), out _vtocBlocks); - - if(error != ErrorNumber.NoError) - return error; - - _vtoc = Marshal.ByteArrayToStructureLittleEndian(_vtocBlocks); - - _track1UsedByFiles = false; - _track2UsedByFiles = false; - _usedSectors = 1; - - error = ReadCatalog(); - - if(error != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("Apple DOS plugin", "Unable to read catalog."); - - return error; - } - - error = CacheAllFiles(); - - if(error != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("Apple DOS plugin", "Unable cache all files."); - - return error; - } - - // Create XML metadata for mounted filesystem - XmlFsType = new FileSystemType - { - Bootable = true, - Clusters = _device.Info.Sectors, - ClusterSize = _vtoc.bytesPerSector, - Files = (ulong)_catalogCache.Count, - FilesSpecified = true, - FreeClustersSpecified = true, - Type = "Apple DOS" - }; - - XmlFsType.FreeClusters = XmlFsType.Clusters - _usedSectors; - - options ??= GetDefaultOptions(); - - if(options.TryGetValue("debug", out string debugString)) - bool.TryParse(debugString, out _debug); - - _mounted = true; - - return ErrorNumber.NoError; + return ErrorNumber.InOutError; } - /// - public ErrorNumber Unmount() + if(_start > 0) { - _mounted = false; - _extentCache = null; - _fileCache = null; - _catalogCache = null; - _fileSizeCache = null; + AaruConsole.DebugWriteLine("Apple DOS plugin", "Partitions are not supported."); - return ErrorNumber.NoError; + return ErrorNumber.InOutError; } - /// - public ErrorNumber StatFs(out FileSystemInfo stat) + if(_device.Info.SectorSize != 256) { - stat = new FileSystemInfo - { - Blocks = _device.Info.Sectors, - FilenameLength = 30, - Files = (ulong)_catalogCache.Count, - PluginId = Id, - Type = "Apple DOS" - }; + AaruConsole.DebugWriteLine("Apple DOS plugin", "Incorrect sector size."); - stat.FreeFiles = _totalFileEntries - stat.Files; - stat.FreeBlocks = stat.Blocks - _usedSectors; - - return ErrorNumber.NoError; + return ErrorNumber.InOutError; } + + _sectorsPerTrack = _device.Info.Sectors == 455 ? 13 : 16; + + // Read the VTOC + ErrorNumber error = _device.ReadSector((ulong)(17 * _sectorsPerTrack), out _vtocBlocks); + + if(error != ErrorNumber.NoError) + return error; + + _vtoc = Marshal.ByteArrayToStructureLittleEndian(_vtocBlocks); + + _track1UsedByFiles = false; + _track2UsedByFiles = false; + _usedSectors = 1; + + error = ReadCatalog(); + + if(error != ErrorNumber.NoError) + { + AaruConsole.DebugWriteLine("Apple DOS plugin", "Unable to read catalog."); + + return error; + } + + error = CacheAllFiles(); + + if(error != ErrorNumber.NoError) + { + AaruConsole.DebugWriteLine("Apple DOS plugin", "Unable cache all files."); + + return error; + } + + // Create XML metadata for mounted filesystem + XmlFsType = new FileSystemType + { + Bootable = true, + Clusters = _device.Info.Sectors, + ClusterSize = _vtoc.bytesPerSector, + Files = (ulong)_catalogCache.Count, + FilesSpecified = true, + FreeClustersSpecified = true, + Type = "Apple DOS" + }; + + XmlFsType.FreeClusters = XmlFsType.Clusters - _usedSectors; + + options ??= GetDefaultOptions(); + + if(options.TryGetValue("debug", out string debugString)) + bool.TryParse(debugString, out _debug); + + _mounted = true; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Unmount() + { + _mounted = false; + _extentCache = null; + _fileCache = null; + _catalogCache = null; + _fileSizeCache = null; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = new FileSystemInfo + { + Blocks = _device.Info.Sectors, + FilenameLength = 30, + Files = (ulong)_catalogCache.Count, + PluginId = Id, + Type = "Apple DOS" + }; + + stat.FreeFiles = _totalFileEntries - stat.Files; + stat.FreeBlocks = stat.Blocks - _usedSectors; + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleDOS/Xattr.cs b/Aaru.Filesystems/AppleDOS/Xattr.cs index 031a7763d..6193b2241 100644 --- a/Aaru.Filesystems/AppleDOS/Xattr.cs +++ b/Aaru.Filesystems/AppleDOS/Xattr.cs @@ -35,99 +35,98 @@ using System.Collections.Generic; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class AppleDOS { - public sealed partial class AppleDOS + /// + public ErrorNumber ListXAttr(string path, out List xattrs) { - /// - public ErrorNumber ListXAttr(string path, out List xattrs) + xattrs = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - xattrs = null; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + string filename = pathElements[0].ToUpperInvariant(); - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + if(filename.Length > 30) + return ErrorNumber.NameTooLong; - string filename = pathElements[0].ToUpperInvariant(); + xattrs = new List(); - if(filename.Length > 30) - return ErrorNumber.NameTooLong; - - xattrs = new List(); - - if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) {} - else - { - if(!_catalogCache.ContainsKey(filename)) - return ErrorNumber.NoSuchFile; - - xattrs.Add("com.apple.dos.type"); - - if(_debug) - xattrs.Add("com.apple.dos.tracksectorlist"); - } - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) {} + else { - if(!_mounted) - return ErrorNumber.AccessDenied; - - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - string filename = pathElements[0].ToUpperInvariant(); - - if(filename.Length > 30) - return ErrorNumber.NameTooLong; - - if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) - return ErrorNumber.NoSuchExtendedAttribute; - if(!_catalogCache.ContainsKey(filename)) return ErrorNumber.NoSuchFile; - if(string.Compare(xattr, "com.apple.dos.type", StringComparison.InvariantCulture) == 0) - { - if(!_fileTypeCache.TryGetValue(filename, out byte type)) - return ErrorNumber.InvalidArgument; + xattrs.Add("com.apple.dos.type"); - buf = new byte[1]; - buf[0] = type; + if(_debug) + xattrs.Add("com.apple.dos.tracksectorlist"); + } - return ErrorNumber.NoError; - } + return ErrorNumber.NoError; + } - if(string.Compare(xattr, "com.apple.dos.tracksectorlist", StringComparison.InvariantCulture) != 0 || - !_debug) - return ErrorNumber.NoSuchExtendedAttribute; + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + { + if(!_mounted) + return ErrorNumber.AccessDenied; - if(!_extentCache.TryGetValue(filename, out byte[] ts)) + string[] pathElements = path.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; + + string filename = pathElements[0].ToUpperInvariant(); + + if(filename.Length > 30) + return ErrorNumber.NameTooLong; + + if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Vtoc", StringComparison.InvariantCulture) == 0)) + return ErrorNumber.NoSuchExtendedAttribute; + + if(!_catalogCache.ContainsKey(filename)) + return ErrorNumber.NoSuchFile; + + if(string.Compare(xattr, "com.apple.dos.type", StringComparison.InvariantCulture) == 0) + { + if(!_fileTypeCache.TryGetValue(filename, out byte type)) return ErrorNumber.InvalidArgument; - buf = new byte[ts.Length]; - Array.Copy(ts, 0, buf, 0, buf.Length); + buf = new byte[1]; + buf[0] = type; return ErrorNumber.NoError; } + + if(string.Compare(xattr, "com.apple.dos.tracksectorlist", StringComparison.InvariantCulture) != 0 || + !_debug) + return ErrorNumber.NoSuchExtendedAttribute; + + if(!_extentCache.TryGetValue(filename, out byte[] ts)) + return ErrorNumber.InvalidArgument; + + buf = new byte[ts.Length]; + Array.Copy(ts, 0, buf, 0, buf.Length); + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleHFS/AppleHFS.cs b/Aaru.Filesystems/AppleHFS/AppleHFS.cs index 7158c88c5..7861b2d33 100644 --- a/Aaru.Filesystems/AppleHFS/AppleHFS.cs +++ b/Aaru.Filesystems/AppleHFS/AppleHFS.cs @@ -36,23 +36,22 @@ using System.Text; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh +// https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf +/// +/// Implements detection of the Apple Hierarchical File System (HFS) +public sealed partial class AppleHFS : IFilesystem { - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf /// - /// Implements detection of the Apple Hierarchical File System (HFS) - public sealed partial class AppleHFS : IFilesystem - { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Apple Hierarchical File System"; - /// - public Guid Id => new Guid("36405F8D-0D26-6ECC-0BBB-1D5225FF404F"); - /// - public string Author => "Natalia Portillo"; - } + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Apple Hierarchical File System"; + /// + public Guid Id => new Guid("36405F8D-0D26-6ECC-0BBB-1D5225FF404F"); + /// + public string Author => "Natalia Portillo"; } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleHFS/Consts.cs b/Aaru.Filesystems/AppleHFS/Consts.cs index bb0567166..e72676063 100644 --- a/Aaru.Filesystems/AppleHFS/Consts.cs +++ b/Aaru.Filesystems/AppleHFS/Consts.cs @@ -34,22 +34,21 @@ using System.Diagnostics.CodeAnalysis; // ReSharper disable InconsistentNaming -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh +// https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class AppleHFS { - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class AppleHFS - { - /// Parent ID of the root directory. - const uint kRootParentCnid = 1; - /// Directory ID of the root directory. - const uint kRootCnid = 2; - /// File number of the extents file. - const uint kExtentsFileCnid = 3; - /// File number of the catalog file. - const uint kCatalogFileCnid = 4; - /// File number of the bad allocation block file. - const uint kBadBlocksFileCnid = 5; - } + /// Parent ID of the root directory. + const uint kRootParentCnid = 1; + /// Directory ID of the root directory. + const uint kRootCnid = 2; + /// File number of the extents file. + const uint kExtentsFileCnid = 3; + /// File number of the catalog file. + const uint kCatalogFileCnid = 4; + /// File number of the bad allocation block file. + const uint kBadBlocksFileCnid = 5; } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleHFS/Enums.cs b/Aaru.Filesystems/AppleHFS/Enums.cs index 852e7ff58..fca41fc07 100644 --- a/Aaru.Filesystems/AppleHFS/Enums.cs +++ b/Aaru.Filesystems/AppleHFS/Enums.cs @@ -34,37 +34,36 @@ // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class AppleHFS { - public sealed partial class AppleHFS + enum NodeType : sbyte { - enum NodeType : sbyte - { - /// Index node - ndIndxNode = 0, - /// Header node - ndHdrNode = 1, - /// Map node - ndMapNode = 2, - /// Leaf node - ndLeafNode = -1 - } + /// Index node + ndIndxNode = 0, + /// Header node + ndHdrNode = 1, + /// Map node + ndMapNode = 2, + /// Leaf node + ndLeafNode = -1 + } - enum CatDataType : sbyte - { - /// Directory record - cdrDirRec = 1, - /// File record - cdrFilRec = 2, - /// Directory thread record - cdrThdRec = 3, - /// File thread record - cdrFThdRec = 4 - } + enum CatDataType : sbyte + { + /// Directory record + cdrDirRec = 1, + /// File record + cdrFilRec = 2, + /// Directory thread record + cdrThdRec = 3, + /// File thread record + cdrFThdRec = 4 + } - enum ForkType : sbyte - { - Data = 0, Resource = -1 - } + enum ForkType : sbyte + { + Data = 0, Resource = -1 } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleHFS/Info.cs b/Aaru.Filesystems/AppleHFS/Info.cs index f5a76027d..626ed04e3 100644 --- a/Aaru.Filesystems/AppleHFS/Info.cs +++ b/Aaru.Filesystems/AppleHFS/Info.cs @@ -39,288 +39,287 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh +// https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf +public sealed partial class AppleHFS { - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf - public sealed partial class AppleHFS + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(2 + partition.Start >= partition.End) + return false; + + byte[] mdbSector; + ushort drSigWord; + ErrorNumber errno; + + if(imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448 || + imagePlugin.Info.SectorSize == 2048) { - if(2 + partition.Start >= partition.End) + errno = imagePlugin.ReadSectors(partition.Start, 2, out mdbSector); + + if(errno != ErrorNumber.NoError) return false; - byte[] mdbSector; - ushort drSigWord; - ErrorNumber errno; - - if(imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448 || - imagePlugin.Info.SectorSize == 2048) + foreach(int offset in new[] + { + 0, 0x200, 0x400, 0x600, 0x800, 0xA00 + }.Where(offset => mdbSector.Length >= offset + 0x7C + 2)) { - errno = imagePlugin.ReadSectors(partition.Start, 2, out mdbSector); - - if(errno != ErrorNumber.NoError) - return false; - - foreach(int offset in new[] - { - 0, 0x200, 0x400, 0x600, 0x800, 0xA00 - }.Where(offset => mdbSector.Length >= offset + 0x7C + 2)) - { - drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, offset); - - if(drSigWord != AppleCommon.HFS_MAGIC) - continue; - - drSigWord = - BigEndianBitConverter.ToUInt16(mdbSector, offset + 0x7C); // Seek to embedded HFS+ signature - - return drSigWord != AppleCommon.HFSP_MAGIC; - } - } - else - { - errno = imagePlugin.ReadSector(2 + partition.Start, out mdbSector); - - if(errno != ErrorNumber.NoError) - return false; - - if(mdbSector.Length < 0x7C + 2) - return false; - - drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); + drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, offset); if(drSigWord != AppleCommon.HFS_MAGIC) - return false; + continue; - drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x7C); // Seek to embedded HFS+ signature + drSigWord = + BigEndianBitConverter.ToUInt16(mdbSector, offset + 0x7C); // Seek to embedded HFS+ signature return drSigWord != AppleCommon.HFSP_MAGIC; } - - return false; } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + else { - Encoding = encoding ?? Encoding.GetEncoding("macintosh"); - information = ""; + errno = imagePlugin.ReadSector(2 + partition.Start, out mdbSector); - var sb = new StringBuilder(); + if(errno != ErrorNumber.NoError) + return false; - byte[] bbSector = null; - byte[] mdbSector = null; - ushort drSigWord; - ErrorNumber errno; + if(mdbSector.Length < 0x7C + 2) + return false; - bool apmFromHddOnCd = false; + drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); - if(imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448 || - imagePlugin.Info.SectorSize == 2048) - { - errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] tmpSector); + if(drSigWord != AppleCommon.HFS_MAGIC) + return false; - if(errno != ErrorNumber.NoError) - return; + drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x7C); // Seek to embedded HFS+ signature - foreach(int offset in new[] - { - 0, 0x200, 0x400, 0x600, 0x800, 0xA00 - }) - { - drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); - - if(drSigWord != AppleCommon.HFS_MAGIC) - continue; - - bbSector = new byte[1024]; - mdbSector = new byte[512]; - - if(offset >= 0x400) - Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); - - Array.Copy(tmpSector, offset, mdbSector, 0, 512); - apmFromHddOnCd = true; - - break; - } - - if(!apmFromHddOnCd) - return; - } - else - { - errno = imagePlugin.ReadSector(2 + partition.Start, out mdbSector); - - if(errno != ErrorNumber.NoError) - return; - - drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); - - if(drSigWord == AppleCommon.HFS_MAGIC) - { - errno = imagePlugin.ReadSector(partition.Start, out bbSector); - - if(errno != ErrorNumber.NoError) - return; - } - else - return; - } - - MasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian(mdbSector); - - sb.AppendLine("Apple Hierarchical File System"); - sb.AppendLine(); - - if(apmFromHddOnCd) - sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); - - sb.AppendLine("Master Directory Block:"); - sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); - sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine(); - - if(mdb.drVolBkUp > 0) - { - sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine(); - sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine(); - } - else - sb.AppendLine("Volume has never been backed up"); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) - sb.AppendLine("Volume is locked by hardware."); - - sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." - : "Volume is mounted."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) - sb.AppendLine("Volume has spared bad blocks."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) - sb.AppendLine("Volume does not need cache."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) - sb.AppendLine("Boot volume is inconsistent."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) - sb.AppendLine("There are reused CNIDs."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Journaled)) - sb.AppendLine("Volume is journaled."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) - sb.AppendLine("Volume is seriously inconsistent."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) - sb.AppendLine("Volume is locked by software."); - - sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine(); - sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine(); - sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine(); - sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine(); - sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine(); - - sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine(); - sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine(); - sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine(); - sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine(); - sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine(); - sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine(); - sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine(); - sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine(); - sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine(); - sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); - - sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine(); - sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine(); - - sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine(); - - sb.AppendLine("Finder info:"); - sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine(); - sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine(); - sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine(); - sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine(); - sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine(); - - if(mdb.drFndrInfo6 != 0 && - mdb.drFndrInfo7 != 0) - sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine(); - - if(mdb.drEmbedSigWord == AppleCommon.HFSP_MAGIC) - { - sb.AppendLine("Volume wraps a HFS+ volume."); - sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine(); - sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine(); - } - else - { - sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine(); - sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine(); - sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine(); - } - - string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); - - if(bootBlockInfo != null) - { - sb.AppendLine("Volume is bootable."); - sb.AppendLine(); - sb.AppendLine(bootBlockInfo); - } - else if(mdb.drFndrInfo0 != 0 || - mdb.drFndrInfo3 != 0 || - mdb.drFndrInfo5 != 0) - sb.AppendLine("Volume is bootable."); - else - sb.AppendLine("Volume is not bootable."); - - information = sb.ToString(); - - XmlFsType = new FileSystemType(); - - if(mdb.drVolBkUp > 0) - { - XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drVolBkUp); - XmlFsType.BackupDateSpecified = true; - } - - XmlFsType.Bootable = bootBlockInfo != null || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || - mdb.drFndrInfo5 != 0; - - XmlFsType.Clusters = mdb.drNmAlBlks; - XmlFsType.ClusterSize = mdb.drAlBlkSiz; - - if(mdb.drCrDate > 0) - { - XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); - XmlFsType.CreationDateSpecified = true; - } - - XmlFsType.Dirty = !mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted); - XmlFsType.Files = mdb.drFilCnt; - XmlFsType.FilesSpecified = true; - XmlFsType.FreeClusters = mdb.drFreeBks; - XmlFsType.FreeClustersSpecified = true; - - if(mdb.drLsMod > 0) - { - XmlFsType.ModificationDate = DateHandlers.MacToDateTime(mdb.drLsMod); - XmlFsType.ModificationDateSpecified = true; - } - - XmlFsType.Type = "HFS"; - XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding); - - if(mdb.drFndrInfo6 != 0 && - mdb.drFndrInfo7 != 0) - XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}"; + return drSigWord != AppleCommon.HFSP_MAGIC; } + + return false; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("macintosh"); + information = ""; + + var sb = new StringBuilder(); + + byte[] bbSector = null; + byte[] mdbSector = null; + ushort drSigWord; + ErrorNumber errno; + + bool apmFromHddOnCd = false; + + if(imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448 || + imagePlugin.Info.SectorSize == 2048) + { + errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] tmpSector); + + if(errno != ErrorNumber.NoError) + return; + + foreach(int offset in new[] + { + 0, 0x200, 0x400, 0x600, 0x800, 0xA00 + }) + { + drSigWord = BigEndianBitConverter.ToUInt16(tmpSector, offset); + + if(drSigWord != AppleCommon.HFS_MAGIC) + continue; + + bbSector = new byte[1024]; + mdbSector = new byte[512]; + + if(offset >= 0x400) + Array.Copy(tmpSector, offset - 0x400, bbSector, 0, 1024); + + Array.Copy(tmpSector, offset, mdbSector, 0, 512); + apmFromHddOnCd = true; + + break; + } + + if(!apmFromHddOnCd) + return; + } + else + { + errno = imagePlugin.ReadSector(2 + partition.Start, out mdbSector); + + if(errno != ErrorNumber.NoError) + return; + + drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0); + + if(drSigWord == AppleCommon.HFS_MAGIC) + { + errno = imagePlugin.ReadSector(partition.Start, out bbSector); + + if(errno != ErrorNumber.NoError) + return; + } + else + return; + } + + MasterDirectoryBlock mdb = Marshal.ByteArrayToStructureBigEndian(mdbSector); + + sb.AppendLine("Apple Hierarchical File System"); + sb.AppendLine(); + + if(apmFromHddOnCd) + sb.AppendLine("HFS uses 512 bytes/sector while device uses 2048 bytes/sector.").AppendLine(); + + sb.AppendLine("Master Directory Block:"); + sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); + sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(mdb.drLsMod)).AppendLine(); + + if(mdb.drVolBkUp > 0) + { + sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drVolBkUp)).AppendLine(); + sb.AppendFormat("Backup sequence number: {0}", mdb.drVSeqNum).AppendLine(); + } + else + sb.AppendLine("Volume has never been backed up"); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) + sb.AppendLine("Volume is locked by hardware."); + + sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." + : "Volume is mounted."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) + sb.AppendLine("Volume has spared bad blocks."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) + sb.AppendLine("Volume does not need cache."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) + sb.AppendLine("Boot volume is inconsistent."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) + sb.AppendLine("There are reused CNIDs."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Journaled)) + sb.AppendLine("Volume is journaled."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) + sb.AppendLine("Volume is seriously inconsistent."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) + sb.AppendLine("Volume is locked by software."); + + sb.AppendFormat("{0} files on root directory", mdb.drNmFls).AppendLine(); + sb.AppendFormat("{0} directories on root directory", mdb.drNmRtDirs).AppendLine(); + sb.AppendFormat("{0} files on volume", mdb.drFilCnt).AppendLine(); + sb.AppendFormat("{0} directories on volume", mdb.drDirCnt).AppendLine(); + sb.AppendFormat("Volume write count: {0}", mdb.drWrCnt).AppendLine(); + + sb.AppendFormat("Volume bitmap starting sector (in 512-bytes): {0}", mdb.drVBMSt).AppendLine(); + sb.AppendFormat("Next allocation block: {0}.", mdb.drAllocPtr).AppendLine(); + sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks).AppendLine(); + sb.AppendFormat("{0} bytes per allocation block.", mdb.drAlBlkSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a file.", mdb.drClpSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a Extents B-Tree.", mdb.drXTClpSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate when extending a Catalog B-Tree.", mdb.drCTClpSiz).AppendLine(); + sb.AppendFormat("Sector of first allocation block: {0}", mdb.drAlBlSt).AppendLine(); + sb.AppendFormat("Next unused CNID: {0}", mdb.drNxtCNID).AppendLine(); + sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); + + sb.AppendFormat("{0} bytes in the Extents B-Tree", mdb.drXTFlSize).AppendLine(); + sb.AppendFormat("{0} bytes in the Catalog B-Tree", mdb.drCTFlSize).AppendLine(); + + sb.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(mdb.drVN, Encoding)).AppendLine(); + + sb.AppendLine("Finder info:"); + sb.AppendFormat("CNID of bootable system's directory: {0}", mdb.drFndrInfo0).AppendLine(); + sb.AppendFormat("CNID of first-run application's directory: {0}", mdb.drFndrInfo1).AppendLine(); + sb.AppendFormat("CNID of previously opened directory: {0}", mdb.drFndrInfo2).AppendLine(); + sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", mdb.drFndrInfo3).AppendLine(); + sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", mdb.drFndrInfo5).AppendLine(); + + if(mdb.drFndrInfo6 != 0 && + mdb.drFndrInfo7 != 0) + sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", mdb.drFndrInfo6, mdb.drFndrInfo7).AppendLine(); + + if(mdb.drEmbedSigWord == AppleCommon.HFSP_MAGIC) + { + sb.AppendLine("Volume wraps a HFS+ volume."); + sb.AppendFormat("Starting block of the HFS+ volume: {0}", mdb.xdrStABNt).AppendLine(); + sb.AppendFormat("Allocations blocks of the HFS+ volume: {0}", mdb.xdrNumABlks).AppendLine(); + } + else + { + sb.AppendFormat("{0} blocks in volume cache", mdb.drVCSize).AppendLine(); + sb.AppendFormat("{0} blocks in volume bitmap cache", mdb.drVBMCSize).AppendLine(); + sb.AppendFormat("{0} blocks in volume common cache", mdb.drCtlCSize).AppendLine(); + } + + string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); + + if(bootBlockInfo != null) + { + sb.AppendLine("Volume is bootable."); + sb.AppendLine(); + sb.AppendLine(bootBlockInfo); + } + else if(mdb.drFndrInfo0 != 0 || + mdb.drFndrInfo3 != 0 || + mdb.drFndrInfo5 != 0) + sb.AppendLine("Volume is bootable."); + else + sb.AppendLine("Volume is not bootable."); + + information = sb.ToString(); + + XmlFsType = new FileSystemType(); + + if(mdb.drVolBkUp > 0) + { + XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drVolBkUp); + XmlFsType.BackupDateSpecified = true; + } + + XmlFsType.Bootable = bootBlockInfo != null || mdb.drFndrInfo0 != 0 || mdb.drFndrInfo3 != 0 || + mdb.drFndrInfo5 != 0; + + XmlFsType.Clusters = mdb.drNmAlBlks; + XmlFsType.ClusterSize = mdb.drAlBlkSiz; + + if(mdb.drCrDate > 0) + { + XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); + XmlFsType.CreationDateSpecified = true; + } + + XmlFsType.Dirty = !mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted); + XmlFsType.Files = mdb.drFilCnt; + XmlFsType.FilesSpecified = true; + XmlFsType.FreeClusters = mdb.drFreeBks; + XmlFsType.FreeClustersSpecified = true; + + if(mdb.drLsMod > 0) + { + XmlFsType.ModificationDate = DateHandlers.MacToDateTime(mdb.drLsMod); + XmlFsType.ModificationDateSpecified = true; + } + + XmlFsType.Type = "HFS"; + XmlFsType.VolumeName = StringHandlers.PascalToString(mdb.drVN, Encoding); + + if(mdb.drFndrInfo6 != 0 && + mdb.drFndrInfo7 != 0) + XmlFsType.VolumeSerial = $"{mdb.drFndrInfo6:X8}{mdb.drFndrInfo7:X8}"; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleHFS/Structs.cs b/Aaru.Filesystems/AppleHFS/Structs.cs index 5cd042460..bab26b050 100644 --- a/Aaru.Filesystems/AppleHFS/Structs.cs +++ b/Aaru.Filesystems/AppleHFS/Structs.cs @@ -37,297 +37,296 @@ using System.Runtime.InteropServices; // ReSharper disable IdentifierTypo // ReSharper disable MemberCanBePrivate.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh +// https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf +public sealed partial class AppleHFS { - // Information from Inside Macintosh - // https://developer.apple.com/legacy/library/documentation/mac/pdf/Files/File_Manager.pdf - public sealed partial class AppleHFS + /// Master Directory Block, should be sector 2 in volume + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct MasterDirectoryBlock // Should be sector 2 in volume { - /// Master Directory Block, should be sector 2 in volume - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct MasterDirectoryBlock // Should be sector 2 in volume - { - /// 0x000, Signature, 0x4244 - public readonly ushort drSigWord; - /// 0x002, Volume creation date - public readonly uint drCrDate; - /// 0x006, Volume last modification date - public readonly uint drLsMod; - /// 0x00A, Volume attributes - public readonly AppleCommon.VolumeAttributes drAtrb; - /// 0x00C, Files in root directory - public readonly ushort drNmFls; - /// 0x00E, Start 512-byte sector of volume bitmap - public readonly ushort drVBMSt; - /// 0x010, Allocation block to begin next allocation - public readonly ushort drAllocPtr; - /// 0x012, Allocation blocks - public readonly ushort drNmAlBlks; - /// 0x014, Bytes per allocation block - public readonly uint drAlBlkSiz; - /// 0x018, Bytes to allocate when extending a file - public readonly uint drClpSiz; - /// 0x01C, Start 512-byte sector of first allocation block - public readonly ushort drAlBlSt; - /// 0x01E, CNID for next file - public readonly uint drNxtCNID; - /// 0x022, Free allocation blocks - public readonly ushort drFreeBks; - /// 0x024, Volume name (28 bytes) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] - public readonly byte[] drVN; - /// 0x040, Volume last backup time - public readonly uint drVolBkUp; - /// 0x044, Volume backup sequence number - public readonly ushort drVSeqNum; - /// 0x046, Filesystem write count - public readonly uint drWrCnt; - /// 0x04A, Bytes to allocate when extending the extents B-Tree - public readonly uint drXTClpSiz; - /// 0x04E, Bytes to allocate when extending the catalog B-Tree - public readonly uint drCTClpSiz; - /// 0x052, Number of directories in root directory - public readonly ushort drNmRtDirs; - /// 0x054, Number of files in the volume - public readonly uint drFilCnt; - /// 0x058, Number of directories in the volume - public readonly uint drDirCnt; - /// 0x05C, finderInfo[0], CNID for bootable system's directory - public readonly uint drFndrInfo0; - /// 0x060, finderInfo[1], CNID of the directory containing the boot application - public readonly uint drFndrInfo1; - /// 0x064, finderInfo[2], CNID of the directory that should be opened on boot - public readonly uint drFndrInfo2; - /// 0x068, finderInfo[3], CNID for Mac OS 8 or 9 directory - public readonly uint drFndrInfo3; - /// 0x06C, finderInfo[4], Reserved - public readonly uint drFndrInfo4; - /// 0x070, finderInfo[5], CNID for Mac OS X directory - public readonly uint drFndrInfo5; - /// 0x074, finderInfo[6], first part of Mac OS X volume ID - public readonly uint drFndrInfo6; - /// 0x078, finderInfo[7], second part of Mac OS X volume ID - public readonly uint drFndrInfo7; + /// 0x000, Signature, 0x4244 + public readonly ushort drSigWord; + /// 0x002, Volume creation date + public readonly uint drCrDate; + /// 0x006, Volume last modification date + public readonly uint drLsMod; + /// 0x00A, Volume attributes + public readonly AppleCommon.VolumeAttributes drAtrb; + /// 0x00C, Files in root directory + public readonly ushort drNmFls; + /// 0x00E, Start 512-byte sector of volume bitmap + public readonly ushort drVBMSt; + /// 0x010, Allocation block to begin next allocation + public readonly ushort drAllocPtr; + /// 0x012, Allocation blocks + public readonly ushort drNmAlBlks; + /// 0x014, Bytes per allocation block + public readonly uint drAlBlkSiz; + /// 0x018, Bytes to allocate when extending a file + public readonly uint drClpSiz; + /// 0x01C, Start 512-byte sector of first allocation block + public readonly ushort drAlBlSt; + /// 0x01E, CNID for next file + public readonly uint drNxtCNID; + /// 0x022, Free allocation blocks + public readonly ushort drFreeBks; + /// 0x024, Volume name (28 bytes) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] + public readonly byte[] drVN; + /// 0x040, Volume last backup time + public readonly uint drVolBkUp; + /// 0x044, Volume backup sequence number + public readonly ushort drVSeqNum; + /// 0x046, Filesystem write count + public readonly uint drWrCnt; + /// 0x04A, Bytes to allocate when extending the extents B-Tree + public readonly uint drXTClpSiz; + /// 0x04E, Bytes to allocate when extending the catalog B-Tree + public readonly uint drCTClpSiz; + /// 0x052, Number of directories in root directory + public readonly ushort drNmRtDirs; + /// 0x054, Number of files in the volume + public readonly uint drFilCnt; + /// 0x058, Number of directories in the volume + public readonly uint drDirCnt; + /// 0x05C, finderInfo[0], CNID for bootable system's directory + public readonly uint drFndrInfo0; + /// 0x060, finderInfo[1], CNID of the directory containing the boot application + public readonly uint drFndrInfo1; + /// 0x064, finderInfo[2], CNID of the directory that should be opened on boot + public readonly uint drFndrInfo2; + /// 0x068, finderInfo[3], CNID for Mac OS 8 or 9 directory + public readonly uint drFndrInfo3; + /// 0x06C, finderInfo[4], Reserved + public readonly uint drFndrInfo4; + /// 0x070, finderInfo[5], CNID for Mac OS X directory + public readonly uint drFndrInfo5; + /// 0x074, finderInfo[6], first part of Mac OS X volume ID + public readonly uint drFndrInfo6; + /// 0x078, finderInfo[7], second part of Mac OS X volume ID + public readonly uint drFndrInfo7; - // If wrapping HFS+ - /// 0x07C, Embedded volume signature, "H+" if HFS+ is embedded ignore following two fields if not - public readonly ushort drEmbedSigWord; - /// 0x07E, Starting block number of embedded HFS+ volume - public readonly ushort xdrStABNt; - /// 0x080, Allocation blocks used by embedded volume - public readonly ushort xdrNumABlks; + // If wrapping HFS+ + /// 0x07C, Embedded volume signature, "H+" if HFS+ is embedded ignore following two fields if not + public readonly ushort drEmbedSigWord; + /// 0x07E, Starting block number of embedded HFS+ volume + public readonly ushort xdrStABNt; + /// 0x080, Allocation blocks used by embedded volume + public readonly ushort xdrNumABlks; - // If not - /// 0x07C, Size in blocks of volume cache - public readonly ushort drVCSize; - /// 0x07E, Size in blocks of volume bitmap cache - public readonly ushort drVBMCSize; - /// 0x080, Size in blocks of volume common cache - public readonly ushort drCtlCSize; + // If not + /// 0x07C, Size in blocks of volume cache + public readonly ushort drVCSize; + /// 0x07E, Size in blocks of volume bitmap cache + public readonly ushort drVBMCSize; + /// 0x080, Size in blocks of volume common cache + public readonly ushort drCtlCSize; - // End of variable variables :D - /// 0x082, Bytes in the extents B-Tree 3 HFS extents following, 32 bits each - public readonly uint drXTFlSize; - /// 0x092, Bytes in the catalog B-Tree 3 HFS extents following, 32 bits each - public readonly uint drCTFlSize; - } + // End of variable variables :D + /// 0x082, Bytes in the extents B-Tree 3 HFS extents following, 32 bits each + public readonly uint drXTFlSize; + /// 0x092, Bytes in the catalog B-Tree 3 HFS extents following, 32 bits each + public readonly uint drCTFlSize; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct NodeDescriptor - { - /// A link to the next node of this type, or null if this is the last one. - public readonly uint ndFLink; - /// A link to the previous node of this type, or null if this is the first one. - public readonly uint ndBLink; - /// The type of this node. - public readonly NodeType ndType; - /// The depth of this node in the B*-tree hierarchy. Maximum depth is apparently 8. - public readonly sbyte ndNHeight; - /// The number of records contained in this node. - public readonly ushort ndNRecs; - /// Reserved, should be 0. - public readonly ushort ndResv2; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct NodeDescriptor + { + /// A link to the next node of this type, or null if this is the last one. + public readonly uint ndFLink; + /// A link to the previous node of this type, or null if this is the first one. + public readonly uint ndBLink; + /// The type of this node. + public readonly NodeType ndType; + /// The depth of this node in the B*-tree hierarchy. Maximum depth is apparently 8. + public readonly sbyte ndNHeight; + /// The number of records contained in this node. + public readonly ushort ndNRecs; + /// Reserved, should be 0. + public readonly ushort ndResv2; + } - /// B*-tree header - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BTHdrRed - { - /// Current depth of tree. - public readonly ushort bthDepth; - /// Number of root node. - public readonly uint bthRoot; - /// Number of leaf records in tree. - public readonly uint bthNRecs; - /// Number of first leaf node. - public readonly uint bthFNode; - /// Number of last leaf node. - public readonly uint bthLNode; - /// Size of a node. - public readonly ushort bthNodeSize; - /// Maximum length of a key. - public readonly ushort bthKeyLen; - /// Total number of nodes in tree. - public readonly uint bthNNodes; - /// Number of free nodes. - public readonly uint bthFree; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 76)] - public readonly sbyte[] bthResv; - } + /// B*-tree header + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BTHdrRed + { + /// Current depth of tree. + public readonly ushort bthDepth; + /// Number of root node. + public readonly uint bthRoot; + /// Number of leaf records in tree. + public readonly uint bthNRecs; + /// Number of first leaf node. + public readonly uint bthFNode; + /// Number of last leaf node. + public readonly uint bthLNode; + /// Size of a node. + public readonly ushort bthNodeSize; + /// Maximum length of a key. + public readonly ushort bthKeyLen; + /// Total number of nodes in tree. + public readonly uint bthNNodes; + /// Number of free nodes. + public readonly uint bthFree; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 76)] + public readonly sbyte[] bthResv; + } - /// Catalog key record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CatKeyRec - { - /// Key length. - public readonly sbyte ckrKeyLen; - /// Reserved. - public readonly sbyte ckrResrv1; - /// Parent directory ID. - public readonly uint ckrParID; - /// Catalog node name. Full 32 bytes in index nodes but only the needed bytes, padded to word, in leaf nodes. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] ckrCName; - } + /// Catalog key record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CatKeyRec + { + /// Key length. + public readonly sbyte ckrKeyLen; + /// Reserved. + public readonly sbyte ckrResrv1; + /// Parent directory ID. + public readonly uint ckrParID; + /// Catalog node name. Full 32 bytes in index nodes but only the needed bytes, padded to word, in leaf nodes. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] ckrCName; + } - /// Catalog data record header - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CatDataRec - { - public readonly CatDataType cdrType; - public readonly sbyte cdrResvr2; - } + /// Catalog data record header + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CatDataRec + { + public readonly CatDataType cdrType; + public readonly sbyte cdrResvr2; + } - /// Directory record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CdrDirRec - { - public readonly CatDataRec dirHdr; - /// Directory flags. - public readonly ushort dirFlags; - /// Directory valence. - public readonly ushort dirVal; - /// Directory ID. - public readonly uint dirDirID; - /// Date and time of creation. - public readonly uint dirCrDat; - /// Date and time of last modification. - public readonly uint dirMdDat; - /// Date and time of last backup. - public readonly uint dirBkDat; - /// Finder information. - public readonly AppleCommon.DInfo dirUsrInfo; - /// Additional Finder information. - public readonly AppleCommon.DXInfo dirFndrInfo; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly uint[] dirResrv; - } + /// Directory record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CdrDirRec + { + public readonly CatDataRec dirHdr; + /// Directory flags. + public readonly ushort dirFlags; + /// Directory valence. + public readonly ushort dirVal; + /// Directory ID. + public readonly uint dirDirID; + /// Date and time of creation. + public readonly uint dirCrDat; + /// Date and time of last modification. + public readonly uint dirMdDat; + /// Date and time of last backup. + public readonly uint dirBkDat; + /// Finder information. + public readonly AppleCommon.DInfo dirUsrInfo; + /// Additional Finder information. + public readonly AppleCommon.DXInfo dirFndrInfo; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly uint[] dirResrv; + } - /// File record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CdrFilRec - { - public readonly CatDataRec filHdr; - /// File flags. - public readonly sbyte filFlags; - /// File type. - public readonly sbyte filType; - /// Finder information. - public readonly AppleCommon.FInfo filUsrWds; - /// File ID. - public readonly uint filFlNum; - /// First allocation block of data fork. - public readonly ushort filStBlk; - /// Logical EOF of data fork. - public readonly uint filLgLen; - /// Physical EOF of data fork. - public readonly uint filPyLen; - /// First allocation block of resource fork. - public readonly ushort filRStBlk; - /// Logical EOF of resource fork. - public readonly uint filRLgLen; - /// Physical EOF of resource fork. - public readonly uint filRPyLen; - /// Date and time of creation. - public readonly uint filCrDat; - /// Date and time of last modification. - public readonly uint filMdDat; - /// Date and time of last backup. - public readonly uint filBkDat; - /// Additional Finder information. - public readonly AppleCommon.FXInfo filFndrInfo; - /// File clump size. - public readonly ushort filClpSize; - /// First data fork extent record. - public readonly ExtDataRec filExtRec; - /// First resource fork extent record. - public readonly ExtDataRec filRExtRec; - /// Reserved - public readonly uint filResrv; - } + /// File record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CdrFilRec + { + public readonly CatDataRec filHdr; + /// File flags. + public readonly sbyte filFlags; + /// File type. + public readonly sbyte filType; + /// Finder information. + public readonly AppleCommon.FInfo filUsrWds; + /// File ID. + public readonly uint filFlNum; + /// First allocation block of data fork. + public readonly ushort filStBlk; + /// Logical EOF of data fork. + public readonly uint filLgLen; + /// Physical EOF of data fork. + public readonly uint filPyLen; + /// First allocation block of resource fork. + public readonly ushort filRStBlk; + /// Logical EOF of resource fork. + public readonly uint filRLgLen; + /// Physical EOF of resource fork. + public readonly uint filRPyLen; + /// Date and time of creation. + public readonly uint filCrDat; + /// Date and time of last modification. + public readonly uint filMdDat; + /// Date and time of last backup. + public readonly uint filBkDat; + /// Additional Finder information. + public readonly AppleCommon.FXInfo filFndrInfo; + /// File clump size. + public readonly ushort filClpSize; + /// First data fork extent record. + public readonly ExtDataRec filExtRec; + /// First resource fork extent record. + public readonly ExtDataRec filRExtRec; + /// Reserved + public readonly uint filResrv; + } - /// Directory thread record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CdrThdRec - { - public readonly CatDataRec thdHdr; - /// Reserved. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly uint[] thdResrv; - /// Parent ID for this directory. - public readonly uint thdParID; - /// Name of this directory. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] thdCName; - } + /// Directory thread record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CdrThdRec + { + public readonly CatDataRec thdHdr; + /// Reserved. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly uint[] thdResrv; + /// Parent ID for this directory. + public readonly uint thdParID; + /// Name of this directory. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] thdCName; + } - /// File thread record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CdrFThdRec - { - public readonly CatDataRec fthdHdr; - /// Reserved. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly uint[] fthdResrv; - /// Parent ID for this file. - public readonly uint fthdParID; - /// Name of this file. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] fthdCName; - } + /// File thread record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CdrFThdRec + { + public readonly CatDataRec fthdHdr; + /// Reserved. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly uint[] fthdResrv; + /// Parent ID for this file. + public readonly uint fthdParID; + /// Name of this file. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] fthdCName; + } - /// Extent descriptor - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ExtDescriptor - { - /// First allocation block - public readonly ushort xdrStABN; - /// Number of allocation blocks - public readonly ushort xdrNumABlks; - } + /// Extent descriptor + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ExtDescriptor + { + /// First allocation block + public readonly ushort xdrStABN; + /// Number of allocation blocks + public readonly ushort xdrNumABlks; + } - /// Extent data record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ExtDataRec - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly ExtDescriptor[] xdr; - } + /// Extent data record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ExtDataRec + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly ExtDescriptor[] xdr; + } - /// Extent key record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ExtKeyRec - { - /// Key length. - public readonly sbyte xkrKeyLen; - /// Fork type. - public readonly ForkType xkrFkType; - /// File number. - public readonly uint xkrFNum; - /// Starting file allocation block. - public readonly ushort xkrFABN; - } + /// Extent key record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ExtKeyRec + { + /// Key length. + public readonly sbyte xkrKeyLen; + /// Fork type. + public readonly ForkType xkrFkType; + /// File number. + public readonly uint xkrFNum; + /// Starting file allocation block. + public readonly ushort xkrFABN; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleHFSPlus.cs b/Aaru.Filesystems/AppleHFSPlus.cs index 1e991ecec..20e7cb64d 100644 --- a/Aaru.Filesystems/AppleHFSPlus.cs +++ b/Aaru.Filesystems/AppleHFSPlus.cs @@ -40,549 +40,548 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Apple TechNote 1150: https://developer.apple.com/legacy/library/technotes/tn/tn1150.html +/// +/// Implements detection of Apple Hierarchical File System Plus (HFS+) +public sealed class AppleHFSPlus : IFilesystem { - // Information from Apple TechNote 1150: https://developer.apple.com/legacy/library/technotes/tn/tn1150.html /// - /// Implements detection of Apple Hierarchical File System Plus (HFS+) - public sealed class AppleHFSPlus : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Apple HFS+ filesystem"; + /// + public Guid Id => new("36405F8D-0D26-6EBE-436F-62F0586B4F08"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Apple HFS+ filesystem"; - /// - public Guid Id => new("36405F8D-0D26-6EBE-436F-62F0586B4F08"); - /// - public string Author => "Natalia Portillo"; + if(2 + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ulong hfspOffset; + + uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize; + + if(0x800 % imagePlugin.Info.SectorSize > 0) + sectorsToRead++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sectorsToRead, out byte[] vhSector); + + if(errno != ErrorNumber.NoError) + return false; + + if(vhSector.Length < 0x800) + return false; + + ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); + + if(drSigWord == AppleCommon.HFS_MAGIC) // "BD" { - if(2 + partition.Start >= partition.End) - return false; + drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature - ulong hfspOffset; - - uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize; - - if(0x800 % imagePlugin.Info.SectorSize > 0) - sectorsToRead++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sectorsToRead, out byte[] vhSector); - - if(errno != ErrorNumber.NoError) - return false; - - if(vhSector.Length < 0x800) - return false; - - ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); - - if(drSigWord == AppleCommon.HFS_MAGIC) // "BD" + if(drSigWord == AppleCommon.HFSP_MAGIC) // "H+" { - drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature + // ReSharper disable once InconsistentNaming + ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E); - if(drSigWord == AppleCommon.HFSP_MAGIC) // "H+" - { - // ReSharper disable once InconsistentNaming - ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E); + uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414); - uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414); + ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C); - ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C); - - hfspOffset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.Info.SectorSize); - } - else - hfspOffset = 0; + hfspOffset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.Info.SectorSize); } else hfspOffset = 0; - - errno = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead, - out vhSector); // Read volume header - - if(errno != ErrorNumber.NoError) - return false; - - drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); - - return drSigWord == AppleCommon.HFSP_MAGIC || drSigWord == AppleCommon.HFSX_MAGIC; } + else + hfspOffset = 0; - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + errno = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead, + out vhSector); // Read volume header + + if(errno != ErrorNumber.NoError) + return false; + + drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); + + return drSigWord == AppleCommon.HFSP_MAGIC || drSigWord == AppleCommon.HFSX_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = Encoding.BigEndianUnicode; + information = ""; + + var vh = new VolumeHeader(); + + ulong hfspOffset; + bool wrapped; + + uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize; + + if(0x800 % imagePlugin.Info.SectorSize > 0) + sectorsToRead++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sectorsToRead, out byte[] vhSector); + + if(errno != ErrorNumber.NoError) + return; + + ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); + + if(drSigWord == AppleCommon.HFS_MAGIC) // "BD" { - Encoding = Encoding.BigEndianUnicode; - information = ""; + drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature - var vh = new VolumeHeader(); - - ulong hfspOffset; - bool wrapped; - - uint sectorsToRead = 0x800 / imagePlugin.Info.SectorSize; - - if(0x800 % imagePlugin.Info.SectorSize > 0) - sectorsToRead++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sectorsToRead, out byte[] vhSector); - - if(errno != ErrorNumber.NoError) - return; - - ushort drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x400); - - if(drSigWord == AppleCommon.HFS_MAGIC) // "BD" + if(drSigWord == AppleCommon.HFSP_MAGIC) // "H+" { - drSigWord = BigEndianBitConverter.ToUInt16(vhSector, 0x47C); // Read embedded HFS+ signature + // ReSharper disable once InconsistentNaming + ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E); - if(drSigWord == AppleCommon.HFSP_MAGIC) // "H+" - { - // ReSharper disable once InconsistentNaming - ushort xdrStABNt = BigEndianBitConverter.ToUInt16(vhSector, 0x47E); + uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414); - uint drAlBlkSiz = BigEndianBitConverter.ToUInt32(vhSector, 0x414); + ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C); - ushort drAlBlSt = BigEndianBitConverter.ToUInt16(vhSector, 0x41C); - - hfspOffset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.Info.SectorSize); - wrapped = true; - } - else - { - hfspOffset = 0; - wrapped = false; - } + hfspOffset = (ulong)(((drAlBlSt * 512) + (xdrStABNt * drAlBlkSiz)) / imagePlugin.Info.SectorSize); + wrapped = true; } else { hfspOffset = 0; wrapped = false; } + } + else + { + hfspOffset = 0; + wrapped = false; + } - errno = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead, - out vhSector); // Read volume header + errno = imagePlugin.ReadSectors(partition.Start + hfspOffset, sectorsToRead, + out vhSector); // Read volume header - if(errno != ErrorNumber.NoError) - return; + if(errno != ErrorNumber.NoError) + return; - vh.signature = BigEndianBitConverter.ToUInt16(vhSector, 0x400); + vh.signature = BigEndianBitConverter.ToUInt16(vhSector, 0x400); - if(vh.signature != AppleCommon.HFSP_MAGIC && - vh.signature != AppleCommon.HFSX_MAGIC) - return; + if(vh.signature != AppleCommon.HFSP_MAGIC && + vh.signature != AppleCommon.HFSX_MAGIC) + return; - var sb = new StringBuilder(); + var sb = new StringBuilder(); + + if(vh.signature == 0x482B) + sb.AppendLine("HFS+ filesystem."); + + if(vh.signature == 0x4858) + sb.AppendLine("HFSX filesystem."); + + if(wrapped) + sb.AppendLine("Volume is wrapped inside an HFS volume."); + + byte[] tmp = new byte[0x400]; + Array.Copy(vhSector, 0x400, tmp, 0, 0x400); + vhSector = tmp; + + vh = Marshal.ByteArrayToStructureBigEndian(vhSector); + + if(vh.version == 4 || + vh.version == 5) + { + sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); + + if((vh.attributes & 0x80) == 0x80) + sb.AppendLine("Volume is locked on hardware."); + + if((vh.attributes & 0x100) == 0x100) + sb.AppendLine("Volume is unmounted."); + + if((vh.attributes & 0x200) == 0x200) + sb.AppendLine("There are bad blocks in the extents file."); + + if((vh.attributes & 0x400) == 0x400) + sb.AppendLine("Volume does not require cache."); + + if((vh.attributes & 0x800) == 0x800) + sb.AppendLine("Volume state is inconsistent."); + + if((vh.attributes & 0x1000) == 0x1000) + sb.AppendLine("CNIDs are reused."); + + if((vh.attributes & 0x2000) == 0x2000) + sb.AppendLine("Volume is journaled."); + + if((vh.attributes & 0x8000) == 0x8000) + sb.AppendLine("Volume is locked on software."); + + sb.AppendFormat("Implementation that last mounted the volume: \"{0}\".", + Encoding.ASCII.GetString(vh.lastMountedVersion)).AppendLine(); + + if((vh.attributes & 0x2000) == 0x2000) + sb.AppendFormat("Journal starts at allocation block {0}.", vh.journalInfoBlock).AppendLine(); + + sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(vh.createDate)).AppendLine(); + sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(vh.modifyDate)).AppendLine(); + + if(vh.backupDate > 0) + sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(vh.backupDate)).AppendLine(); + else + sb.AppendLine("Volume has never been backed up"); + + if(vh.backupDate > 0) + sb.AppendFormat("Last check date: {0}", DateHandlers.MacToDateTime(vh.checkedDate)).AppendLine(); + else + sb.AppendLine("Volume has never been checked up"); + + sb.AppendFormat("{0} files on volume.", vh.fileCount).AppendLine(); + sb.AppendFormat("{0} folders on volume.", vh.folderCount).AppendLine(); + sb.AppendFormat("{0} bytes per allocation block.", vh.blockSize).AppendLine(); + sb.AppendFormat("{0} allocation blocks.", vh.totalBlocks).AppendLine(); + sb.AppendFormat("{0} free blocks.", vh.freeBlocks).AppendLine(); + sb.AppendFormat("Next allocation block: {0}.", vh.nextAllocation).AppendLine(); + sb.AppendFormat("Resource fork clump size: {0} bytes.", vh.rsrcClumpSize).AppendLine(); + sb.AppendFormat("Data fork clump size: {0} bytes.", vh.dataClumpSize).AppendLine(); + sb.AppendFormat("Next unused CNID: {0}.", vh.nextCatalogID).AppendLine(); + sb.AppendFormat("Volume has been mounted writable {0} times.", vh.writeCount).AppendLine(); + sb.AppendFormat("Allocation File is {0} bytes.", vh.allocationFile_logicalSize).AppendLine(); + sb.AppendFormat("Extents File is {0} bytes.", vh.extentsFile_logicalSize).AppendLine(); + sb.AppendFormat("Catalog File is {0} bytes.", vh.catalogFile_logicalSize).AppendLine(); + sb.AppendFormat("Attributes File is {0} bytes.", vh.attributesFile_logicalSize).AppendLine(); + sb.AppendFormat("Startup File is {0} bytes.", vh.startupFile_logicalSize).AppendLine(); + sb.AppendLine("Finder info:"); + sb.AppendFormat("CNID of bootable system's directory: {0}", vh.drFndrInfo0).AppendLine(); + sb.AppendFormat("CNID of first-run application's directory: {0}", vh.drFndrInfo1).AppendLine(); + sb.AppendFormat("CNID of previously opened directory: {0}", vh.drFndrInfo2).AppendLine(); + sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", vh.drFndrInfo3).AppendLine(); + sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", vh.drFndrInfo5).AppendLine(); + + if(vh.drFndrInfo6 != 0 && + vh.drFndrInfo7 != 0) + sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", vh.drFndrInfo6, vh.drFndrInfo7).AppendLine(); + + XmlFsType = new FileSystemType(); + + if(vh.backupDate > 0) + { + XmlFsType.BackupDate = DateHandlers.MacToDateTime(vh.backupDate); + XmlFsType.BackupDateSpecified = true; + } + + XmlFsType.Bootable |= vh.drFndrInfo0 != 0 || vh.drFndrInfo3 != 0 || vh.drFndrInfo5 != 0; + XmlFsType.Clusters = vh.totalBlocks; + XmlFsType.ClusterSize = vh.blockSize; + + if(vh.createDate > 0) + { + XmlFsType.CreationDate = DateHandlers.MacToDateTime(vh.createDate); + XmlFsType.CreationDateSpecified = true; + } + + XmlFsType.Dirty = (vh.attributes & 0x100) != 0x100; + XmlFsType.Files = vh.fileCount; + XmlFsType.FilesSpecified = true; + XmlFsType.FreeClusters = vh.freeBlocks; + XmlFsType.FreeClustersSpecified = true; + + if(vh.modifyDate > 0) + { + XmlFsType.ModificationDate = DateHandlers.MacToDateTime(vh.modifyDate); + XmlFsType.ModificationDateSpecified = true; + } if(vh.signature == 0x482B) - sb.AppendLine("HFS+ filesystem."); + XmlFsType.Type = "HFS+"; if(vh.signature == 0x4858) - sb.AppendLine("HFSX filesystem."); + XmlFsType.Type = "HFSX"; - if(wrapped) - sb.AppendLine("Volume is wrapped inside an HFS volume."); + if(vh.drFndrInfo6 != 0 && + vh.drFndrInfo7 != 0) + XmlFsType.VolumeSerial = $"{vh.drFndrInfo6:X8}{vh.drFndrInfo7:X8}"; - byte[] tmp = new byte[0x400]; - Array.Copy(vhSector, 0x400, tmp, 0, 0x400); - vhSector = tmp; - - vh = Marshal.ByteArrayToStructureBigEndian(vhSector); - - if(vh.version == 4 || - vh.version == 5) - { - sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); - - if((vh.attributes & 0x80) == 0x80) - sb.AppendLine("Volume is locked on hardware."); - - if((vh.attributes & 0x100) == 0x100) - sb.AppendLine("Volume is unmounted."); - - if((vh.attributes & 0x200) == 0x200) - sb.AppendLine("There are bad blocks in the extents file."); - - if((vh.attributes & 0x400) == 0x400) - sb.AppendLine("Volume does not require cache."); - - if((vh.attributes & 0x800) == 0x800) - sb.AppendLine("Volume state is inconsistent."); - - if((vh.attributes & 0x1000) == 0x1000) - sb.AppendLine("CNIDs are reused."); - - if((vh.attributes & 0x2000) == 0x2000) - sb.AppendLine("Volume is journaled."); - - if((vh.attributes & 0x8000) == 0x8000) - sb.AppendLine("Volume is locked on software."); - - sb.AppendFormat("Implementation that last mounted the volume: \"{0}\".", - Encoding.ASCII.GetString(vh.lastMountedVersion)).AppendLine(); - - if((vh.attributes & 0x2000) == 0x2000) - sb.AppendFormat("Journal starts at allocation block {0}.", vh.journalInfoBlock).AppendLine(); - - sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(vh.createDate)).AppendLine(); - sb.AppendFormat("Last modification date: {0}", DateHandlers.MacToDateTime(vh.modifyDate)).AppendLine(); - - if(vh.backupDate > 0) - sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(vh.backupDate)).AppendLine(); - else - sb.AppendLine("Volume has never been backed up"); - - if(vh.backupDate > 0) - sb.AppendFormat("Last check date: {0}", DateHandlers.MacToDateTime(vh.checkedDate)).AppendLine(); - else - sb.AppendLine("Volume has never been checked up"); - - sb.AppendFormat("{0} files on volume.", vh.fileCount).AppendLine(); - sb.AppendFormat("{0} folders on volume.", vh.folderCount).AppendLine(); - sb.AppendFormat("{0} bytes per allocation block.", vh.blockSize).AppendLine(); - sb.AppendFormat("{0} allocation blocks.", vh.totalBlocks).AppendLine(); - sb.AppendFormat("{0} free blocks.", vh.freeBlocks).AppendLine(); - sb.AppendFormat("Next allocation block: {0}.", vh.nextAllocation).AppendLine(); - sb.AppendFormat("Resource fork clump size: {0} bytes.", vh.rsrcClumpSize).AppendLine(); - sb.AppendFormat("Data fork clump size: {0} bytes.", vh.dataClumpSize).AppendLine(); - sb.AppendFormat("Next unused CNID: {0}.", vh.nextCatalogID).AppendLine(); - sb.AppendFormat("Volume has been mounted writable {0} times.", vh.writeCount).AppendLine(); - sb.AppendFormat("Allocation File is {0} bytes.", vh.allocationFile_logicalSize).AppendLine(); - sb.AppendFormat("Extents File is {0} bytes.", vh.extentsFile_logicalSize).AppendLine(); - sb.AppendFormat("Catalog File is {0} bytes.", vh.catalogFile_logicalSize).AppendLine(); - sb.AppendFormat("Attributes File is {0} bytes.", vh.attributesFile_logicalSize).AppendLine(); - sb.AppendFormat("Startup File is {0} bytes.", vh.startupFile_logicalSize).AppendLine(); - sb.AppendLine("Finder info:"); - sb.AppendFormat("CNID of bootable system's directory: {0}", vh.drFndrInfo0).AppendLine(); - sb.AppendFormat("CNID of first-run application's directory: {0}", vh.drFndrInfo1).AppendLine(); - sb.AppendFormat("CNID of previously opened directory: {0}", vh.drFndrInfo2).AppendLine(); - sb.AppendFormat("CNID of bootable Mac OS 8 or 9 directory: {0}", vh.drFndrInfo3).AppendLine(); - sb.AppendFormat("CNID of bootable Mac OS X directory: {0}", vh.drFndrInfo5).AppendLine(); - - if(vh.drFndrInfo6 != 0 && - vh.drFndrInfo7 != 0) - sb.AppendFormat("Mac OS X Volume ID: {0:X8}{1:X8}", vh.drFndrInfo6, vh.drFndrInfo7).AppendLine(); - - XmlFsType = new FileSystemType(); - - if(vh.backupDate > 0) - { - XmlFsType.BackupDate = DateHandlers.MacToDateTime(vh.backupDate); - XmlFsType.BackupDateSpecified = true; - } - - XmlFsType.Bootable |= vh.drFndrInfo0 != 0 || vh.drFndrInfo3 != 0 || vh.drFndrInfo5 != 0; - XmlFsType.Clusters = vh.totalBlocks; - XmlFsType.ClusterSize = vh.blockSize; - - if(vh.createDate > 0) - { - XmlFsType.CreationDate = DateHandlers.MacToDateTime(vh.createDate); - XmlFsType.CreationDateSpecified = true; - } - - XmlFsType.Dirty = (vh.attributes & 0x100) != 0x100; - XmlFsType.Files = vh.fileCount; - XmlFsType.FilesSpecified = true; - XmlFsType.FreeClusters = vh.freeBlocks; - XmlFsType.FreeClustersSpecified = true; - - if(vh.modifyDate > 0) - { - XmlFsType.ModificationDate = DateHandlers.MacToDateTime(vh.modifyDate); - XmlFsType.ModificationDateSpecified = true; - } - - if(vh.signature == 0x482B) - XmlFsType.Type = "HFS+"; - - if(vh.signature == 0x4858) - XmlFsType.Type = "HFSX"; - - if(vh.drFndrInfo6 != 0 && - vh.drFndrInfo7 != 0) - XmlFsType.VolumeSerial = $"{vh.drFndrInfo6:X8}{vh.drFndrInfo7:X8}"; - - XmlFsType.SystemIdentifier = Encoding.ASCII.GetString(vh.lastMountedVersion); - } - else - { - sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); - sb.AppendLine("This version is not supported yet."); - } - - information = sb.ToString(); + XmlFsType.SystemIdentifier = Encoding.ASCII.GetString(vh.lastMountedVersion); } - - /// HFS+ Volume Header, should be at offset 0x0400 bytes in volume with a size of 532 bytes - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct VolumeHeader + else { - /// 0x000, "H+" for HFS+, "HX" for HFSX - public ushort signature; - /// 0x002, 4 for HFS+, 5 for HFSX - public readonly ushort version; - /// 0x004, Volume attributes - public readonly uint attributes; - /// - /// 0x008, Implementation that last mounted the volume. Reserved by Apple: "8.10" Mac OS 8.1 to 9.2.2 "10.0" Mac - /// OS X "HFSJ" Journaled implementation "fsck" /sbin/fsck - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] lastMountedVersion; - /// 0x00C, Allocation block number containing the journal - public readonly uint journalInfoBlock; - /// 0x010, Date of volume creation - public readonly uint createDate; - /// 0x014, Date of last volume modification - public readonly uint modifyDate; - /// 0x018, Date of last backup - public readonly uint backupDate; - /// 0x01C, Date of last consistency check - public readonly uint checkedDate; - /// 0x020, File on the volume - public readonly uint fileCount; - /// 0x024, Folders on the volume - public readonly uint folderCount; - /// 0x028, Bytes per allocation block - public readonly uint blockSize; - /// 0x02C, Allocation blocks on the volume - public readonly uint totalBlocks; - /// 0x030, Free allocation blocks - public readonly uint freeBlocks; - /// 0x034, Hint for next allocation block - public readonly uint nextAllocation; - /// 0x038, Resource fork clump size - public readonly uint rsrcClumpSize; - /// 0x03C, Data fork clump size - public readonly uint dataClumpSize; - /// 0x040, Next unused CNID - public readonly uint nextCatalogID; - /// 0x044, Times that the volume has been mounted writable - public readonly uint writeCount; - /// 0x048, Used text encoding hints - public readonly ulong encodingsBitmap; - /// 0x050, finderInfo[0], CNID for bootable system's directory - public readonly uint drFndrInfo0; - /// 0x054, finderInfo[1], CNID of the directory containing the boot application - public readonly uint drFndrInfo1; - /// 0x058, finderInfo[2], CNID of the directory that should be opened on boot - public readonly uint drFndrInfo2; - /// 0x05C, finderInfo[3], CNID for Mac OS 8 or 9 directory - public readonly uint drFndrInfo3; - /// 0x060, finderInfo[4], Reserved - public readonly uint drFndrInfo4; - /// 0x064, finderInfo[5], CNID for Mac OS X directory - public readonly uint drFndrInfo5; - /// 0x068, finderInfo[6], first part of Mac OS X volume ID - public readonly uint drFndrInfo6; - /// 0x06C, finderInfo[7], second part of Mac OS X volume ID - public readonly uint drFndrInfo7; - - // HFSPlusForkData allocationFile; - /// 0x070 - public readonly ulong allocationFile_logicalSize; - /// 0x078 - public readonly uint allocationFile_clumpSize; - /// 0x07C - public readonly uint allocationFile_totalBlocks; - /// 0x080 - public readonly uint allocationFile_extents_startBlock0; - /// 0x084 - public readonly uint allocationFile_extents_blockCount0; - /// 0x088 - public readonly uint allocationFile_extents_startBlock1; - /// 0x08C - public readonly uint allocationFile_extents_blockCount1; - /// 0x090 - public readonly uint allocationFile_extents_startBlock2; - /// 0x094 - public readonly uint allocationFile_extents_blockCount2; - /// 0x098 - public readonly uint allocationFile_extents_startBlock3; - /// 0x09C - public readonly uint allocationFile_extents_blockCount3; - /// 0x0A0 - public readonly uint allocationFile_extents_startBlock4; - /// 0x0A4 - public readonly uint allocationFile_extents_blockCount4; - /// 0x0A8 - public readonly uint allocationFile_extents_startBlock5; - /// 0x0AC - public readonly uint allocationFile_extents_blockCount5; - /// 0x0B0 - public readonly uint allocationFile_extents_startBlock6; - /// 0x0B4 - public readonly uint allocationFile_extents_blockCount6; - /// 0x0B8 - public readonly uint allocationFile_extents_startBlock7; - /// 0x0BC - public readonly uint allocationFile_extents_blockCount7; - - // HFSPlusForkData extentsFile; - /// 0x0C0 - public readonly ulong extentsFile_logicalSize; - /// 0x0C8 - public readonly uint extentsFile_clumpSize; - /// 0x0CC - public readonly uint extentsFile_totalBlocks; - /// 0x0D0 - public readonly uint extentsFile_extents_startBlock0; - /// 0x0D4 - public readonly uint extentsFile_extents_blockCount0; - /// 0x0D8 - public readonly uint extentsFile_extents_startBlock1; - /// 0x0DC - public readonly uint extentsFile_extents_blockCount1; - /// 0x0E0 - public readonly uint extentsFile_extents_startBlock2; - /// 0x0E4 - public readonly uint extentsFile_extents_blockCount2; - /// 0x0E8 - public readonly uint extentsFile_extents_startBlock3; - /// 0x0EC - public readonly uint extentsFile_extents_blockCount3; - /// 0x0F0 - public readonly uint extentsFile_extents_startBlock4; - /// 0x0F4 - public readonly uint extentsFile_extents_blockCount4; - /// 0x0F8 - public readonly uint extentsFile_extents_startBlock5; - /// 0x0FC - public readonly uint extentsFile_extents_blockCount5; - /// 0x100 - public readonly uint extentsFile_extents_startBlock6; - /// 0x104 - public readonly uint extentsFile_extents_blockCount6; - /// 0x108 - public readonly uint extentsFile_extents_startBlock7; - /// 0x10C - public readonly uint extentsFile_extents_blockCount7; - - // HFSPlusForkData catalogFile; - /// 0x110 - public readonly ulong catalogFile_logicalSize; - /// 0x118 - public readonly uint catalogFile_clumpSize; - /// 0x11C - public readonly uint catalogFile_totalBlocks; - /// 0x120 - public readonly uint catalogFile_extents_startBlock0; - /// 0x124 - public readonly uint catalogFile_extents_blockCount0; - /// 0x128 - public readonly uint catalogFile_extents_startBlock1; - /// 0x12C - public readonly uint catalogFile_extents_blockCount1; - /// 0x130 - public readonly uint catalogFile_extents_startBlock2; - /// 0x134 - public readonly uint catalogFile_extents_blockCount2; - /// 0x138 - public readonly uint catalogFile_extents_startBlock3; - /// 0x13C - public readonly uint catalogFile_extents_blockCount3; - /// 0x140 - public readonly uint catalogFile_extents_startBlock4; - /// 0x144 - public readonly uint catalogFile_extents_blockCount4; - /// 0x148 - public readonly uint catalogFile_extents_startBlock5; - /// 0x14C - public readonly uint catalogFile_extents_blockCount5; - /// 0x150 - public readonly uint catalogFile_extents_startBlock6; - /// 0x154 - public readonly uint catalogFile_extents_blockCount6; - /// 0x158 - public readonly uint catalogFile_extents_startBlock7; - /// 0x15C - public readonly uint catalogFile_extents_blockCount7; - - // HFSPlusForkData attributesFile; - /// 0x160 - public readonly ulong attributesFile_logicalSize; - /// 0x168 - public readonly uint attributesFile_clumpSize; - /// 0x16C - public readonly uint attributesFile_totalBlocks; - /// 0x170 - public readonly uint attributesFile_extents_startBlock0; - /// 0x174 - public readonly uint attributesFile_extents_blockCount0; - /// 0x178 - public readonly uint attributesFile_extents_startBlock1; - /// 0x17C - public readonly uint attributesFile_extents_blockCount1; - /// 0x180 - public readonly uint attributesFile_extents_startBlock2; - /// 0x184 - public readonly uint attributesFile_extents_blockCount2; - /// 0x188 - public readonly uint attributesFile_extents_startBlock3; - /// 0x18C - public readonly uint attributesFile_extents_blockCount3; - /// 0x190 - public readonly uint attributesFile_extents_startBlock4; - /// 0x194 - public readonly uint attributesFile_extents_blockCount4; - /// 0x198 - public readonly uint attributesFile_extents_startBlock5; - /// 0x19C - public readonly uint attributesFile_extents_blockCount5; - /// 0x1A0 - public readonly uint attributesFile_extents_startBlock6; - /// 0x1A4 - public readonly uint attributesFile_extents_blockCount6; - /// 0x1A8 - public readonly uint attributesFile_extents_startBlock7; - /// 0x1AC - public readonly uint attributesFile_extents_blockCount7; - - // HFSPlusForkData startupFile; - /// 0x1B0 - public readonly ulong startupFile_logicalSize; - /// 0x1B8 - public readonly uint startupFile_clumpSize; - /// 0x1BC - public readonly uint startupFile_totalBlocks; - /// 0x1C0 - public readonly uint startupFile_extents_startBlock0; - /// 0x1C4 - public readonly uint startupFile_extents_blockCount0; - /// 0x1C8 - public readonly uint startupFile_extents_startBlock1; - /// 0x1D0 - public readonly uint startupFile_extents_blockCount1; - /// 0x1D4 - public readonly uint startupFile_extents_startBlock2; - /// 0x1D8 - public readonly uint startupFile_extents_blockCount2; - /// 0x1DC - public readonly uint startupFile_extents_startBlock3; - /// 0x1E0 - public readonly uint startupFile_extents_blockCount3; - /// 0x1E4 - public readonly uint startupFile_extents_startBlock4; - /// 0x1E8 - public readonly uint startupFile_extents_blockCount4; - /// 0x1EC - public readonly uint startupFile_extents_startBlock5; - /// 0x1F0 - public readonly uint startupFile_extents_blockCount5; - /// 0x1F4 - public readonly uint startupFile_extents_startBlock6; - /// 0x1F8 - public readonly uint startupFile_extents_blockCount6; - /// 0x1FC - public readonly uint startupFile_extents_startBlock7; - /// 0x200 - public readonly uint startupFile_extents_blockCount7; + sb.AppendFormat("Filesystem version is {0}.", vh.version).AppendLine(); + sb.AppendLine("This version is not supported yet."); } + + information = sb.ToString(); + } + + /// HFS+ Volume Header, should be at offset 0x0400 bytes in volume with a size of 532 bytes + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct VolumeHeader + { + /// 0x000, "H+" for HFS+, "HX" for HFSX + public ushort signature; + /// 0x002, 4 for HFS+, 5 for HFSX + public readonly ushort version; + /// 0x004, Volume attributes + public readonly uint attributes; + /// + /// 0x008, Implementation that last mounted the volume. Reserved by Apple: "8.10" Mac OS 8.1 to 9.2.2 "10.0" Mac + /// OS X "HFSJ" Journaled implementation "fsck" /sbin/fsck + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] lastMountedVersion; + /// 0x00C, Allocation block number containing the journal + public readonly uint journalInfoBlock; + /// 0x010, Date of volume creation + public readonly uint createDate; + /// 0x014, Date of last volume modification + public readonly uint modifyDate; + /// 0x018, Date of last backup + public readonly uint backupDate; + /// 0x01C, Date of last consistency check + public readonly uint checkedDate; + /// 0x020, File on the volume + public readonly uint fileCount; + /// 0x024, Folders on the volume + public readonly uint folderCount; + /// 0x028, Bytes per allocation block + public readonly uint blockSize; + /// 0x02C, Allocation blocks on the volume + public readonly uint totalBlocks; + /// 0x030, Free allocation blocks + public readonly uint freeBlocks; + /// 0x034, Hint for next allocation block + public readonly uint nextAllocation; + /// 0x038, Resource fork clump size + public readonly uint rsrcClumpSize; + /// 0x03C, Data fork clump size + public readonly uint dataClumpSize; + /// 0x040, Next unused CNID + public readonly uint nextCatalogID; + /// 0x044, Times that the volume has been mounted writable + public readonly uint writeCount; + /// 0x048, Used text encoding hints + public readonly ulong encodingsBitmap; + /// 0x050, finderInfo[0], CNID for bootable system's directory + public readonly uint drFndrInfo0; + /// 0x054, finderInfo[1], CNID of the directory containing the boot application + public readonly uint drFndrInfo1; + /// 0x058, finderInfo[2], CNID of the directory that should be opened on boot + public readonly uint drFndrInfo2; + /// 0x05C, finderInfo[3], CNID for Mac OS 8 or 9 directory + public readonly uint drFndrInfo3; + /// 0x060, finderInfo[4], Reserved + public readonly uint drFndrInfo4; + /// 0x064, finderInfo[5], CNID for Mac OS X directory + public readonly uint drFndrInfo5; + /// 0x068, finderInfo[6], first part of Mac OS X volume ID + public readonly uint drFndrInfo6; + /// 0x06C, finderInfo[7], second part of Mac OS X volume ID + public readonly uint drFndrInfo7; + + // HFSPlusForkData allocationFile; + /// 0x070 + public readonly ulong allocationFile_logicalSize; + /// 0x078 + public readonly uint allocationFile_clumpSize; + /// 0x07C + public readonly uint allocationFile_totalBlocks; + /// 0x080 + public readonly uint allocationFile_extents_startBlock0; + /// 0x084 + public readonly uint allocationFile_extents_blockCount0; + /// 0x088 + public readonly uint allocationFile_extents_startBlock1; + /// 0x08C + public readonly uint allocationFile_extents_blockCount1; + /// 0x090 + public readonly uint allocationFile_extents_startBlock2; + /// 0x094 + public readonly uint allocationFile_extents_blockCount2; + /// 0x098 + public readonly uint allocationFile_extents_startBlock3; + /// 0x09C + public readonly uint allocationFile_extents_blockCount3; + /// 0x0A0 + public readonly uint allocationFile_extents_startBlock4; + /// 0x0A4 + public readonly uint allocationFile_extents_blockCount4; + /// 0x0A8 + public readonly uint allocationFile_extents_startBlock5; + /// 0x0AC + public readonly uint allocationFile_extents_blockCount5; + /// 0x0B0 + public readonly uint allocationFile_extents_startBlock6; + /// 0x0B4 + public readonly uint allocationFile_extents_blockCount6; + /// 0x0B8 + public readonly uint allocationFile_extents_startBlock7; + /// 0x0BC + public readonly uint allocationFile_extents_blockCount7; + + // HFSPlusForkData extentsFile; + /// 0x0C0 + public readonly ulong extentsFile_logicalSize; + /// 0x0C8 + public readonly uint extentsFile_clumpSize; + /// 0x0CC + public readonly uint extentsFile_totalBlocks; + /// 0x0D0 + public readonly uint extentsFile_extents_startBlock0; + /// 0x0D4 + public readonly uint extentsFile_extents_blockCount0; + /// 0x0D8 + public readonly uint extentsFile_extents_startBlock1; + /// 0x0DC + public readonly uint extentsFile_extents_blockCount1; + /// 0x0E0 + public readonly uint extentsFile_extents_startBlock2; + /// 0x0E4 + public readonly uint extentsFile_extents_blockCount2; + /// 0x0E8 + public readonly uint extentsFile_extents_startBlock3; + /// 0x0EC + public readonly uint extentsFile_extents_blockCount3; + /// 0x0F0 + public readonly uint extentsFile_extents_startBlock4; + /// 0x0F4 + public readonly uint extentsFile_extents_blockCount4; + /// 0x0F8 + public readonly uint extentsFile_extents_startBlock5; + /// 0x0FC + public readonly uint extentsFile_extents_blockCount5; + /// 0x100 + public readonly uint extentsFile_extents_startBlock6; + /// 0x104 + public readonly uint extentsFile_extents_blockCount6; + /// 0x108 + public readonly uint extentsFile_extents_startBlock7; + /// 0x10C + public readonly uint extentsFile_extents_blockCount7; + + // HFSPlusForkData catalogFile; + /// 0x110 + public readonly ulong catalogFile_logicalSize; + /// 0x118 + public readonly uint catalogFile_clumpSize; + /// 0x11C + public readonly uint catalogFile_totalBlocks; + /// 0x120 + public readonly uint catalogFile_extents_startBlock0; + /// 0x124 + public readonly uint catalogFile_extents_blockCount0; + /// 0x128 + public readonly uint catalogFile_extents_startBlock1; + /// 0x12C + public readonly uint catalogFile_extents_blockCount1; + /// 0x130 + public readonly uint catalogFile_extents_startBlock2; + /// 0x134 + public readonly uint catalogFile_extents_blockCount2; + /// 0x138 + public readonly uint catalogFile_extents_startBlock3; + /// 0x13C + public readonly uint catalogFile_extents_blockCount3; + /// 0x140 + public readonly uint catalogFile_extents_startBlock4; + /// 0x144 + public readonly uint catalogFile_extents_blockCount4; + /// 0x148 + public readonly uint catalogFile_extents_startBlock5; + /// 0x14C + public readonly uint catalogFile_extents_blockCount5; + /// 0x150 + public readonly uint catalogFile_extents_startBlock6; + /// 0x154 + public readonly uint catalogFile_extents_blockCount6; + /// 0x158 + public readonly uint catalogFile_extents_startBlock7; + /// 0x15C + public readonly uint catalogFile_extents_blockCount7; + + // HFSPlusForkData attributesFile; + /// 0x160 + public readonly ulong attributesFile_logicalSize; + /// 0x168 + public readonly uint attributesFile_clumpSize; + /// 0x16C + public readonly uint attributesFile_totalBlocks; + /// 0x170 + public readonly uint attributesFile_extents_startBlock0; + /// 0x174 + public readonly uint attributesFile_extents_blockCount0; + /// 0x178 + public readonly uint attributesFile_extents_startBlock1; + /// 0x17C + public readonly uint attributesFile_extents_blockCount1; + /// 0x180 + public readonly uint attributesFile_extents_startBlock2; + /// 0x184 + public readonly uint attributesFile_extents_blockCount2; + /// 0x188 + public readonly uint attributesFile_extents_startBlock3; + /// 0x18C + public readonly uint attributesFile_extents_blockCount3; + /// 0x190 + public readonly uint attributesFile_extents_startBlock4; + /// 0x194 + public readonly uint attributesFile_extents_blockCount4; + /// 0x198 + public readonly uint attributesFile_extents_startBlock5; + /// 0x19C + public readonly uint attributesFile_extents_blockCount5; + /// 0x1A0 + public readonly uint attributesFile_extents_startBlock6; + /// 0x1A4 + public readonly uint attributesFile_extents_blockCount6; + /// 0x1A8 + public readonly uint attributesFile_extents_startBlock7; + /// 0x1AC + public readonly uint attributesFile_extents_blockCount7; + + // HFSPlusForkData startupFile; + /// 0x1B0 + public readonly ulong startupFile_logicalSize; + /// 0x1B8 + public readonly uint startupFile_clumpSize; + /// 0x1BC + public readonly uint startupFile_totalBlocks; + /// 0x1C0 + public readonly uint startupFile_extents_startBlock0; + /// 0x1C4 + public readonly uint startupFile_extents_blockCount0; + /// 0x1C8 + public readonly uint startupFile_extents_startBlock1; + /// 0x1D0 + public readonly uint startupFile_extents_blockCount1; + /// 0x1D4 + public readonly uint startupFile_extents_startBlock2; + /// 0x1D8 + public readonly uint startupFile_extents_blockCount2; + /// 0x1DC + public readonly uint startupFile_extents_startBlock3; + /// 0x1E0 + public readonly uint startupFile_extents_blockCount3; + /// 0x1E4 + public readonly uint startupFile_extents_startBlock4; + /// 0x1E8 + public readonly uint startupFile_extents_blockCount4; + /// 0x1EC + public readonly uint startupFile_extents_startBlock5; + /// 0x1F0 + public readonly uint startupFile_extents_blockCount5; + /// 0x1F4 + public readonly uint startupFile_extents_startBlock6; + /// 0x1F8 + public readonly uint startupFile_extents_blockCount6; + /// 0x1FC + public readonly uint startupFile_extents_startBlock7; + /// 0x200 + public readonly uint startupFile_extents_blockCount7; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleMFS/AppleMFS.cs b/Aaru.Filesystems/AppleMFS/AppleMFS.cs index 537bcfda5..efe4eda47 100644 --- a/Aaru.Filesystems/AppleMFS/AppleMFS.cs +++ b/Aaru.Filesystems/AppleMFS/AppleMFS.cs @@ -36,57 +36,56 @@ using System.Text; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh Volume II +/// +/// Implements the Apple Macintosh File System +public sealed partial class AppleMFS : IReadOnlyFilesystem { - // Information from Inside Macintosh Volume II + bool _mounted; + bool _debug; + IMediaImage _device; + ulong _partitionStart; + Dictionary _idToFilename; + Dictionary _idToEntry; + Dictionary _filenameToId; + MasterDirectoryBlock _volMdb; + byte[] _bootBlocks; + byte[] _mdbBlocks; + byte[] _directoryBlocks; + byte[] _blockMapBytes; + uint[] _blockMap; + int _sectorsPerBlock; + byte[] _bootTags; + byte[] _mdbTags; + byte[] _directoryTags; + byte[] _bitmapTags; + /// - /// Implements the Apple Macintosh File System - public sealed partial class AppleMFS : IReadOnlyFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public string Name => "Apple Macintosh File System"; + /// + public Guid Id => new("36405F8D-0D26-4066-6538-5DBF5D065C3A"); + /// + public Encoding Encoding { get; private set; } + /// + public string Author => "Natalia Portillo"; + + // TODO: Implement Finder namespace (requires decoding Desktop database) + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + {}; + + /// + public Dictionary Namespaces => null; + + static Dictionary GetDefaultOptions() => new() { - bool _mounted; - bool _debug; - IMediaImage _device; - ulong _partitionStart; - Dictionary _idToFilename; - Dictionary _idToEntry; - Dictionary _filenameToId; - MasterDirectoryBlock _volMdb; - byte[] _bootBlocks; - byte[] _mdbBlocks; - byte[] _directoryBlocks; - byte[] _blockMapBytes; - uint[] _blockMap; - int _sectorsPerBlock; - byte[] _bootTags; - byte[] _mdbTags; - byte[] _directoryTags; - byte[] _bitmapTags; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Name => "Apple Macintosh File System"; - /// - public Guid Id => new("36405F8D-0D26-4066-6538-5DBF5D065C3A"); - /// - public Encoding Encoding { get; private set; } - /// - public string Author => "Natalia Portillo"; - - // TODO: Implement Finder namespace (requires decoding Desktop database) - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - {}; - - /// - public Dictionary Namespaces => null; - - static Dictionary GetDefaultOptions() => new() { - { - "debug", false.ToString() - } - }; - } + "debug", false.ToString() + } + }; } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleMFS/Consts.cs b/Aaru.Filesystems/AppleMFS/Consts.cs index 5ea118282..c796b7431 100644 --- a/Aaru.Filesystems/AppleMFS/Consts.cs +++ b/Aaru.Filesystems/AppleMFS/Consts.cs @@ -32,21 +32,20 @@ using System.Diagnostics.CodeAnalysis; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh Volume II +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class AppleMFS { - // Information from Inside Macintosh Volume II - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class AppleMFS - { - const ushort MFS_MAGIC = 0xD2D7; + const ushort MFS_MAGIC = 0xD2D7; - const short DIRID_TRASH = -3; - const short DIRID_DESKTOP = -2; - const short DIRID_TEMPLATE = -1; - const short DIRID_ROOT = 0; + const short DIRID_TRASH = -3; + const short DIRID_DESKTOP = -2; + const short DIRID_TEMPLATE = -1; + const short DIRID_ROOT = 0; - const int BMAP_FREE = 0; - const int BMAP_LAST = 1; - const int BMAP_DIR = 0xFFF; - } + const int BMAP_FREE = 0; + const int BMAP_LAST = 1; + const int BMAP_DIR = 0xFFF; } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleMFS/Dir.cs b/Aaru.Filesystems/AppleMFS/Dir.cs index 9b84acf43..ef26add9f 100644 --- a/Aaru.Filesystems/AppleMFS/Dir.cs +++ b/Aaru.Filesystems/AppleMFS/Dir.cs @@ -37,121 +37,120 @@ using Aaru.CommonTypes.Enums; using Aaru.Console; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh Volume II +public sealed partial class AppleMFS { - // Information from Inside Macintosh Volume II - public sealed partial class AppleMFS + /// + public ErrorNumber ReadDir(string path, out List contents) { - /// - public ErrorNumber ReadDir(string path, out List contents) + contents = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(!string.IsNullOrEmpty(path) && + string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) + return ErrorNumber.NotSupported; + + contents = _idToFilename.Select(kvp => kvp.Value).ToList(); + + if(_debug) { - contents = null; + contents.Add("$"); + contents.Add("$Bitmap"); + contents.Add("$MDB"); - if(!_mounted) - return ErrorNumber.AccessDenied; - - if(!string.IsNullOrEmpty(path) && - string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) - return ErrorNumber.NotSupported; - - contents = _idToFilename.Select(kvp => kvp.Value).ToList(); - - if(_debug) - { - contents.Add("$"); - contents.Add("$Bitmap"); - contents.Add("$MDB"); - - if(_bootBlocks != null) - contents.Add("$Boot"); - } - - contents.Sort(); - - return ErrorNumber.NoError; + if(_bootBlocks != null) + contents.Add("$Boot"); } - bool FillDirectory() + contents.Sort(); + + return ErrorNumber.NoError; + } + + bool FillDirectory() + { + _idToFilename = new Dictionary(); + _idToEntry = new Dictionary(); + _filenameToId = new Dictionary(); + + int offset = 0; + + while(offset + 51 < _directoryBlocks.Length) { - _idToFilename = new Dictionary(); - _idToEntry = new Dictionary(); - _filenameToId = new Dictionary(); - - int offset = 0; - - while(offset + 51 < _directoryBlocks.Length) + var entry = new FileEntry { - var entry = new FileEntry - { - flFlags = (FileFlags)_directoryBlocks[offset + 0] - }; + flFlags = (FileFlags)_directoryBlocks[offset + 0] + }; - if(!entry.flFlags.HasFlag(FileFlags.Used)) - break; + if(!entry.flFlags.HasFlag(FileFlags.Used)) + break; - entry.flTyp = _directoryBlocks[offset + 1]; + entry.flTyp = _directoryBlocks[offset + 1]; - entry.flUsrWds = - Marshal.ByteArrayToStructureBigEndian(_directoryBlocks, offset + 2, 16); + entry.flUsrWds = + Marshal.ByteArrayToStructureBigEndian(_directoryBlocks, offset + 2, 16); - entry.flFlNum = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 18); - entry.flStBlk = BigEndianBitConverter.ToUInt16(_directoryBlocks, offset + 22); - entry.flLgLen = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 24); - entry.flPyLen = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 28); - entry.flRStBlk = BigEndianBitConverter.ToUInt16(_directoryBlocks, offset + 32); - entry.flRLgLen = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 34); - entry.flRPyLen = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 38); - entry.flCrDat = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 42); - entry.flMdDat = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 46); - entry.flNam = new byte[_directoryBlocks[offset + 50] + 1]; - Array.Copy(_directoryBlocks, offset + 50, entry.flNam, 0, entry.flNam.Length); + entry.flFlNum = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 18); + entry.flStBlk = BigEndianBitConverter.ToUInt16(_directoryBlocks, offset + 22); + entry.flLgLen = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 24); + entry.flPyLen = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 28); + entry.flRStBlk = BigEndianBitConverter.ToUInt16(_directoryBlocks, offset + 32); + entry.flRLgLen = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 34); + entry.flRPyLen = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 38); + entry.flCrDat = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 42); + entry.flMdDat = BigEndianBitConverter.ToUInt32(_directoryBlocks, offset + 46); + entry.flNam = new byte[_directoryBlocks[offset + 50] + 1]; + Array.Copy(_directoryBlocks, offset + 50, entry.flNam, 0, entry.flNam.Length); - string lowerFilename = StringHandlers.PascalToString(entry.flNam, Encoding).ToLowerInvariant(). - Replace('/', ':'); + string lowerFilename = StringHandlers.PascalToString(entry.flNam, Encoding).ToLowerInvariant(). + Replace('/', ':'); - if(entry.flFlags.HasFlag(FileFlags.Used) && - !_idToFilename.ContainsKey(entry.flFlNum) && - !_idToEntry.ContainsKey(entry.flFlNum) && - !_filenameToId.ContainsKey(lowerFilename) && - entry.flFlNum > 0) - { - _idToEntry.Add(entry.flFlNum, entry); + if(entry.flFlags.HasFlag(FileFlags.Used) && + !_idToFilename.ContainsKey(entry.flFlNum) && + !_idToEntry.ContainsKey(entry.flFlNum) && + !_filenameToId.ContainsKey(lowerFilename) && + entry.flFlNum > 0) + { + _idToEntry.Add(entry.flFlNum, entry); - _idToFilename.Add(entry.flFlNum, - StringHandlers.PascalToString(entry.flNam, Encoding).Replace('/', ':')); + _idToFilename.Add(entry.flFlNum, + StringHandlers.PascalToString(entry.flNam, Encoding).Replace('/', ':')); - _filenameToId.Add(lowerFilename, entry.flFlNum); + _filenameToId.Add(lowerFilename, entry.flFlNum); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlags = {0}", entry.flFlags); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flTyp = {0}", entry.flTyp); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlNum = {0}", entry.flFlNum); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flStBlk = {0}", entry.flStBlk); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flLgLen = {0}", entry.flLgLen); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flPyLen = {0}", entry.flPyLen); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRStBlk = {0}", entry.flRStBlk); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRLgLen = {0}", entry.flRLgLen); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRPyLen = {0}", entry.flRPyLen); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlags = {0}", entry.flFlags); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flTyp = {0}", entry.flTyp); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flFlNum = {0}", entry.flFlNum); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flStBlk = {0}", entry.flStBlk); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flLgLen = {0}", entry.flLgLen); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flPyLen = {0}", entry.flPyLen); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRStBlk = {0}", entry.flRStBlk); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRLgLen = {0}", entry.flRLgLen); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flRPyLen = {0}", entry.flRPyLen); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flCrDat = {0}", - DateHandlers.MacToDateTime(entry.flCrDat)); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flCrDat = {0}", + DateHandlers.MacToDateTime(entry.flCrDat)); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flMdDat = {0}", - DateHandlers.MacToDateTime(entry.flMdDat)); + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flMdDat = {0}", + DateHandlers.MacToDateTime(entry.flMdDat)); - AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flNam0 = {0}", - StringHandlers.PascalToString(entry.flNam, Encoding)); - } - - offset += 50 + entry.flNam.Length; - - // "Entries are always an integral number of words" - if(offset % 2 != 0) - offset++; - - // TODO: "Entries don't cross logical block boundaries" + AaruConsole.DebugWriteLine("DEBUG (AppleMFS plugin)", "entry.flNam0 = {0}", + StringHandlers.PascalToString(entry.flNam, Encoding)); } - return true; + offset += 50 + entry.flNam.Length; + + // "Entries are always an integral number of words" + if(offset % 2 != 0) + offset++; + + // TODO: "Entries don't cross logical block boundaries" } + + return true; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleMFS/File.cs b/Aaru.Filesystems/AppleMFS/File.cs index f0e899201..f599449ed 100644 --- a/Aaru.Filesystems/AppleMFS/File.cs +++ b/Aaru.Filesystems/AppleMFS/File.cs @@ -38,371 +38,370 @@ using Aaru.Console; using Aaru.Helpers; using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh Volume II +public sealed partial class AppleMFS { - // Information from Inside Macintosh Volume II - public sealed partial class AppleMFS + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) { - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + deviceBlock = new long(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - deviceBlock = new long(); + '/' + }, StringSplitOptions.RemoveEmptyEntries); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - string[] pathElements = path.Split(new[] + path = pathElements[0]; + + if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) + return ErrorNumber.NoSuchFile; + + if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) + return ErrorNumber.NoSuchFile; + + if(fileBlock > entry.flPyLen / _volMdb.drAlBlkSiz) + return ErrorNumber.InvalidArgument; + + uint nextBlock = entry.flStBlk; + long relBlock = 0; + + while(true) + { + if(relBlock == fileBlock) { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + deviceBlock = ((nextBlock - 2) * _sectorsPerBlock) + _volMdb.drAlBlSt + (long)_partitionStart; - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - path = pathElements[0]; - - if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) - return ErrorNumber.NoSuchFile; - - if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) - return ErrorNumber.NoSuchFile; - - if(fileBlock > entry.flPyLen / _volMdb.drAlBlkSiz) - return ErrorNumber.InvalidArgument; - - uint nextBlock = entry.flStBlk; - long relBlock = 0; - - while(true) - { - if(relBlock == fileBlock) - { - deviceBlock = ((nextBlock - 2) * _sectorsPerBlock) + _volMdb.drAlBlSt + (long)_partitionStart; - - return ErrorNumber.NoError; - } - - if(_blockMap[nextBlock] == BMAP_FREE || - _blockMap[nextBlock] == BMAP_LAST) - break; - - nextBlock = _blockMap[nextBlock]; - relBlock++; + return ErrorNumber.NoError; } - return ErrorNumber.InOutError; + if(_blockMap[nextBlock] == BMAP_FREE || + _blockMap[nextBlock] == BMAP_LAST) + break; + + nextBlock = _blockMap[nextBlock]; + relBlock++; } - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + return ErrorNumber.InOutError; + } + + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + { + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - attributes = new FileAttributes(); + '/' + }, StringSplitOptions.RemoveEmptyEntries); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + path = pathElements[0]; - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) + return ErrorNumber.NoSuchFile; - path = pathElements[0]; + if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) + return ErrorNumber.NoSuchFile; - if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) - return ErrorNumber.NoSuchFile; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsAlias)) + attributes |= FileAttributes.Alias; - if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) - return ErrorNumber.NoSuchFile; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBundle)) + attributes |= FileAttributes.Bundle; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsAlias)) - attributes |= FileAttributes.Alias; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBeenInited)) + attributes |= FileAttributes.HasBeenInited; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBundle)) - attributes |= FileAttributes.Bundle; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasCustomIcon)) + attributes |= FileAttributes.HasCustomIcon; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBeenInited)) - attributes |= FileAttributes.HasBeenInited; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasNoINITs)) + attributes |= FileAttributes.HasNoINITs; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasCustomIcon)) - attributes |= FileAttributes.HasCustomIcon; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsInvisible)) + attributes |= FileAttributes.Hidden; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasNoINITs)) - attributes |= FileAttributes.HasNoINITs; + if(entry.flFlags.HasFlag(FileFlags.Locked)) + attributes |= FileAttributes.Immutable; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsInvisible)) - attributes |= FileAttributes.Hidden; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsOnDesk)) + attributes |= FileAttributes.IsOnDesk; - if(entry.flFlags.HasFlag(FileFlags.Locked)) - attributes |= FileAttributes.Immutable; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsShared)) + attributes |= FileAttributes.Shared; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsOnDesk)) - attributes |= FileAttributes.IsOnDesk; + if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsStationery)) + attributes |= FileAttributes.Stationery; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsShared)) - attributes |= FileAttributes.Shared; + if(!attributes.HasFlag(FileAttributes.Alias) && + !attributes.HasFlag(FileAttributes.Bundle) && + !attributes.HasFlag(FileAttributes.Stationery)) + attributes |= FileAttributes.File; - if(entry.flUsrWds.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsStationery)) - attributes |= FileAttributes.Stationery; + attributes |= FileAttributes.BlockUnits; - if(!attributes.HasFlag(FileAttributes.Alias) && - !attributes.HasFlag(FileAttributes.Bundle) && - !attributes.HasFlag(FileAttributes.Stationery)) - attributes |= FileAttributes.File; + return ErrorNumber.NoError; + } - attributes |= FileAttributes.BlockUnits; + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + byte[] file; + ErrorNumber error = ErrorNumber.NoError; + + if(_debug && string.Compare(path, "$", StringComparison.InvariantCulture) == 0) + file = _directoryBlocks; + else if(_debug && + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 && + _bootBlocks != null) + file = _bootBlocks; + else if(_debug && string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0) + file = _blockMapBytes; + else if(_debug && string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) + file = _mdbBlocks; + else + error = ReadFile(path, out file, false, false); + + if(error != ErrorNumber.NoError) + return error; + + if(size == 0) + { + buf = Array.Empty(); return ErrorNumber.NoError; } - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + if(offset >= file.Length) + return ErrorNumber.InvalidArgument; + + if(size + offset >= file.Length) + size = file.Length - offset; + + buf = new byte[size]; + + Array.Copy(file, offset, buf, 0, size); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - if(!_mounted) - return ErrorNumber.AccessDenied; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - byte[] file; - ErrorNumber error = ErrorNumber.NoError; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - if(_debug && string.Compare(path, "$", StringComparison.InvariantCulture) == 0) - file = _directoryBlocks; - else if(_debug && - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 && - _bootBlocks != null) - file = _bootBlocks; - else if(_debug && string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0) - file = _blockMapBytes; - else if(_debug && string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) - file = _mdbBlocks; - else - error = ReadFile(path, out file, false, false); + path = pathElements[0]; - if(error != ErrorNumber.NoError) - return error; + if(_debug) + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) + { + stat = new FileEntryInfo + { + BlockSize = _device.Info.SectorSize, + Inode = 0, + Links = 1, + Attributes = FileAttributes.System + }; - if(size == 0) + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) + { + stat.Blocks = (_directoryBlocks.Length / stat.BlockSize) + + (_directoryBlocks.Length % stat.BlockSize); + + stat.Length = _directoryBlocks.Length; + } + else if(string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0) + { + stat.Blocks = (_blockMapBytes.Length / stat.BlockSize) + + (_blockMapBytes.Length % stat.BlockSize); + + stat.Length = _blockMapBytes.Length; + } + else if(string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 && + _bootBlocks != null) + { + stat.Blocks = (_bootBlocks.Length / stat.BlockSize) + (_bootBlocks.Length % stat.BlockSize); + stat.Length = _bootBlocks.Length; + } + else if(string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) + { + stat.Blocks = (_mdbBlocks.Length / stat.BlockSize) + (_mdbBlocks.Length % stat.BlockSize); + stat.Length = _mdbBlocks.Length; + } + else + return ErrorNumber.InvalidArgument; + + return ErrorNumber.NoError; + } + + if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) + return ErrorNumber.NoSuchFile; + + if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) + return ErrorNumber.NoSuchFile; + + ErrorNumber error = GetAttributes(path, out FileAttributes attr); + + if(error != ErrorNumber.NoError) + return error; + + stat = new FileEntryInfo + { + Attributes = attr, + Blocks = entry.flLgLen / _volMdb.drAlBlkSiz, + BlockSize = _volMdb.drAlBlkSiz, + CreationTime = DateHandlers.MacToDateTime(entry.flCrDat), + Inode = entry.flFlNum, + LastWriteTime = DateHandlers.MacToDateTime(entry.flMdDat), + Length = entry.flPyLen, + Links = 1 + }; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber ReadLink(string path, out string dest) + { + dest = null; + + return ErrorNumber.NotImplemented; + } + + ErrorNumber ReadFile(string path, out byte[] buf, bool resourceFork, bool tags) + { + buf = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; + + path = pathElements[0]; + + if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) + return ErrorNumber.NoSuchFile; + + if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) + return ErrorNumber.NoSuchFile; + + uint nextBlock; + + if(resourceFork) + { + if(entry.flRPyLen == 0) { buf = Array.Empty(); return ErrorNumber.NoError; } - if(offset >= file.Length) - return ErrorNumber.InvalidArgument; + nextBlock = entry.flRStBlk; + } + else + { + if(entry.flPyLen == 0) + { + buf = Array.Empty(); - if(size + offset >= file.Length) - size = file.Length - offset; + return ErrorNumber.NoError; + } - buf = new byte[size]; - - Array.Copy(file, offset, buf, 0, size); - - return ErrorNumber.NoError; + nextBlock = entry.flStBlk; } - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) + var ms = new MemoryStream(); + + do { - stat = null; + byte[] sectors; + ErrorNumber errno; - if(!_mounted) - return ErrorNumber.AccessDenied; + errno = + tags + ? _device. + ReadSectorsTag((ulong)((nextBlock - 2) * _sectorsPerBlock) + _volMdb.drAlBlSt + _partitionStart, + (uint)_sectorsPerBlock, SectorTagType.AppleSectorTag, out sectors) + : _device. + ReadSectors((ulong)((nextBlock - 2) * _sectorsPerBlock) + _volMdb.drAlBlSt + _partitionStart, + (uint)_sectorsPerBlock, out sectors); - string[] pathElements = path.Split(new[] + if(errno != ErrorNumber.NoError) + return errno; + + ms.Write(sectors, 0, sectors.Length); + + if(_blockMap[nextBlock] == BMAP_FREE) { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + AaruConsole.ErrorWriteLine("File truncated at block {0}", nextBlock); - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + break; + } - path = pathElements[0]; + nextBlock = _blockMap[nextBlock]; + } while(nextBlock > BMAP_LAST); - if(_debug) - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) - { - stat = new FileEntryInfo - { - BlockSize = _device.Info.SectorSize, - Inode = 0, - Links = 1, - Attributes = FileAttributes.System - }; - - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) - { - stat.Blocks = (_directoryBlocks.Length / stat.BlockSize) + - (_directoryBlocks.Length % stat.BlockSize); - - stat.Length = _directoryBlocks.Length; - } - else if(string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0) - { - stat.Blocks = (_blockMapBytes.Length / stat.BlockSize) + - (_blockMapBytes.Length % stat.BlockSize); - - stat.Length = _blockMapBytes.Length; - } - else if(string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 && - _bootBlocks != null) - { - stat.Blocks = (_bootBlocks.Length / stat.BlockSize) + (_bootBlocks.Length % stat.BlockSize); - stat.Length = _bootBlocks.Length; - } - else if(string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) - { - stat.Blocks = (_mdbBlocks.Length / stat.BlockSize) + (_mdbBlocks.Length % stat.BlockSize); - stat.Length = _mdbBlocks.Length; - } - else - return ErrorNumber.InvalidArgument; - - return ErrorNumber.NoError; - } - - if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) - return ErrorNumber.NoSuchFile; - - if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) - return ErrorNumber.NoSuchFile; - - ErrorNumber error = GetAttributes(path, out FileAttributes attr); - - if(error != ErrorNumber.NoError) - return error; - - stat = new FileEntryInfo - { - Attributes = attr, - Blocks = entry.flLgLen / _volMdb.drAlBlkSiz, - BlockSize = _volMdb.drAlBlkSiz, - CreationTime = DateHandlers.MacToDateTime(entry.flCrDat), - Inode = entry.flFlNum, - LastWriteTime = DateHandlers.MacToDateTime(entry.flMdDat), - Length = entry.flPyLen, - Links = 1 - }; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber ReadLink(string path, out string dest) + if(tags) + buf = ms.ToArray(); + else { - dest = null; - - return ErrorNumber.NotImplemented; - } - - ErrorNumber ReadFile(string path, out byte[] buf, bool resourceFork, bool tags) - { - buf = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - path = pathElements[0]; - - if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) - return ErrorNumber.NoSuchFile; - - if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) - return ErrorNumber.NoSuchFile; - - uint nextBlock; - if(resourceFork) - { - if(entry.flRPyLen == 0) - { - buf = Array.Empty(); - - return ErrorNumber.NoError; - } - - nextBlock = entry.flRStBlk; - } - else - { - if(entry.flPyLen == 0) - { - buf = Array.Empty(); - - return ErrorNumber.NoError; - } - - nextBlock = entry.flStBlk; - } - - var ms = new MemoryStream(); - - do - { - byte[] sectors; - ErrorNumber errno; - - errno = - tags - ? _device. - ReadSectorsTag((ulong)((nextBlock - 2) * _sectorsPerBlock) + _volMdb.drAlBlSt + _partitionStart, - (uint)_sectorsPerBlock, SectorTagType.AppleSectorTag, out sectors) - : _device. - ReadSectors((ulong)((nextBlock - 2) * _sectorsPerBlock) + _volMdb.drAlBlSt + _partitionStart, - (uint)_sectorsPerBlock, out sectors); - - if(errno != ErrorNumber.NoError) - return errno; - - ms.Write(sectors, 0, sectors.Length); - - if(_blockMap[nextBlock] == BMAP_FREE) - { - AaruConsole.ErrorWriteLine("File truncated at block {0}", nextBlock); - - break; - } - - nextBlock = _blockMap[nextBlock]; - } while(nextBlock > BMAP_LAST); - - if(tags) - buf = ms.ToArray(); - else - { - if(resourceFork) - if(ms.Length < entry.flRLgLen) - buf = ms.ToArray(); - else - { - buf = new byte[entry.flRLgLen]; - Array.Copy(ms.ToArray(), 0, buf, 0, buf.Length); - } + if(ms.Length < entry.flRLgLen) + buf = ms.ToArray(); else { - if(ms.Length < entry.flLgLen) - buf = ms.ToArray(); - else - { - buf = new byte[entry.flLgLen]; - Array.Copy(ms.ToArray(), 0, buf, 0, buf.Length); - } + buf = new byte[entry.flRLgLen]; + Array.Copy(ms.ToArray(), 0, buf, 0, buf.Length); + } + else + { + if(ms.Length < entry.flLgLen) + buf = ms.ToArray(); + else + { + buf = new byte[entry.flLgLen]; + Array.Copy(ms.ToArray(), 0, buf, 0, buf.Length); } } - - return ErrorNumber.NoError; } + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleMFS/Info.cs b/Aaru.Filesystems/AppleMFS/Info.cs index 1fbd6b910..92e60ca44 100644 --- a/Aaru.Filesystems/AppleMFS/Info.cs +++ b/Aaru.Filesystems/AppleMFS/Info.cs @@ -40,148 +40,147 @@ using Claunia.Encoding; using Schemas; using Encoding = System.Text.Encoding; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh Volume II +public sealed partial class AppleMFS { - // Information from Inside Macintosh Volume II - public sealed partial class AppleMFS + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(2 + partition.Start >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSector(2 + partition.Start, out byte[] mdbSector); + + if(errno != ErrorNumber.NoError) + return false; + + ushort drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000); + + return drSigWord == MFS_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? new MacRoman(); + information = ""; + + var sb = new StringBuilder(); + + var mdb = new MasterDirectoryBlock(); + + ErrorNumber errno = imagePlugin.ReadSector(2 + partition.Start, out byte[] mdbSector); + + if(errno != ErrorNumber.NoError) + return; + + errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bbSector); + + if(errno != ErrorNumber.NoError) + return; + + mdb.drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000); + + if(mdb.drSigWord != MFS_MAGIC) + return; + + mdb.drCrDate = BigEndianBitConverter.ToUInt32(mdbSector, 0x002); + mdb.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbSector, 0x006); + mdb.drAtrb = (AppleCommon.VolumeAttributes)BigEndianBitConverter.ToUInt16(mdbSector, 0x00A); + mdb.drNmFls = BigEndianBitConverter.ToUInt16(mdbSector, 0x00C); + mdb.drDirSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x00E); + mdb.drBlLen = BigEndianBitConverter.ToUInt16(mdbSector, 0x010); + mdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbSector, 0x012); + mdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x014); + mdb.drClpSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x018); + mdb.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x01C); + mdb.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbSector, 0x01E); + mdb.drFreeBks = BigEndianBitConverter.ToUInt16(mdbSector, 0x022); + mdb.drVNSiz = mdbSector[0x024]; + byte[] variableSize = new byte[mdb.drVNSiz + 1]; + Array.Copy(mdbSector, 0x024, variableSize, 0, mdb.drVNSiz + 1); + mdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); + + sb.AppendLine("Apple Macintosh File System"); + sb.AppendLine(); + sb.AppendLine("Master Directory Block:"); + sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); + sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drLsBkUp)).AppendLine(); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) + sb.AppendLine("Volume is locked by hardware."); + + sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." + : "Volume is mounted."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) + sb.AppendLine("Volume has spared bad blocks."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) + sb.AppendLine("Volume does not need cache."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) + sb.AppendLine("Boot volume is inconsistent."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) + sb.AppendLine("There are reused CNIDs."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) + sb.AppendLine("Volume is seriously inconsistent."); + + if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) + sb.AppendLine("Volume is locked by software."); + + sb.AppendFormat("{0} files on volume", mdb.drNmFls).AppendLine(); + sb.AppendFormat("First directory sector: {0}", mdb.drDirSt).AppendLine(); + sb.AppendFormat("{0} sectors in directory.", mdb.drBlLen).AppendLine(); + sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks + 1).AppendLine(); + sb.AppendFormat("Size of allocation blocks: {0} bytes", mdb.drAlBlkSiz).AppendLine(); + sb.AppendFormat("{0} bytes to allocate.", mdb.drClpSiz).AppendLine(); + sb.AppendFormat("First allocation block (#2) starts in sector {0}.", mdb.drAlBlSt).AppendLine(); + sb.AppendFormat("Next unused file number: {0}", mdb.drNxtFNum).AppendLine(); + sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); + sb.AppendFormat("Volume name: {0}", mdb.drVN).AppendLine(); + + string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); + + if(bootBlockInfo != null) { - if(2 + partition.Start >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(2 + partition.Start, out byte[] mdbSector); - - if(errno != ErrorNumber.NoError) - return false; - - ushort drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000); - - return drSigWord == MFS_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? new MacRoman(); - information = ""; - - var sb = new StringBuilder(); - - var mdb = new MasterDirectoryBlock(); - - ErrorNumber errno = imagePlugin.ReadSector(2 + partition.Start, out byte[] mdbSector); - - if(errno != ErrorNumber.NoError) - return; - - errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bbSector); - - if(errno != ErrorNumber.NoError) - return; - - mdb.drSigWord = BigEndianBitConverter.ToUInt16(mdbSector, 0x000); - - if(mdb.drSigWord != MFS_MAGIC) - return; - - mdb.drCrDate = BigEndianBitConverter.ToUInt32(mdbSector, 0x002); - mdb.drLsBkUp = BigEndianBitConverter.ToUInt32(mdbSector, 0x006); - mdb.drAtrb = (AppleCommon.VolumeAttributes)BigEndianBitConverter.ToUInt16(mdbSector, 0x00A); - mdb.drNmFls = BigEndianBitConverter.ToUInt16(mdbSector, 0x00C); - mdb.drDirSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x00E); - mdb.drBlLen = BigEndianBitConverter.ToUInt16(mdbSector, 0x010); - mdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(mdbSector, 0x012); - mdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x014); - mdb.drClpSiz = BigEndianBitConverter.ToUInt32(mdbSector, 0x018); - mdb.drAlBlSt = BigEndianBitConverter.ToUInt16(mdbSector, 0x01C); - mdb.drNxtFNum = BigEndianBitConverter.ToUInt32(mdbSector, 0x01E); - mdb.drFreeBks = BigEndianBitConverter.ToUInt16(mdbSector, 0x022); - mdb.drVNSiz = mdbSector[0x024]; - byte[] variableSize = new byte[mdb.drVNSiz + 1]; - Array.Copy(mdbSector, 0x024, variableSize, 0, mdb.drVNSiz + 1); - mdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); - - sb.AppendLine("Apple Macintosh File System"); + sb.AppendLine("Volume is bootable."); sb.AppendLine(); - sb.AppendLine("Master Directory Block:"); - sb.AppendFormat("Creation date: {0}", DateHandlers.MacToDateTime(mdb.drCrDate)).AppendLine(); - sb.AppendFormat("Last backup date: {0}", DateHandlers.MacToDateTime(mdb.drLsBkUp)).AppendLine(); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.HardwareLock)) - sb.AppendLine("Volume is locked by hardware."); - - sb.AppendLine(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Unmounted) ? "Volume was unmonted." - : "Volume is mounted."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SparedBadBlocks)) - sb.AppendLine("Volume has spared bad blocks."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.DoesNotNeedCache)) - sb.AppendLine("Volume does not need cache."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.BootInconsistent)) - sb.AppendLine("Boot volume is inconsistent."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.ReusedIds)) - sb.AppendLine("There are reused CNIDs."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.Inconsistent)) - sb.AppendLine("Volume is seriously inconsistent."); - - if(mdb.drAtrb.HasFlag(AppleCommon.VolumeAttributes.SoftwareLock)) - sb.AppendLine("Volume is locked by software."); - - sb.AppendFormat("{0} files on volume", mdb.drNmFls).AppendLine(); - sb.AppendFormat("First directory sector: {0}", mdb.drDirSt).AppendLine(); - sb.AppendFormat("{0} sectors in directory.", mdb.drBlLen).AppendLine(); - sb.AppendFormat("{0} volume allocation blocks.", mdb.drNmAlBlks + 1).AppendLine(); - sb.AppendFormat("Size of allocation blocks: {0} bytes", mdb.drAlBlkSiz).AppendLine(); - sb.AppendFormat("{0} bytes to allocate.", mdb.drClpSiz).AppendLine(); - sb.AppendFormat("First allocation block (#2) starts in sector {0}.", mdb.drAlBlSt).AppendLine(); - sb.AppendFormat("Next unused file number: {0}", mdb.drNxtFNum).AppendLine(); - sb.AppendFormat("{0} unused allocation blocks.", mdb.drFreeBks).AppendLine(); - sb.AppendFormat("Volume name: {0}", mdb.drVN).AppendLine(); - - string bootBlockInfo = AppleCommon.GetBootBlockInformation(bbSector, Encoding); - - if(bootBlockInfo != null) - { - sb.AppendLine("Volume is bootable."); - sb.AppendLine(); - sb.AppendLine(bootBlockInfo); - } - else - sb.AppendLine("Volume is not bootable."); - - information = sb.ToString(); - - XmlFsType = new FileSystemType(); - - if(mdb.drLsBkUp > 0) - { - XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drLsBkUp); - XmlFsType.BackupDateSpecified = true; - } - - XmlFsType.Bootable = bootBlockInfo != null; - XmlFsType.Clusters = mdb.drNmAlBlks; - XmlFsType.ClusterSize = mdb.drAlBlkSiz; - - if(mdb.drCrDate > 0) - { - XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); - XmlFsType.CreationDateSpecified = true; - } - - XmlFsType.Files = mdb.drNmFls; - XmlFsType.FilesSpecified = true; - XmlFsType.FreeClusters = mdb.drFreeBks; - XmlFsType.FreeClustersSpecified = true; - XmlFsType.Type = "MFS"; - XmlFsType.VolumeName = mdb.drVN; + sb.AppendLine(bootBlockInfo); } + else + sb.AppendLine("Volume is not bootable."); + + information = sb.ToString(); + + XmlFsType = new FileSystemType(); + + if(mdb.drLsBkUp > 0) + { + XmlFsType.BackupDate = DateHandlers.MacToDateTime(mdb.drLsBkUp); + XmlFsType.BackupDateSpecified = true; + } + + XmlFsType.Bootable = bootBlockInfo != null; + XmlFsType.Clusters = mdb.drNmAlBlks; + XmlFsType.ClusterSize = mdb.drAlBlkSiz; + + if(mdb.drCrDate > 0) + { + XmlFsType.CreationDate = DateHandlers.MacToDateTime(mdb.drCrDate); + XmlFsType.CreationDateSpecified = true; + } + + XmlFsType.Files = mdb.drNmFls; + XmlFsType.FilesSpecified = true; + XmlFsType.FreeClusters = mdb.drFreeBks; + XmlFsType.FreeClustersSpecified = true; + XmlFsType.Type = "MFS"; + XmlFsType.VolumeName = mdb.drVN; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleMFS/Structs.cs b/Aaru.Filesystems/AppleMFS/Structs.cs index c4f65f054..45c142792 100644 --- a/Aaru.Filesystems/AppleMFS/Structs.cs +++ b/Aaru.Filesystems/AppleMFS/Structs.cs @@ -35,81 +35,80 @@ using System.Diagnostics.CodeAnalysis; #pragma warning disable 169 -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh Volume II +[SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "NotAccessedField.Local")] +public sealed partial class AppleMFS { - // Information from Inside Macintosh Volume II - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "NotAccessedField.Local")] - public sealed partial class AppleMFS + /// Master Directory Block, should be at offset 0x0400 bytes in volume + struct MasterDirectoryBlock { - /// Master Directory Block, should be at offset 0x0400 bytes in volume - struct MasterDirectoryBlock - { - /// 0x000, Signature, 0xD2D7 - public ushort drSigWord; - /// 0x002, Volume creation date - public uint drCrDate; - /// 0x006, Volume last backup date - public uint drLsBkUp; - /// 0x00A, Volume attributes - public AppleCommon.VolumeAttributes drAtrb; - /// 0x00C, Volume number of files - public ushort drNmFls; - /// 0x00E, First directory sector - public ushort drDirSt; - /// 0x010, Length of directory in sectors - public ushort drBlLen; - /// 0x012, Volume allocation blocks - public ushort drNmAlBlks; - /// 0x014, Size of allocation blocks - public uint drAlBlkSiz; - /// 0x018, Number of bytes to allocate - public uint drClpSiz; - /// 0x01C, First allocation block in block map - public ushort drAlBlSt; - /// 0x01E. Next unused file number - public uint drNxtFNum; - /// 0x022, Number of unused allocation blocks - public ushort drFreeBks; - /// 0x024, Length of volume name - public byte drVNSiz; - /// 0x025, Characters of volume name - public string drVN; - } + /// 0x000, Signature, 0xD2D7 + public ushort drSigWord; + /// 0x002, Volume creation date + public uint drCrDate; + /// 0x006, Volume last backup date + public uint drLsBkUp; + /// 0x00A, Volume attributes + public AppleCommon.VolumeAttributes drAtrb; + /// 0x00C, Volume number of files + public ushort drNmFls; + /// 0x00E, First directory sector + public ushort drDirSt; + /// 0x010, Length of directory in sectors + public ushort drBlLen; + /// 0x012, Volume allocation blocks + public ushort drNmAlBlks; + /// 0x014, Size of allocation blocks + public uint drAlBlkSiz; + /// 0x018, Number of bytes to allocate + public uint drClpSiz; + /// 0x01C, First allocation block in block map + public ushort drAlBlSt; + /// 0x01E. Next unused file number + public uint drNxtFNum; + /// 0x022, Number of unused allocation blocks + public ushort drFreeBks; + /// 0x024, Length of volume name + public byte drVNSiz; + /// 0x025, Characters of volume name + public string drVN; + } - [Flags] - enum FileFlags : byte - { - Locked = 0x01, Used = 0x80 - } + [Flags] + enum FileFlags : byte + { + Locked = 0x01, Used = 0x80 + } - struct FileEntry - { - /// 0x00, Entry flags - public FileFlags flFlags; - /// 0x01, Version number - public byte flTyp; - /// 0x02, FinderInfo - public AppleCommon.FInfo flUsrWds; - /// 0x12, file ID - public uint flFlNum; - /// 0x16, first allocation block of data fork - public ushort flStBlk; - /// 0x18, logical end-of-file of data fork - public uint flLgLen; - /// 0x1C, physical end-of-file of data fork - public uint flPyLen; - /// 0x20, first allocation block of resource fork - public ushort flRStBlk; - /// 0x22, logical end-of-file of resource fork - public uint flRLgLen; - /// 0x26, physical end-of-file of resource fork - public uint flRPyLen; - /// 0x2A, date and time of creation - public uint flCrDat; - /// 0x2E, date and time of last modification - public uint flMdDat; - /// 0x32, file name prefixed with length - public byte[] flNam; - } + struct FileEntry + { + /// 0x00, Entry flags + public FileFlags flFlags; + /// 0x01, Version number + public byte flTyp; + /// 0x02, FinderInfo + public AppleCommon.FInfo flUsrWds; + /// 0x12, file ID + public uint flFlNum; + /// 0x16, first allocation block of data fork + public ushort flStBlk; + /// 0x18, logical end-of-file of data fork + public uint flLgLen; + /// 0x1C, physical end-of-file of data fork + public uint flPyLen; + /// 0x20, first allocation block of resource fork + public ushort flRStBlk; + /// 0x22, logical end-of-file of resource fork + public uint flRLgLen; + /// 0x26, physical end-of-file of resource fork + public uint flRPyLen; + /// 0x2A, date and time of creation + public uint flCrDat; + /// 0x2E, date and time of last modification + public uint flMdDat; + /// 0x32, file name prefixed with length + public byte[] flNam; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleMFS/Super.cs b/Aaru.Filesystems/AppleMFS/Super.cs index d37577c68..e49e121a6 100644 --- a/Aaru.Filesystems/AppleMFS/Super.cs +++ b/Aaru.Filesystems/AppleMFS/Super.cs @@ -40,205 +40,204 @@ using Aaru.CommonTypes.Structs; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh Volume II +public sealed partial class AppleMFS { - // Information from Inside Macintosh Volume II - public sealed partial class AppleMFS + const int BYTES_BEFORE_BLOCK_MAP = 64; + + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - const int BYTES_BEFORE_BLOCK_MAP = 64; + _device = imagePlugin; + _partitionStart = partition.Start; + Encoding = encoding ?? Encoding.GetEncoding("macintosh"); + ErrorNumber errno; - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + options ??= GetDefaultOptions(); + + if(options.TryGetValue("debug", out string debugString)) + bool.TryParse(debugString, out _debug); + + _volMdb = new MasterDirectoryBlock(); + + errno = _device.ReadSector(2 + _partitionStart, out _mdbBlocks); + + if(errno != ErrorNumber.NoError) + return errno; + + errno = _device.ReadSector(0 + _partitionStart, out _bootBlocks); + + if(errno != ErrorNumber.NoError) + return errno; + + _volMdb.drSigWord = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x000); + + if(_volMdb.drSigWord != MFS_MAGIC) + return ErrorNumber.InvalidArgument; + + _volMdb.drCrDate = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x002); + _volMdb.drLsBkUp = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x006); + _volMdb.drAtrb = (AppleCommon.VolumeAttributes)BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00A); + _volMdb.drNmFls = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00C); + _volMdb.drDirSt = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00E); + _volMdb.drBlLen = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x010); + _volMdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x012); + _volMdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x014); + _volMdb.drClpSiz = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x018); + _volMdb.drAlBlSt = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x01C); + _volMdb.drNxtFNum = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x01E); + _volMdb.drFreeBks = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x022); + _volMdb.drVNSiz = _mdbBlocks[0x024]; + byte[] variableSize = new byte[_volMdb.drVNSiz + 1]; + Array.Copy(_mdbBlocks, 0x024, variableSize, 0, _volMdb.drVNSiz + 1); + _volMdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); + + errno = _device.ReadSectors(_volMdb.drDirSt + _partitionStart, _volMdb.drBlLen, out _directoryBlocks); + + if(errno != ErrorNumber.NoError) + return errno; + + int bytesInBlockMap = (_volMdb.drNmAlBlks * 12 / 8) + (_volMdb.drNmAlBlks * 12 % 8); + int bytesInWholeMdb = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP; + + int sectorsInWholeMdb = (bytesInWholeMdb / (int)_device.Info.SectorSize) + + (bytesInWholeMdb % (int)_device.Info.SectorSize); + + errno = _device.ReadSectors(_partitionStart + 2, (uint)sectorsInWholeMdb, out byte[] wholeMdb); + + if(errno != ErrorNumber.NoError) + return errno; + + _blockMapBytes = new byte[bytesInBlockMap]; + Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, _blockMapBytes, 0, _blockMapBytes.Length); + + int offset = 0; + _blockMap = new uint[_volMdb.drNmAlBlks + 2 + 1]; + + for(int i = 2; i < _volMdb.drNmAlBlks + 2; i += 8) { - _device = imagePlugin; - _partitionStart = partition.Start; - Encoding = encoding ?? Encoding.GetEncoding("macintosh"); - ErrorNumber errno; + uint tmp1 = 0; + uint tmp2 = 0; + uint tmp3 = 0; - options ??= GetDefaultOptions(); + if(offset + 4 <= _blockMapBytes.Length) + tmp1 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset); - if(options.TryGetValue("debug", out string debugString)) - bool.TryParse(debugString, out _debug); + if(offset + 4 + 4 <= _blockMapBytes.Length) + tmp2 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset + 4); - _volMdb = new MasterDirectoryBlock(); + if(offset + 8 + 4 <= _blockMapBytes.Length) + tmp3 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset + 8); - errno = _device.ReadSector(2 + _partitionStart, out _mdbBlocks); + if(i < _blockMap.Length) + _blockMap[i] = (tmp1 & 0xFFF00000) >> 20; - if(errno != ErrorNumber.NoError) - return errno; + if(i + 2 < _blockMap.Length) + _blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8; - errno = _device.ReadSector(0 + _partitionStart, out _bootBlocks); + if(i + 3 < _blockMap.Length) + _blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28); - if(errno != ErrorNumber.NoError) - return errno; + if(i + 4 < _blockMap.Length) + _blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16; - _volMdb.drSigWord = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x000); + if(i + 5 < _blockMap.Length) + _blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4; - if(_volMdb.drSigWord != MFS_MAGIC) - return ErrorNumber.InvalidArgument; + if(i + 6 < _blockMap.Length) + _blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24); - _volMdb.drCrDate = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x002); - _volMdb.drLsBkUp = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x006); - _volMdb.drAtrb = (AppleCommon.VolumeAttributes)BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00A); - _volMdb.drNmFls = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00C); - _volMdb.drDirSt = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x00E); - _volMdb.drBlLen = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x010); - _volMdb.drNmAlBlks = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x012); - _volMdb.drAlBlkSiz = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x014); - _volMdb.drClpSiz = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x018); - _volMdb.drAlBlSt = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x01C); - _volMdb.drNxtFNum = BigEndianBitConverter.ToUInt32(_mdbBlocks, 0x01E); - _volMdb.drFreeBks = BigEndianBitConverter.ToUInt16(_mdbBlocks, 0x022); - _volMdb.drVNSiz = _mdbBlocks[0x024]; - byte[] variableSize = new byte[_volMdb.drVNSiz + 1]; - Array.Copy(_mdbBlocks, 0x024, variableSize, 0, _volMdb.drVNSiz + 1); - _volMdb.drVN = StringHandlers.PascalToString(variableSize, Encoding); + if(i + 7 < _blockMap.Length) + _blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12; - errno = _device.ReadSectors(_volMdb.drDirSt + _partitionStart, _volMdb.drBlLen, out _directoryBlocks); + if(i + 8 < _blockMap.Length) + _blockMap[i + 7] = tmp3 & 0xFFF; - if(errno != ErrorNumber.NoError) - return errno; - - int bytesInBlockMap = (_volMdb.drNmAlBlks * 12 / 8) + (_volMdb.drNmAlBlks * 12 % 8); - int bytesInWholeMdb = bytesInBlockMap + BYTES_BEFORE_BLOCK_MAP; - - int sectorsInWholeMdb = (bytesInWholeMdb / (int)_device.Info.SectorSize) + - (bytesInWholeMdb % (int)_device.Info.SectorSize); - - errno = _device.ReadSectors(_partitionStart + 2, (uint)sectorsInWholeMdb, out byte[] wholeMdb); - - if(errno != ErrorNumber.NoError) - return errno; - - _blockMapBytes = new byte[bytesInBlockMap]; - Array.Copy(wholeMdb, BYTES_BEFORE_BLOCK_MAP, _blockMapBytes, 0, _blockMapBytes.Length); - - int offset = 0; - _blockMap = new uint[_volMdb.drNmAlBlks + 2 + 1]; - - for(int i = 2; i < _volMdb.drNmAlBlks + 2; i += 8) - { - uint tmp1 = 0; - uint tmp2 = 0; - uint tmp3 = 0; - - if(offset + 4 <= _blockMapBytes.Length) - tmp1 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset); - - if(offset + 4 + 4 <= _blockMapBytes.Length) - tmp2 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset + 4); - - if(offset + 8 + 4 <= _blockMapBytes.Length) - tmp3 = BigEndianBitConverter.ToUInt32(_blockMapBytes, offset + 8); - - if(i < _blockMap.Length) - _blockMap[i] = (tmp1 & 0xFFF00000) >> 20; - - if(i + 2 < _blockMap.Length) - _blockMap[i + 1] = (tmp1 & 0xFFF00) >> 8; - - if(i + 3 < _blockMap.Length) - _blockMap[i + 2] = ((tmp1 & 0xFF) << 4) + ((tmp2 & 0xF0000000) >> 28); - - if(i + 4 < _blockMap.Length) - _blockMap[i + 3] = (tmp2 & 0xFFF0000) >> 16; - - if(i + 5 < _blockMap.Length) - _blockMap[i + 4] = (tmp2 & 0xFFF0) >> 4; - - if(i + 6 < _blockMap.Length) - _blockMap[i + 5] = ((tmp2 & 0xF) << 8) + ((tmp3 & 0xFF000000) >> 24); - - if(i + 7 < _blockMap.Length) - _blockMap[i + 6] = (tmp3 & 0xFFF000) >> 12; - - if(i + 8 < _blockMap.Length) - _blockMap[i + 7] = tmp3 & 0xFFF; - - offset += 12; - } - - if(_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) - { - _device.ReadSectorTag(2 + _partitionStart, SectorTagType.AppleSectorTag, out _mdbTags); - _device.ReadSectorTag(0 + _partitionStart, SectorTagType.AppleSectorTag, out _bootTags); - - _device.ReadSectorsTag(_volMdb.drDirSt + _partitionStart, _volMdb.drBlLen, SectorTagType.AppleSectorTag, - out _directoryTags); - - _device.ReadSectorsTag(_partitionStart + 2, (uint)sectorsInWholeMdb, SectorTagType.AppleSectorTag, - out _bitmapTags); - } - - _sectorsPerBlock = (int)(_volMdb.drAlBlkSiz / _device.Info.SectorSize); - - if(!FillDirectory()) - return ErrorNumber.InvalidArgument; - - _mounted = true; - - ushort bbSig = BigEndianBitConverter.ToUInt16(_bootBlocks, 0x000); - - if(bbSig != AppleCommon.BB_MAGIC) - _bootBlocks = null; - - XmlFsType = new FileSystemType(); - - if(_volMdb.drLsBkUp > 0) - { - XmlFsType.BackupDate = DateHandlers.MacToDateTime(_volMdb.drLsBkUp); - XmlFsType.BackupDateSpecified = true; - } - - XmlFsType.Bootable = bbSig == AppleCommon.BB_MAGIC; - XmlFsType.Clusters = _volMdb.drNmAlBlks; - XmlFsType.ClusterSize = _volMdb.drAlBlkSiz; - - if(_volMdb.drCrDate > 0) - { - XmlFsType.CreationDate = DateHandlers.MacToDateTime(_volMdb.drCrDate); - XmlFsType.CreationDateSpecified = true; - } - - XmlFsType.Files = _volMdb.drNmFls; - XmlFsType.FilesSpecified = true; - XmlFsType.FreeClusters = _volMdb.drFreeBks; - XmlFsType.FreeClustersSpecified = true; - XmlFsType.Type = "MFS"; - XmlFsType.VolumeName = _volMdb.drVN; - - return ErrorNumber.NoError; + offset += 12; } - /// - public ErrorNumber Unmount() + if(_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) { - _mounted = false; - _idToFilename = null; - _idToEntry = null; - _filenameToId = null; - _bootBlocks = null; + _device.ReadSectorTag(2 + _partitionStart, SectorTagType.AppleSectorTag, out _mdbTags); + _device.ReadSectorTag(0 + _partitionStart, SectorTagType.AppleSectorTag, out _bootTags); - return ErrorNumber.NoError; + _device.ReadSectorsTag(_volMdb.drDirSt + _partitionStart, _volMdb.drBlLen, SectorTagType.AppleSectorTag, + out _directoryTags); + + _device.ReadSectorsTag(_partitionStart + 2, (uint)sectorsInWholeMdb, SectorTagType.AppleSectorTag, + out _bitmapTags); } - /// - public ErrorNumber StatFs(out FileSystemInfo stat) + _sectorsPerBlock = (int)(_volMdb.drAlBlkSiz / _device.Info.SectorSize); + + if(!FillDirectory()) + return ErrorNumber.InvalidArgument; + + _mounted = true; + + ushort bbSig = BigEndianBitConverter.ToUInt16(_bootBlocks, 0x000); + + if(bbSig != AppleCommon.BB_MAGIC) + _bootBlocks = null; + + XmlFsType = new FileSystemType(); + + if(_volMdb.drLsBkUp > 0) { - stat = new FileSystemInfo - { - Blocks = _volMdb.drNmAlBlks, - FilenameLength = 255, - Files = _volMdb.drNmFls, - FreeBlocks = _volMdb.drFreeBks, - PluginId = Id, - Type = "Apple MFS" - }; - - stat.FreeFiles = uint.MaxValue - stat.Files; - - return ErrorNumber.NoError; + XmlFsType.BackupDate = DateHandlers.MacToDateTime(_volMdb.drLsBkUp); + XmlFsType.BackupDateSpecified = true; } + + XmlFsType.Bootable = bbSig == AppleCommon.BB_MAGIC; + XmlFsType.Clusters = _volMdb.drNmAlBlks; + XmlFsType.ClusterSize = _volMdb.drAlBlkSiz; + + if(_volMdb.drCrDate > 0) + { + XmlFsType.CreationDate = DateHandlers.MacToDateTime(_volMdb.drCrDate); + XmlFsType.CreationDateSpecified = true; + } + + XmlFsType.Files = _volMdb.drNmFls; + XmlFsType.FilesSpecified = true; + XmlFsType.FreeClusters = _volMdb.drFreeBks; + XmlFsType.FreeClustersSpecified = true; + XmlFsType.Type = "MFS"; + XmlFsType.VolumeName = _volMdb.drVN; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Unmount() + { + _mounted = false; + _idToFilename = null; + _idToEntry = null; + _filenameToId = null; + _bootBlocks = null; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = new FileSystemInfo + { + Blocks = _volMdb.drNmAlBlks, + FilenameLength = 255, + Files = _volMdb.drNmFls, + FreeBlocks = _volMdb.drFreeBks, + PluginId = Id, + Type = "Apple MFS" + }; + + stat.FreeFiles = uint.MaxValue - stat.Files; + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AppleMFS/Xattr.cs b/Aaru.Filesystems/AppleMFS/Xattr.cs index f75d6a2c5..0a278bc56 100644 --- a/Aaru.Filesystems/AppleMFS/Xattr.cs +++ b/Aaru.Filesystems/AppleMFS/Xattr.cs @@ -37,167 +37,166 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Macintosh Volume II +public sealed partial class AppleMFS { - // Information from Inside Macintosh Volume II - public sealed partial class AppleMFS + /// + public ErrorNumber ListXAttr(string path, out List xattrs) { - /// - public ErrorNumber ListXAttr(string path, out List xattrs) + xattrs = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - xattrs = null; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - string[] pathElements = path.Split(new[] + path = pathElements[0]; + + xattrs = new List(); + + if(_debug) + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - path = pathElements[0]; - - xattrs = new List(); - - if(_debug) - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) - { - if(_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) - xattrs.Add("com.apple.macintosh.tags"); - - return ErrorNumber.NoError; - } - - if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) - return ErrorNumber.NoSuchFile; - - if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) - return ErrorNumber.NoSuchFile; - - if(entry.flRLgLen > 0) - { - xattrs.Add("com.apple.ResourceFork"); - - if(_debug && _device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) - xattrs.Add("com.apple.ResourceFork.tags"); - } - - xattrs.Add("com.apple.FinderInfo"); - - if(_debug && - _device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag) && - entry.flLgLen > 0) - xattrs.Add("com.apple.macintosh.tags"); - - xattrs.Sort(); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) - { - if(!_mounted) - return ErrorNumber.AccessDenied; - - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - path = pathElements[0]; - - if(_debug) - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) - if(_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag) && - string.Compare(xattr, "com.apple.macintosh.tags", StringComparison.InvariantCulture) == 0) - { - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) - { - buf = new byte[_directoryTags.Length]; - Array.Copy(_directoryTags, 0, buf, 0, buf.Length); - - return ErrorNumber.NoError; - } - - if(string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0) - { - buf = new byte[_bitmapTags.Length]; - Array.Copy(_bitmapTags, 0, buf, 0, buf.Length); - - return ErrorNumber.NoError; - } - - if(string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0) - { - buf = new byte[_bootTags.Length]; - Array.Copy(_bootTags, 0, buf, 0, buf.Length); - - return ErrorNumber.NoError; - } - - if(string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) - { - buf = new byte[_mdbTags.Length]; - Array.Copy(_mdbTags, 0, buf, 0, buf.Length); - - return ErrorNumber.NoError; - } - } - else - return ErrorNumber.NoSuchExtendedAttribute; - - ErrorNumber error; - - if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) - return ErrorNumber.NoSuchFile; - - if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) - return ErrorNumber.NoSuchFile; - - if(entry.flRLgLen > 0 && - string.Compare(xattr, "com.apple.ResourceFork", StringComparison.InvariantCulture) == 0) - { - error = ReadFile(path, out buf, true, false); - - return error; - } - - if(entry.flRLgLen > 0 && - string.Compare(xattr, "com.apple.ResourceFork.tags", StringComparison.InvariantCulture) == 0) - { - error = ReadFile(path, out buf, true, true); - - return error; - } - - if(string.Compare(xattr, "com.apple.FinderInfo", StringComparison.InvariantCulture) == 0) - { - buf = Marshal.StructureToByteArrayBigEndian(entry.flUsrWds); + if(_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) + xattrs.Add("com.apple.macintosh.tags"); return ErrorNumber.NoError; } - if(!_debug || - !_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag) || - string.Compare(xattr, "com.apple.macintosh.tags", StringComparison.InvariantCulture) != 0) - return ErrorNumber.NoSuchExtendedAttribute; + if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) + return ErrorNumber.NoSuchFile; - error = ReadFile(path, out buf, false, true); + if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) + return ErrorNumber.NoSuchFile; + + if(entry.flRLgLen > 0) + { + xattrs.Add("com.apple.ResourceFork"); + + if(_debug && _device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) + xattrs.Add("com.apple.ResourceFork.tags"); + } + + xattrs.Add("com.apple.FinderInfo"); + + if(_debug && + _device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag) && + entry.flLgLen > 0) + xattrs.Add("com.apple.macintosh.tags"); + + xattrs.Sort(); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; + + path = pathElements[0]; + + if(_debug) + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) + if(_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag) && + string.Compare(xattr, "com.apple.macintosh.tags", StringComparison.InvariantCulture) == 0) + { + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) + { + buf = new byte[_directoryTags.Length]; + Array.Copy(_directoryTags, 0, buf, 0, buf.Length); + + return ErrorNumber.NoError; + } + + if(string.Compare(path, "$Bitmap", StringComparison.InvariantCulture) == 0) + { + buf = new byte[_bitmapTags.Length]; + Array.Copy(_bitmapTags, 0, buf, 0, buf.Length); + + return ErrorNumber.NoError; + } + + if(string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0) + { + buf = new byte[_bootTags.Length]; + Array.Copy(_bootTags, 0, buf, 0, buf.Length); + + return ErrorNumber.NoError; + } + + if(string.Compare(path, "$MDB", StringComparison.InvariantCulture) == 0) + { + buf = new byte[_mdbTags.Length]; + Array.Copy(_mdbTags, 0, buf, 0, buf.Length); + + return ErrorNumber.NoError; + } + } + else + return ErrorNumber.NoSuchExtendedAttribute; + + ErrorNumber error; + + if(!_filenameToId.TryGetValue(path.ToLowerInvariant(), out uint fileId)) + return ErrorNumber.NoSuchFile; + + if(!_idToEntry.TryGetValue(fileId, out FileEntry entry)) + return ErrorNumber.NoSuchFile; + + if(entry.flRLgLen > 0 && + string.Compare(xattr, "com.apple.ResourceFork", StringComparison.InvariantCulture) == 0) + { + error = ReadFile(path, out buf, true, false); return error; } + + if(entry.flRLgLen > 0 && + string.Compare(xattr, "com.apple.ResourceFork.tags", StringComparison.InvariantCulture) == 0) + { + error = ReadFile(path, out buf, true, true); + + return error; + } + + if(string.Compare(xattr, "com.apple.FinderInfo", StringComparison.InvariantCulture) == 0) + { + buf = Marshal.StructureToByteArrayBigEndian(entry.flUsrWds); + + return ErrorNumber.NoError; + } + + if(!_debug || + !_device.Info.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag) || + string.Compare(xattr, "com.apple.macintosh.tags", StringComparison.InvariantCulture) != 0) + return ErrorNumber.NoSuchExtendedAttribute; + + error = ReadFile(path, out buf, false, true); + + return error; } } \ No newline at end of file diff --git a/Aaru.Filesystems/AtheOS.cs b/Aaru.Filesystems/AtheOS.cs index 520def7c4..f806e6a0e 100644 --- a/Aaru.Filesystems/AtheOS.cs +++ b/Aaru.Filesystems/AtheOS.cs @@ -41,214 +41,213 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection for the AtheOS filesystem +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class AtheOS : IFilesystem { + // Little endian constants (that is, as read by .NET :p) + const uint AFS_MAGIC1 = 0x41465331; + const uint AFS_MAGIC2 = 0xDD121031; + const uint AFS_MAGIC3 = 0x15B6830E; + + // Common constants + const uint AFS_SUPERBLOCK_SIZE = 1024; + const uint AFS_BOOTBLOCK_SIZE = AFS_SUPERBLOCK_SIZE; + /// - /// Implements detection for the AtheOS filesystem - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class AtheOS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "AtheOS Filesystem"; + /// + public Guid Id => new("AAB2C4F1-DC07-49EE-A948-576CC51B58C5"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - // Little endian constants (that is, as read by .NET :p) - const uint AFS_MAGIC1 = 0x41465331; - const uint AFS_MAGIC2 = 0xDD121031; - const uint AFS_MAGIC3 = 0x15B6830E; + ulong sector = AFS_BOOTBLOCK_SIZE / imagePlugin.Info.SectorSize; + uint offset = AFS_BOOTBLOCK_SIZE % imagePlugin.Info.SectorSize; + uint run = 1; - // Common constants - const uint AFS_SUPERBLOCK_SIZE = 1024; - const uint AFS_BOOTBLOCK_SIZE = AFS_SUPERBLOCK_SIZE; + if(imagePlugin.Info.SectorSize < AFS_SUPERBLOCK_SIZE) + run = AFS_SUPERBLOCK_SIZE / imagePlugin.Info.SectorSize; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "AtheOS Filesystem"; - /// - public Guid Id => new("AAB2C4F1-DC07-49EE-A948-576CC51B58C5"); - /// - public string Author => "Natalia Portillo"; + if(sector + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = imagePlugin.ReadSectors(sector + partition.Start, run, out byte[] tmp); + + if(errno != ErrorNumber.NoError) + return false; + + byte[] sbSector = new byte[AFS_SUPERBLOCK_SIZE]; + + if(offset + AFS_SUPERBLOCK_SIZE > tmp.Length) + return false; + + Array.Copy(tmp, offset, sbSector, 0, AFS_SUPERBLOCK_SIZE); + + uint magic = BitConverter.ToUInt32(sbSector, 0x20); + + return magic == AFS_MAGIC1; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + + ulong sector = AFS_BOOTBLOCK_SIZE / imagePlugin.Info.SectorSize; + uint offset = AFS_BOOTBLOCK_SIZE % imagePlugin.Info.SectorSize; + uint run = 1; + + if(imagePlugin.Info.SectorSize < AFS_SUPERBLOCK_SIZE) + run = AFS_SUPERBLOCK_SIZE / imagePlugin.Info.SectorSize; + + ErrorNumber errno = imagePlugin.ReadSectors(sector + partition.Start, run, out byte[] tmp); + + if(errno != ErrorNumber.NoError) + return; + + byte[] sbSector = new byte[AFS_SUPERBLOCK_SIZE]; + Array.Copy(tmp, offset, sbSector, 0, AFS_SUPERBLOCK_SIZE); + + SuperBlock afsSb = Marshal.ByteArrayToStructureLittleEndian(sbSector); + + sb.AppendLine("Atheos filesystem"); + + if(afsSb.flags == 1) + sb.AppendLine("Filesystem is read-only"); + + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(afsSb.name, Encoding)).AppendLine(); + sb.AppendFormat("{0} bytes per block", afsSb.block_size).AppendLine(); + + sb.AppendFormat("{0} blocks in volume ({1} bytes)", afsSb.num_blocks, afsSb.num_blocks * afsSb.block_size). + AppendLine(); + + sb.AppendFormat("{0} used blocks ({1} bytes)", afsSb.used_blocks, afsSb.used_blocks * afsSb.block_size). + AppendLine(); + + sb.AppendFormat("{0} bytes per i-node", afsSb.inode_size).AppendLine(); + + sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", afsSb.blocks_per_ag, + afsSb.blocks_per_ag * afsSb.block_size).AppendLine(); + + sb.AppendFormat("{0} allocation groups in volume", afsSb.num_ags).AppendLine(); + + sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", + afsSb.log_blocks_start, afsSb.log_blocks_ag, afsSb.log_blocks_len, + afsSb.log_blocks_len * afsSb.block_size).AppendLine(); + + sb.AppendFormat("Journal starts in byte {0} and has {1} bytes in {2} blocks", afsSb.log_start, + afsSb.log_size, afsSb.log_valid_blocks).AppendLine(); + + sb. + AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", + afsSb.root_dir_start, afsSb.root_dir_ag, afsSb.root_dir_len, + afsSb.root_dir_len * afsSb.block_size).AppendLine(); + + sb. + AppendFormat("Directory containing files scheduled for deletion's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", + afsSb.deleted_start, afsSb.deleted_ag, afsSb.deleted_len, + afsSb.deleted_len * afsSb.block_size).AppendLine(); + + sb. + AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", + afsSb.indices_start, afsSb.indices_ag, afsSb.indices_len, + afsSb.indices_len * afsSb.block_size).AppendLine(); + + sb.AppendFormat("{0} blocks for bootloader ({1} bytes)", afsSb.boot_size, + afsSb.boot_size * afsSb.block_size).AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - ulong sector = AFS_BOOTBLOCK_SIZE / imagePlugin.Info.SectorSize; - uint offset = AFS_BOOTBLOCK_SIZE % imagePlugin.Info.SectorSize; - uint run = 1; + Clusters = (ulong)afsSb.num_blocks, + ClusterSize = afsSb.block_size, + Dirty = false, + FreeClusters = (ulong)(afsSb.num_blocks - afsSb.used_blocks), + FreeClustersSpecified = true, + Type = "AtheOS filesystem", + VolumeName = StringHandlers.CToString(afsSb.name, Encoding) + }; + } - if(imagePlugin.Info.SectorSize < AFS_SUPERBLOCK_SIZE) - run = AFS_SUPERBLOCK_SIZE / imagePlugin.Info.SectorSize; - - if(sector + partition.Start >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(sector + partition.Start, run, out byte[] tmp); - - if(errno != ErrorNumber.NoError) - return false; - - byte[] sbSector = new byte[AFS_SUPERBLOCK_SIZE]; - - if(offset + AFS_SUPERBLOCK_SIZE > tmp.Length) - return false; - - Array.Copy(tmp, offset, sbSector, 0, AFS_SUPERBLOCK_SIZE); - - uint magic = BitConverter.ToUInt32(sbSector, 0x20); - - return magic == AFS_MAGIC1; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - var sb = new StringBuilder(); - - ulong sector = AFS_BOOTBLOCK_SIZE / imagePlugin.Info.SectorSize; - uint offset = AFS_BOOTBLOCK_SIZE % imagePlugin.Info.SectorSize; - uint run = 1; - - if(imagePlugin.Info.SectorSize < AFS_SUPERBLOCK_SIZE) - run = AFS_SUPERBLOCK_SIZE / imagePlugin.Info.SectorSize; - - ErrorNumber errno = imagePlugin.ReadSectors(sector + partition.Start, run, out byte[] tmp); - - if(errno != ErrorNumber.NoError) - return; - - byte[] sbSector = new byte[AFS_SUPERBLOCK_SIZE]; - Array.Copy(tmp, offset, sbSector, 0, AFS_SUPERBLOCK_SIZE); - - SuperBlock afsSb = Marshal.ByteArrayToStructureLittleEndian(sbSector); - - sb.AppendLine("Atheos filesystem"); - - if(afsSb.flags == 1) - sb.AppendLine("Filesystem is read-only"); - - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(afsSb.name, Encoding)).AppendLine(); - sb.AppendFormat("{0} bytes per block", afsSb.block_size).AppendLine(); - - sb.AppendFormat("{0} blocks in volume ({1} bytes)", afsSb.num_blocks, afsSb.num_blocks * afsSb.block_size). - AppendLine(); - - sb.AppendFormat("{0} used blocks ({1} bytes)", afsSb.used_blocks, afsSb.used_blocks * afsSb.block_size). - AppendLine(); - - sb.AppendFormat("{0} bytes per i-node", afsSb.inode_size).AppendLine(); - - sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", afsSb.blocks_per_ag, - afsSb.blocks_per_ag * afsSb.block_size).AppendLine(); - - sb.AppendFormat("{0} allocation groups in volume", afsSb.num_ags).AppendLine(); - - sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", - afsSb.log_blocks_start, afsSb.log_blocks_ag, afsSb.log_blocks_len, - afsSb.log_blocks_len * afsSb.block_size).AppendLine(); - - sb.AppendFormat("Journal starts in byte {0} and has {1} bytes in {2} blocks", afsSb.log_start, - afsSb.log_size, afsSb.log_valid_blocks).AppendLine(); - - sb. - AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", - afsSb.root_dir_start, afsSb.root_dir_ag, afsSb.root_dir_len, - afsSb.root_dir_len * afsSb.block_size).AppendLine(); - - sb. - AppendFormat("Directory containing files scheduled for deletion's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", - afsSb.deleted_start, afsSb.deleted_ag, afsSb.deleted_len, - afsSb.deleted_len * afsSb.block_size).AppendLine(); - - sb. - AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", - afsSb.indices_start, afsSb.indices_ag, afsSb.indices_len, - afsSb.indices_len * afsSb.block_size).AppendLine(); - - sb.AppendFormat("{0} blocks for bootloader ({1} bytes)", afsSb.boot_size, - afsSb.boot_size * afsSb.block_size).AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Clusters = (ulong)afsSb.num_blocks, - ClusterSize = afsSb.block_size, - Dirty = false, - FreeClusters = (ulong)(afsSb.num_blocks - afsSb.used_blocks), - FreeClustersSpecified = true, - Type = "AtheOS filesystem", - VolumeName = StringHandlers.CToString(afsSb.name, Encoding) - }; - } - - /// Be superblock - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - /// 0x000, Volume name, 32 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] name; - /// 0x020, "AFS1", 0x41465331 - public readonly uint magic1; - /// 0x024, "BIGE", 0x42494745 - public readonly uint fs_byte_order; - /// 0x028, Bytes per block - public readonly uint block_size; - /// 0x02C, 1 << block_shift == block_size - public readonly uint block_shift; - /// 0x030, Blocks in volume - public readonly long num_blocks; - /// 0x038, Used blocks in volume - public readonly long used_blocks; - /// 0x040, Bytes per inode - public readonly int inode_size; - /// 0x044, 0xDD121031 - public readonly uint magic2; - /// 0x048, Blocks per allocation group - public readonly int blocks_per_ag; - /// 0x04C, 1 << ag_shift == blocks_per_ag - public readonly int ag_shift; - /// 0x050, Allocation groups in volume - public readonly int num_ags; - /// 0x054, 0x434c454e if clean, 0x44495254 if dirty - public readonly uint flags; - /// 0x058, Allocation group of journal - public readonly int log_blocks_ag; - /// 0x05C, Start block of journal, inside ag - public readonly ushort log_blocks_start; - /// 0x05E, Length in blocks of journal, inside ag - public readonly ushort log_blocks_len; - /// 0x060, Start of journal - public readonly long log_start; - /// 0x068, Valid block logs - public readonly int log_valid_blocks; - /// 0x06C, Log size - public readonly int log_size; - /// 0x070, 0x15B6830E - public readonly uint magic3; - /// 0x074, Allocation group where root folder's i-node resides - public readonly int root_dir_ag; - /// 0x078, Start in ag of root folder's i-node - public readonly ushort root_dir_start; - /// 0x07A, As this is part of inode_addr, this is 1 - public readonly ushort root_dir_len; - /// 0x07C, Allocation group where pending-delete-files' i-node resides - public readonly int deleted_ag; - /// 0x080, Start in ag of pending-delete-files' i-node - public readonly ushort deleted_start; - /// 0x082, As this is part of inode_addr, this is 1 - public readonly ushort deleted_len; - /// 0x084, Allocation group where indices' i-node resides - public readonly int indices_ag; - /// 0x088, Start in ag of indices' i-node - public readonly ushort indices_start; - /// 0x08A, As this is part of inode_addr, this is 1 - public readonly ushort indices_len; - /// 0x08C, Size of bootloader - public readonly int boot_size; - } + /// Be superblock + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + /// 0x000, Volume name, 32 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] name; + /// 0x020, "AFS1", 0x41465331 + public readonly uint magic1; + /// 0x024, "BIGE", 0x42494745 + public readonly uint fs_byte_order; + /// 0x028, Bytes per block + public readonly uint block_size; + /// 0x02C, 1 << block_shift == block_size + public readonly uint block_shift; + /// 0x030, Blocks in volume + public readonly long num_blocks; + /// 0x038, Used blocks in volume + public readonly long used_blocks; + /// 0x040, Bytes per inode + public readonly int inode_size; + /// 0x044, 0xDD121031 + public readonly uint magic2; + /// 0x048, Blocks per allocation group + public readonly int blocks_per_ag; + /// 0x04C, 1 << ag_shift == blocks_per_ag + public readonly int ag_shift; + /// 0x050, Allocation groups in volume + public readonly int num_ags; + /// 0x054, 0x434c454e if clean, 0x44495254 if dirty + public readonly uint flags; + /// 0x058, Allocation group of journal + public readonly int log_blocks_ag; + /// 0x05C, Start block of journal, inside ag + public readonly ushort log_blocks_start; + /// 0x05E, Length in blocks of journal, inside ag + public readonly ushort log_blocks_len; + /// 0x060, Start of journal + public readonly long log_start; + /// 0x068, Valid block logs + public readonly int log_valid_blocks; + /// 0x06C, Log size + public readonly int log_size; + /// 0x070, 0x15B6830E + public readonly uint magic3; + /// 0x074, Allocation group where root folder's i-node resides + public readonly int root_dir_ag; + /// 0x078, Start in ag of root folder's i-node + public readonly ushort root_dir_start; + /// 0x07A, As this is part of inode_addr, this is 1 + public readonly ushort root_dir_len; + /// 0x07C, Allocation group where pending-delete-files' i-node resides + public readonly int deleted_ag; + /// 0x080, Start in ag of pending-delete-files' i-node + public readonly ushort deleted_start; + /// 0x082, As this is part of inode_addr, this is 1 + public readonly ushort deleted_len; + /// 0x084, Allocation group where indices' i-node resides + public readonly int indices_ag; + /// 0x088, Start in ag of indices' i-node + public readonly ushort indices_start; + /// 0x08A, As this is part of inode_addr, this is 1 + public readonly ushort indices_len; + /// 0x08C, Size of bootloader + public readonly int boot_size; } } \ No newline at end of file diff --git a/Aaru.Filesystems/BFS.cs b/Aaru.Filesystems/BFS.cs index 778429877..a94b10f34 100644 --- a/Aaru.Filesystems/BFS.cs +++ b/Aaru.Filesystems/BFS.cs @@ -41,282 +41,281 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Practical Filesystem Design, ISBN 1-55860-497-9 +/// +/// Implements detection of the Be (new) filesystem +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class BeFS : IFilesystem { - // Information from Practical Filesystem Design, ISBN 1-55860-497-9 + // Little endian constants (that is, as read by .NET :p) + const uint BEFS_MAGIC1 = 0x42465331; + const uint BEFS_MAGIC2 = 0xDD121031; + const uint BEFS_MAGIC3 = 0x15B6830E; + const uint BEFS_ENDIAN = 0x42494745; + + // Big endian constants + const uint BEFS_CIGAM1 = 0x31534642; + const uint BEFS_NAIDNE = 0x45474942; + + // Common constants + const uint BEFS_CLEAN = 0x434C454E; + const uint BEFS_DIRTY = 0x44495254; + /// - /// Implements detection of the Be (new) filesystem - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class BeFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Be Filesystem"; + /// + public Guid Id => new("dc8572b3-b6ad-46e4-8de9-cbe123ff6672"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - // Little endian constants (that is, as read by .NET :p) - const uint BEFS_MAGIC1 = 0x42465331; - const uint BEFS_MAGIC2 = 0xDD121031; - const uint BEFS_MAGIC3 = 0x15B6830E; - const uint BEFS_ENDIAN = 0x42494745; + if(2 + partition.Start >= partition.End) + return false; - // Big endian constants - const uint BEFS_CIGAM1 = 0x31534642; - const uint BEFS_NAIDNE = 0x45474942; + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); - // Common constants - const uint BEFS_CLEAN = 0x434C454E; - const uint BEFS_DIRTY = 0x44495254; + if(errno != ErrorNumber.NoError) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Be Filesystem"; - /// - public Guid Id => new("dc8572b3-b6ad-46e4-8de9-cbe123ff6672"); - /// - public string Author => "Natalia Portillo"; + uint magic = BitConverter.ToUInt32(sbSector, 0x20); + uint magicBe = BigEndianBitConverter.ToUInt32(sbSector, 0x20); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(magic == BEFS_MAGIC1 || + magicBe == BEFS_MAGIC1) + return true; + + if(sbSector.Length >= 0x400) { - if(2 + partition.Start >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); - - if(errno != ErrorNumber.NoError) - return false; - - uint magic = BitConverter.ToUInt32(sbSector, 0x20); - uint magicBe = BigEndianBitConverter.ToUInt32(sbSector, 0x20); - - if(magic == BEFS_MAGIC1 || - magicBe == BEFS_MAGIC1) - return true; - - if(sbSector.Length >= 0x400) - { - magic = BitConverter.ToUInt32(sbSector, 0x220); - magicBe = BigEndianBitConverter.ToUInt32(sbSector, 0x220); - } - - if(magic == BEFS_MAGIC1 || - magicBe == BEFS_MAGIC1) - return true; - - errno = imagePlugin.ReadSector(1 + partition.Start, out sbSector); - - if(errno != ErrorNumber.NoError) - return false; - - magic = BitConverter.ToUInt32(sbSector, 0x20); - magicBe = BigEndianBitConverter.ToUInt32(sbSector, 0x20); - - return magic == BEFS_MAGIC1 || magicBe == BEFS_MAGIC1; + magic = BitConverter.ToUInt32(sbSector, 0x220); + magicBe = BigEndianBitConverter.ToUInt32(sbSector, 0x220); } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + if(magic == BEFS_MAGIC1 || + magicBe == BEFS_MAGIC1) + return true; + + errno = imagePlugin.ReadSector(1 + partition.Start, out sbSector); + + if(errno != ErrorNumber.NoError) + return false; + + magic = BitConverter.ToUInt32(sbSector, 0x20); + magicBe = BigEndianBitConverter.ToUInt32(sbSector, 0x20); + + return magic == BEFS_MAGIC1 || magicBe == BEFS_MAGIC1; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + + var besb = new SuperBlock(); + + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); + + if(errno != ErrorNumber.NoError) + return; + + bool littleEndian; + + besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20); + + if(besb.magic1 == BEFS_MAGIC1 || + besb.magic1 == BEFS_CIGAM1) // Magic is at offset + littleEndian = besb.magic1 == BEFS_CIGAM1; + else { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - var sb = new StringBuilder(); - - var besb = new SuperBlock(); - - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); + errno = imagePlugin.ReadSector(1 + partition.Start, out sbSector); if(errno != ErrorNumber.NoError) return; - bool littleEndian; - besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20); if(besb.magic1 == BEFS_MAGIC1 || - besb.magic1 == BEFS_CIGAM1) // Magic is at offset + besb.magic1 == BEFS_CIGAM1) // There is a boot sector littleEndian = besb.magic1 == BEFS_CIGAM1; - else + else if(sbSector.Length >= 0x400) { - errno = imagePlugin.ReadSector(1 + partition.Start, out sbSector); + errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] temp); if(errno != ErrorNumber.NoError) return; - besb.magic1 = BigEndianBitConverter.ToUInt32(sbSector, 0x20); + besb.magic1 = BigEndianBitConverter.ToUInt32(temp, 0x220); if(besb.magic1 == BEFS_MAGIC1 || besb.magic1 == BEFS_CIGAM1) // There is a boot sector - littleEndian = besb.magic1 == BEFS_CIGAM1; - else if(sbSector.Length >= 0x400) { - errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] temp); - - if(errno != ErrorNumber.NoError) - return; - - besb.magic1 = BigEndianBitConverter.ToUInt32(temp, 0x220); - - if(besb.magic1 == BEFS_MAGIC1 || - besb.magic1 == BEFS_CIGAM1) // There is a boot sector - { - littleEndian = besb.magic1 == BEFS_CIGAM1; - sbSector = new byte[0x200]; - Array.Copy(temp, 0x200, sbSector, 0, 0x200); - } - else - return; + littleEndian = besb.magic1 == BEFS_CIGAM1; + sbSector = new byte[0x200]; + Array.Copy(temp, 0x200, sbSector, 0, 0x200); } else return; } - - besb = littleEndian ? Marshal.ByteArrayToStructureLittleEndian(sbSector) - : Marshal.ByteArrayToStructureBigEndian(sbSector); - - sb.AppendLine(littleEndian ? "Little-endian BeFS" : "Big-endian BeFS"); - - if(besb.magic1 != BEFS_MAGIC1 || - besb.fs_byte_order != BEFS_ENDIAN || - besb.magic2 != BEFS_MAGIC2 || - besb.magic3 != BEFS_MAGIC3 || - besb.root_dir_len != 1 || - besb.indices_len != 1 || - 1 << (int)besb.block_shift != besb.block_size) - { - sb.AppendLine("Superblock seems corrupt, following information may be incorrect"); - sb.AppendFormat("Magic 1: 0x{0:X8} (Should be 0x42465331)", besb.magic1).AppendLine(); - sb.AppendFormat("Magic 2: 0x{0:X8} (Should be 0xDD121031)", besb.magic2).AppendLine(); - sb.AppendFormat("Magic 3: 0x{0:X8} (Should be 0x15B6830E)", besb.magic3).AppendLine(); - - sb.AppendFormat("Filesystem endianness: 0x{0:X8} (Should be 0x42494745)", besb.fs_byte_order). - AppendLine(); - - sb.AppendFormat("Root folder's i-node size: {0} blocks (Should be 1)", besb.root_dir_len).AppendLine(); - sb.AppendFormat("Indices' i-node size: {0} blocks (Should be 1)", besb.indices_len).AppendLine(); - - sb.AppendFormat("1 << block_shift == block_size => 1 << {0} == {1} (Should be {2})", besb.block_shift, - 1 << (int)besb.block_shift, besb.block_size).AppendLine(); - } - - switch(besb.flags) - { - case BEFS_CLEAN: - sb.AppendLine(besb.log_start == besb.log_end ? "Filesystem is clean" : "Filesystem is dirty"); - - break; - case BEFS_DIRTY: - sb.AppendLine("Filesystem is dirty"); - - break; - default: - sb.AppendFormat("Unknown flags: {0:X8}", besb.flags).AppendLine(); - - break; - } - - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(besb.name, Encoding)).AppendLine(); - sb.AppendFormat("{0} bytes per block", besb.block_size).AppendLine(); - - sb.AppendFormat("{0} blocks in volume ({1} bytes)", besb.num_blocks, besb.num_blocks * besb.block_size). - AppendLine(); - - sb.AppendFormat("{0} used blocks ({1} bytes)", besb.used_blocks, besb.used_blocks * besb.block_size). - AppendLine(); - - sb.AppendFormat("{0} bytes per i-node", besb.inode_size).AppendLine(); - - sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", besb.blocks_per_ag, - besb.blocks_per_ag * besb.block_size).AppendLine(); - - sb.AppendFormat("{0} allocation groups in volume", besb.num_ags).AppendLine(); - - sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", - besb.log_blocks_start, besb.log_blocks_ag, besb.log_blocks_len, - besb.log_blocks_len * besb.block_size).AppendLine(); - - sb.AppendFormat("Journal starts in byte {0} and ends in byte {1}", besb.log_start, besb.log_end). - AppendLine(); - - sb. - AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", - besb.root_dir_start, besb.root_dir_ag, besb.root_dir_len, - besb.root_dir_len * besb.block_size).AppendLine(); - - sb. - AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", - besb.indices_start, besb.indices_ag, besb.indices_len, besb.indices_len * besb.block_size). - AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Clusters = (ulong)besb.num_blocks, - ClusterSize = besb.block_size, - Dirty = besb.flags == BEFS_DIRTY, - FreeClusters = (ulong)(besb.num_blocks - besb.used_blocks), - FreeClustersSpecified = true, - Type = "BeFS", - VolumeName = StringHandlers.CToString(besb.name, Encoding) - }; + else + return; } - /// Be superblock - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct SuperBlock + besb = littleEndian ? Marshal.ByteArrayToStructureLittleEndian(sbSector) + : Marshal.ByteArrayToStructureBigEndian(sbSector); + + sb.AppendLine(littleEndian ? "Little-endian BeFS" : "Big-endian BeFS"); + + if(besb.magic1 != BEFS_MAGIC1 || + besb.fs_byte_order != BEFS_ENDIAN || + besb.magic2 != BEFS_MAGIC2 || + besb.magic3 != BEFS_MAGIC3 || + besb.root_dir_len != 1 || + besb.indices_len != 1 || + 1 << (int)besb.block_shift != besb.block_size) { - /// 0x000, Volume name, 32 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] name; - /// 0x020, "BFS1", 0x42465331 - public uint magic1; - /// 0x024, "BIGE", 0x42494745 - public readonly uint fs_byte_order; - /// 0x028, Bytes per block - public readonly uint block_size; - /// 0x02C, 1 << block_shift == block_size - public readonly uint block_shift; - /// 0x030, Blocks in volume - public readonly long num_blocks; - /// 0x038, Used blocks in volume - public readonly long used_blocks; - /// 0x040, Bytes per inode - public readonly int inode_size; - /// 0x044, 0xDD121031 - public readonly uint magic2; - /// 0x048, Blocks per allocation group - public readonly int blocks_per_ag; - /// 0x04C, 1 << ag_shift == blocks_per_ag - public readonly int ag_shift; - /// 0x050, Allocation groups in volume - public readonly int num_ags; - /// 0x054, 0x434c454e if clean, 0x44495254 if dirty - public readonly uint flags; - /// 0x058, Allocation group of journal - public readonly int log_blocks_ag; - /// 0x05C, Start block of journal, inside ag - public readonly ushort log_blocks_start; - /// 0x05E, Length in blocks of journal, inside ag - public readonly ushort log_blocks_len; - /// 0x060, Start of journal - public readonly long log_start; - /// 0x068, End of journal - public readonly long log_end; - /// 0x070, 0x15B6830E - public readonly uint magic3; - /// 0x074, Allocation group where root folder's i-node resides - public readonly int root_dir_ag; - /// 0x078, Start in ag of root folder's i-node - public readonly ushort root_dir_start; - /// 0x07A, As this is part of inode_addr, this is 1 - public readonly ushort root_dir_len; - /// 0x07C, Allocation group where indices' i-node resides - public readonly int indices_ag; - /// 0x080, Start in ag of indices' i-node - public readonly ushort indices_start; - /// 0x082, As this is part of inode_addr, this is 1 - public readonly ushort indices_len; + sb.AppendLine("Superblock seems corrupt, following information may be incorrect"); + sb.AppendFormat("Magic 1: 0x{0:X8} (Should be 0x42465331)", besb.magic1).AppendLine(); + sb.AppendFormat("Magic 2: 0x{0:X8} (Should be 0xDD121031)", besb.magic2).AppendLine(); + sb.AppendFormat("Magic 3: 0x{0:X8} (Should be 0x15B6830E)", besb.magic3).AppendLine(); + + sb.AppendFormat("Filesystem endianness: 0x{0:X8} (Should be 0x42494745)", besb.fs_byte_order). + AppendLine(); + + sb.AppendFormat("Root folder's i-node size: {0} blocks (Should be 1)", besb.root_dir_len).AppendLine(); + sb.AppendFormat("Indices' i-node size: {0} blocks (Should be 1)", besb.indices_len).AppendLine(); + + sb.AppendFormat("1 << block_shift == block_size => 1 << {0} == {1} (Should be {2})", besb.block_shift, + 1 << (int)besb.block_shift, besb.block_size).AppendLine(); } + + switch(besb.flags) + { + case BEFS_CLEAN: + sb.AppendLine(besb.log_start == besb.log_end ? "Filesystem is clean" : "Filesystem is dirty"); + + break; + case BEFS_DIRTY: + sb.AppendLine("Filesystem is dirty"); + + break; + default: + sb.AppendFormat("Unknown flags: {0:X8}", besb.flags).AppendLine(); + + break; + } + + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(besb.name, Encoding)).AppendLine(); + sb.AppendFormat("{0} bytes per block", besb.block_size).AppendLine(); + + sb.AppendFormat("{0} blocks in volume ({1} bytes)", besb.num_blocks, besb.num_blocks * besb.block_size). + AppendLine(); + + sb.AppendFormat("{0} used blocks ({1} bytes)", besb.used_blocks, besb.used_blocks * besb.block_size). + AppendLine(); + + sb.AppendFormat("{0} bytes per i-node", besb.inode_size).AppendLine(); + + sb.AppendFormat("{0} blocks per allocation group ({1} bytes)", besb.blocks_per_ag, + besb.blocks_per_ag * besb.block_size).AppendLine(); + + sb.AppendFormat("{0} allocation groups in volume", besb.num_ags).AppendLine(); + + sb.AppendFormat("Journal resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", + besb.log_blocks_start, besb.log_blocks_ag, besb.log_blocks_len, + besb.log_blocks_len * besb.block_size).AppendLine(); + + sb.AppendFormat("Journal starts in byte {0} and ends in byte {1}", besb.log_start, besb.log_end). + AppendLine(); + + sb. + AppendFormat("Root folder's i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", + besb.root_dir_start, besb.root_dir_ag, besb.root_dir_len, + besb.root_dir_len * besb.block_size).AppendLine(); + + sb. + AppendFormat("Indices' i-node resides in block {0} of allocation group {1} and runs for {2} blocks ({3} bytes)", + besb.indices_start, besb.indices_ag, besb.indices_len, besb.indices_len * besb.block_size). + AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType + { + Clusters = (ulong)besb.num_blocks, + ClusterSize = besb.block_size, + Dirty = besb.flags == BEFS_DIRTY, + FreeClusters = (ulong)(besb.num_blocks - besb.used_blocks), + FreeClustersSpecified = true, + Type = "BeFS", + VolumeName = StringHandlers.CToString(besb.name, Encoding) + }; + } + + /// Be superblock + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct SuperBlock + { + /// 0x000, Volume name, 32 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] name; + /// 0x020, "BFS1", 0x42465331 + public uint magic1; + /// 0x024, "BIGE", 0x42494745 + public readonly uint fs_byte_order; + /// 0x028, Bytes per block + public readonly uint block_size; + /// 0x02C, 1 << block_shift == block_size + public readonly uint block_shift; + /// 0x030, Blocks in volume + public readonly long num_blocks; + /// 0x038, Used blocks in volume + public readonly long used_blocks; + /// 0x040, Bytes per inode + public readonly int inode_size; + /// 0x044, 0xDD121031 + public readonly uint magic2; + /// 0x048, Blocks per allocation group + public readonly int blocks_per_ag; + /// 0x04C, 1 << ag_shift == blocks_per_ag + public readonly int ag_shift; + /// 0x050, Allocation groups in volume + public readonly int num_ags; + /// 0x054, 0x434c454e if clean, 0x44495254 if dirty + public readonly uint flags; + /// 0x058, Allocation group of journal + public readonly int log_blocks_ag; + /// 0x05C, Start block of journal, inside ag + public readonly ushort log_blocks_start; + /// 0x05E, Length in blocks of journal, inside ag + public readonly ushort log_blocks_len; + /// 0x060, Start of journal + public readonly long log_start; + /// 0x068, End of journal + public readonly long log_end; + /// 0x070, 0x15B6830E + public readonly uint magic3; + /// 0x074, Allocation group where root folder's i-node resides + public readonly int root_dir_ag; + /// 0x078, Start in ag of root folder's i-node + public readonly ushort root_dir_start; + /// 0x07A, As this is part of inode_addr, this is 1 + public readonly ushort root_dir_len; + /// 0x07C, Allocation group where indices' i-node resides + public readonly int indices_ag; + /// 0x080, Start in ag of indices' i-node + public readonly ushort indices_start; + /// 0x082, As this is part of inode_addr, this is 1 + public readonly ushort indices_len; } } \ No newline at end of file diff --git a/Aaru.Filesystems/BTRFS.cs b/Aaru.Filesystems/BTRFS.cs index 45bb3b9bd..10a0172bc 100644 --- a/Aaru.Filesystems/BTRFS.cs +++ b/Aaru.Filesystems/BTRFS.cs @@ -40,242 +40,241 @@ using Aaru.Console; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the b-tree filesystem (btrfs) +public sealed class BTRFS : IFilesystem { + /// BTRFS magic "_BHRfS_M" + const ulong BTRFS_MAGIC = 0x4D5F53665248425F; + /// - /// Implements detection of the b-tree filesystem (btrfs) - public sealed class BTRFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "B-tree file system"; + /// + public Guid Id => new("C904CF15-5222-446B-B7DB-02EAC5D781B3"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// BTRFS magic "_BHRfS_M" - const ulong BTRFS_MAGIC = 0x4D5F53665248425F; + if(partition.Start >= partition.End) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "B-tree file system"; - /// - public Guid Id => new("C904CF15-5222-446B-B7DB-02EAC5D781B3"); - /// - public string Author => "Natalia Portillo"; + ulong sbSectorOff = 0x10000 / imagePlugin.Info.SectorSize; + uint sbSectorSize = 0x1000 / imagePlugin.Info.SectorSize; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(sbSectorOff + partition.Start >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSectorSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + SuperBlock btrfsSb; + + try { - if(partition.Start >= partition.End) - return false; - - ulong sbSectorOff = 0x10000 / imagePlugin.Info.SectorSize; - uint sbSectorSize = 0x1000 / imagePlugin.Info.SectorSize; - - if(sbSectorOff + partition.Start >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSectorSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - SuperBlock btrfsSb; - - try - { - btrfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); - } - catch - { - return false; - } - - AaruConsole.DebugWriteLine("BTRFS Plugin", "sbSectorOff = {0}", sbSectorOff); - AaruConsole.DebugWriteLine("BTRFS Plugin", "sbSectorSize = {0}", sbSectorSize); - AaruConsole.DebugWriteLine("BTRFS Plugin", "partition.PartitionStartSector = {0}", partition.Start); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.magic = 0x{0:X16}", btrfsSb.magic); - - return btrfsSb.magic == BTRFS_MAGIC; + btrfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); + } + catch + { + return false; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + AaruConsole.DebugWriteLine("BTRFS Plugin", "sbSectorOff = {0}", sbSectorOff); + AaruConsole.DebugWriteLine("BTRFS Plugin", "sbSectorSize = {0}", sbSectorSize); + AaruConsole.DebugWriteLine("BTRFS Plugin", "partition.PartitionStartSector = {0}", partition.Start); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.magic = 0x{0:X16}", btrfsSb.magic); + + return btrfsSb.magic == BTRFS_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + var sbInformation = new StringBuilder(); + XmlFsType = new FileSystemType(); + information = ""; + + ulong sbSectorOff = 0x10000 / imagePlugin.Info.SectorSize; + uint sbSectorSize = 0x1000 / imagePlugin.Info.SectorSize; + + ErrorNumber errno = imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSectorSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + SuperBlock btrfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.checksum = {0}", btrfsSb.checksum); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.uuid = {0}", btrfsSb.uuid); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.pba = {0}", btrfsSb.pba); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.flags = {0}", btrfsSb.flags); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.magic = {0}", btrfsSb.magic); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.generation = {0}", btrfsSb.generation); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.root_lba = {0}", btrfsSb.root_lba); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.chunk_lba = {0}", btrfsSb.chunk_lba); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.log_lba = {0}", btrfsSb.log_lba); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.log_root_transid = {0}", btrfsSb.log_root_transid); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.total_bytes = {0}", btrfsSb.total_bytes); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.bytes_used = {0}", btrfsSb.bytes_used); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.root_dir_objectid = {0}", btrfsSb.root_dir_objectid); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.num_devices = {0}", btrfsSb.num_devices); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.sectorsize = {0}", btrfsSb.sectorsize); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.nodesize = {0}", btrfsSb.nodesize); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.leafsize = {0}", btrfsSb.leafsize); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.stripesize = {0}", btrfsSb.stripesize); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.n = {0}", btrfsSb.n); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.chunk_root_generation = {0}", + btrfsSb.chunk_root_generation); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.compat_flags = 0x{0:X16}", btrfsSb.compat_flags); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.compat_ro_flags = 0x{0:X16}", btrfsSb.compat_ro_flags); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.incompat_flags = 0x{0:X16}", btrfsSb.incompat_flags); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.csum_type = {0}", btrfsSb.csum_type); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.root_level = {0}", btrfsSb.root_level); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.chunk_root_level = {0}", btrfsSb.chunk_root_level); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.log_root_level = {0}", btrfsSb.log_root_level); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.id = 0x{0:X16}", btrfsSb.dev_item.id); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.bytes = {0}", btrfsSb.dev_item.bytes); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.used = {0}", btrfsSb.dev_item.used); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.optimal_align = {0}", + btrfsSb.dev_item.optimal_align); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.optimal_width = {0}", + btrfsSb.dev_item.optimal_width); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.minimal_size = {0}", + btrfsSb.dev_item.minimal_size); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.type = {0}", btrfsSb.dev_item.type); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.generation = {0}", + btrfsSb.dev_item.generation); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.start_offset = {0}", + btrfsSb.dev_item.start_offset); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.dev_group = {0}", btrfsSb.dev_item.dev_group); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.seek_speed = {0}", + btrfsSb.dev_item.seek_speed); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.bandwitdh = {0}", btrfsSb.dev_item.bandwitdh); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.device_uuid = {0}", + btrfsSb.dev_item.device_uuid); + + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.uuid = {0}", btrfsSb.dev_item.uuid); + AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.label = {0}", btrfsSb.label); + + sbInformation.AppendLine("B-tree filesystem"); + sbInformation.AppendFormat("UUID: {0}", btrfsSb.uuid).AppendLine(); + sbInformation.AppendFormat("This superblock resides on physical block {0}", btrfsSb.pba).AppendLine(); + sbInformation.AppendFormat("Root tree starts at LBA {0}", btrfsSb.root_lba).AppendLine(); + sbInformation.AppendFormat("Chunk tree starts at LBA {0}", btrfsSb.chunk_lba).AppendLine(); + sbInformation.AppendFormat("Log tree starts at LBA {0}", btrfsSb.log_lba).AppendLine(); + + sbInformation.AppendFormat("Volume has {0} bytes spanned in {1} devices", btrfsSb.total_bytes, + btrfsSb.num_devices).AppendLine(); + + sbInformation.AppendFormat("Volume has {0} bytes used", btrfsSb.bytes_used).AppendLine(); + sbInformation.AppendFormat("{0} bytes/sector", btrfsSb.sectorsize).AppendLine(); + sbInformation.AppendFormat("{0} bytes/node", btrfsSb.nodesize).AppendLine(); + sbInformation.AppendFormat("{0} bytes/leaf", btrfsSb.leafsize).AppendLine(); + sbInformation.AppendFormat("{0} bytes/stripe", btrfsSb.stripesize).AppendLine(); + sbInformation.AppendFormat("Flags: 0x{0:X}", btrfsSb.flags).AppendLine(); + sbInformation.AppendFormat("Compatible flags: 0x{0:X}", btrfsSb.compat_flags).AppendLine(); + sbInformation.AppendFormat("Read-only compatible flags: 0x{0:X}", btrfsSb.compat_ro_flags).AppendLine(); + sbInformation.AppendFormat("Incompatible flags: 0x{0:X}", btrfsSb.incompat_flags).AppendLine(); + sbInformation.AppendFormat("Device's UUID: {0}", btrfsSb.dev_item.uuid).AppendLine(); + sbInformation.AppendFormat("Volume label: {0}", btrfsSb.label).AppendLine(); + + information = sbInformation.ToString(); + + XmlFsType = new FileSystemType { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - var sbInformation = new StringBuilder(); - XmlFsType = new FileSystemType(); - information = ""; + Clusters = btrfsSb.total_bytes / btrfsSb.sectorsize, + ClusterSize = btrfsSb.sectorsize, + FreeClustersSpecified = true, + VolumeName = btrfsSb.label, + VolumeSerial = $"{btrfsSb.uuid}", + VolumeSetIdentifier = $"{btrfsSb.dev_item.device_uuid}", + Type = Name + }; - ulong sbSectorOff = 0x10000 / imagePlugin.Info.SectorSize; - uint sbSectorSize = 0x1000 / imagePlugin.Info.SectorSize; + XmlFsType.FreeClusters = XmlFsType.Clusters - (btrfsSb.bytes_used / btrfsSb.sectorsize); + } - ErrorNumber errno = imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSectorSize, out byte[] sector); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] + public readonly byte[] checksum; + public readonly Guid uuid; + public readonly ulong pba; + public readonly ulong flags; + public readonly ulong magic; + public readonly ulong generation; + public readonly ulong root_lba; + public readonly ulong chunk_lba; + public readonly ulong log_lba; + public readonly ulong log_root_transid; + public readonly ulong total_bytes; + public readonly ulong bytes_used; + public readonly ulong root_dir_objectid; + public readonly ulong num_devices; + public readonly uint sectorsize; + public readonly uint nodesize; + public readonly uint leafsize; + public readonly uint stripesize; + public readonly uint n; + public readonly ulong chunk_root_generation; + public readonly ulong compat_flags; + public readonly ulong compat_ro_flags; + public readonly ulong incompat_flags; + public readonly ushort csum_type; + public readonly byte root_level; + public readonly byte chunk_root_level; + public readonly byte log_root_level; + public readonly DevItem dev_item; + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x100)] + public readonly string label; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x100)] + public readonly byte[] reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x800)] + public readonly byte[] chunkpairs; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4D5)] + public readonly byte[] unused; + } - if(errno != ErrorNumber.NoError) - return; - - SuperBlock btrfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.checksum = {0}", btrfsSb.checksum); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.uuid = {0}", btrfsSb.uuid); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.pba = {0}", btrfsSb.pba); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.flags = {0}", btrfsSb.flags); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.magic = {0}", btrfsSb.magic); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.generation = {0}", btrfsSb.generation); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.root_lba = {0}", btrfsSb.root_lba); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.chunk_lba = {0}", btrfsSb.chunk_lba); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.log_lba = {0}", btrfsSb.log_lba); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.log_root_transid = {0}", btrfsSb.log_root_transid); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.total_bytes = {0}", btrfsSb.total_bytes); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.bytes_used = {0}", btrfsSb.bytes_used); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.root_dir_objectid = {0}", btrfsSb.root_dir_objectid); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.num_devices = {0}", btrfsSb.num_devices); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.sectorsize = {0}", btrfsSb.sectorsize); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.nodesize = {0}", btrfsSb.nodesize); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.leafsize = {0}", btrfsSb.leafsize); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.stripesize = {0}", btrfsSb.stripesize); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.n = {0}", btrfsSb.n); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.chunk_root_generation = {0}", - btrfsSb.chunk_root_generation); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.compat_flags = 0x{0:X16}", btrfsSb.compat_flags); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.compat_ro_flags = 0x{0:X16}", btrfsSb.compat_ro_flags); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.incompat_flags = 0x{0:X16}", btrfsSb.incompat_flags); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.csum_type = {0}", btrfsSb.csum_type); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.root_level = {0}", btrfsSb.root_level); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.chunk_root_level = {0}", btrfsSb.chunk_root_level); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.log_root_level = {0}", btrfsSb.log_root_level); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.id = 0x{0:X16}", btrfsSb.dev_item.id); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.bytes = {0}", btrfsSb.dev_item.bytes); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.used = {0}", btrfsSb.dev_item.used); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.optimal_align = {0}", - btrfsSb.dev_item.optimal_align); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.optimal_width = {0}", - btrfsSb.dev_item.optimal_width); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.minimal_size = {0}", - btrfsSb.dev_item.minimal_size); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.type = {0}", btrfsSb.dev_item.type); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.generation = {0}", - btrfsSb.dev_item.generation); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.start_offset = {0}", - btrfsSb.dev_item.start_offset); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.dev_group = {0}", btrfsSb.dev_item.dev_group); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.seek_speed = {0}", - btrfsSb.dev_item.seek_speed); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.bandwitdh = {0}", btrfsSb.dev_item.bandwitdh); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.device_uuid = {0}", - btrfsSb.dev_item.device_uuid); - - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.dev_item.uuid = {0}", btrfsSb.dev_item.uuid); - AaruConsole.DebugWriteLine("BTRFS Plugin", "btrfsSb.label = {0}", btrfsSb.label); - - sbInformation.AppendLine("B-tree filesystem"); - sbInformation.AppendFormat("UUID: {0}", btrfsSb.uuid).AppendLine(); - sbInformation.AppendFormat("This superblock resides on physical block {0}", btrfsSb.pba).AppendLine(); - sbInformation.AppendFormat("Root tree starts at LBA {0}", btrfsSb.root_lba).AppendLine(); - sbInformation.AppendFormat("Chunk tree starts at LBA {0}", btrfsSb.chunk_lba).AppendLine(); - sbInformation.AppendFormat("Log tree starts at LBA {0}", btrfsSb.log_lba).AppendLine(); - - sbInformation.AppendFormat("Volume has {0} bytes spanned in {1} devices", btrfsSb.total_bytes, - btrfsSb.num_devices).AppendLine(); - - sbInformation.AppendFormat("Volume has {0} bytes used", btrfsSb.bytes_used).AppendLine(); - sbInformation.AppendFormat("{0} bytes/sector", btrfsSb.sectorsize).AppendLine(); - sbInformation.AppendFormat("{0} bytes/node", btrfsSb.nodesize).AppendLine(); - sbInformation.AppendFormat("{0} bytes/leaf", btrfsSb.leafsize).AppendLine(); - sbInformation.AppendFormat("{0} bytes/stripe", btrfsSb.stripesize).AppendLine(); - sbInformation.AppendFormat("Flags: 0x{0:X}", btrfsSb.flags).AppendLine(); - sbInformation.AppendFormat("Compatible flags: 0x{0:X}", btrfsSb.compat_flags).AppendLine(); - sbInformation.AppendFormat("Read-only compatible flags: 0x{0:X}", btrfsSb.compat_ro_flags).AppendLine(); - sbInformation.AppendFormat("Incompatible flags: 0x{0:X}", btrfsSb.incompat_flags).AppendLine(); - sbInformation.AppendFormat("Device's UUID: {0}", btrfsSb.dev_item.uuid).AppendLine(); - sbInformation.AppendFormat("Volume label: {0}", btrfsSb.label).AppendLine(); - - information = sbInformation.ToString(); - - XmlFsType = new FileSystemType - { - Clusters = btrfsSb.total_bytes / btrfsSb.sectorsize, - ClusterSize = btrfsSb.sectorsize, - FreeClustersSpecified = true, - VolumeName = btrfsSb.label, - VolumeSerial = $"{btrfsSb.uuid}", - VolumeSetIdentifier = $"{btrfsSb.dev_item.device_uuid}", - Type = Name - }; - - XmlFsType.FreeClusters = XmlFsType.Clusters - (btrfsSb.bytes_used / btrfsSb.sectorsize); - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)] - public readonly byte[] checksum; - public readonly Guid uuid; - public readonly ulong pba; - public readonly ulong flags; - public readonly ulong magic; - public readonly ulong generation; - public readonly ulong root_lba; - public readonly ulong chunk_lba; - public readonly ulong log_lba; - public readonly ulong log_root_transid; - public readonly ulong total_bytes; - public readonly ulong bytes_used; - public readonly ulong root_dir_objectid; - public readonly ulong num_devices; - public readonly uint sectorsize; - public readonly uint nodesize; - public readonly uint leafsize; - public readonly uint stripesize; - public readonly uint n; - public readonly ulong chunk_root_generation; - public readonly ulong compat_flags; - public readonly ulong compat_ro_flags; - public readonly ulong incompat_flags; - public readonly ushort csum_type; - public readonly byte root_level; - public readonly byte chunk_root_level; - public readonly byte log_root_level; - public readonly DevItem dev_item; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x100)] - public readonly string label; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x100)] - public readonly byte[] reserved; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x800)] - public readonly byte[] chunkpairs; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x4D5)] - public readonly byte[] unused; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DevItem - { - public readonly ulong id; - public readonly ulong bytes; - public readonly ulong used; - public readonly uint optimal_align; - public readonly uint optimal_width; - public readonly uint minimal_size; - public readonly ulong type; - public readonly ulong generation; - public readonly ulong start_offset; - public readonly uint dev_group; - public readonly byte seek_speed; - public readonly byte bandwitdh; - public readonly Guid device_uuid; - public readonly Guid uuid; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DevItem + { + public readonly ulong id; + public readonly ulong bytes; + public readonly ulong used; + public readonly uint optimal_align; + public readonly uint optimal_width; + public readonly uint minimal_size; + public readonly ulong type; + public readonly ulong generation; + public readonly ulong start_offset; + public readonly uint dev_group; + public readonly byte seek_speed; + public readonly byte bandwitdh; + public readonly Guid device_uuid; + public readonly Guid uuid; } } \ No newline at end of file diff --git a/Aaru.Filesystems/CBM.cs b/Aaru.Filesystems/CBM.cs index 3b12e3354..3b13672ce 100644 --- a/Aaru.Filesystems/CBM.cs +++ b/Aaru.Filesystems/CBM.cs @@ -42,229 +42,228 @@ using Schemas; using Encoding = System.Text.Encoding; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the filesystem used in 8-bit Commodore microcomputers +public sealed class CBM : IFilesystem { /// - /// Implements detection of the filesystem used in 8-bit Commodore microcomputers - public sealed class CBM : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public string Name => "Commodore file system"; + /// + public Guid Id => new("D104744E-A376-450C-BAC0-1347C93F983B"); + /// + public Encoding Encoding { get; private set; } + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Name => "Commodore file system"; - /// - public Guid Id => new("D104744E-A376-450C-BAC0-1347C93F983B"); - /// - public Encoding Encoding { get; private set; } - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(partition.Start > 0) - return false; - - if(imagePlugin.Info.SectorSize != 256) - return false; - - if(imagePlugin.Info.Sectors != 683 && - imagePlugin.Info.Sectors != 768 && - imagePlugin.Info.Sectors != 1366 && - imagePlugin.Info.Sectors != 3200) - return false; - - byte[] sector; - - if(imagePlugin.Info.Sectors == 3200) - { - ErrorNumber errno = imagePlugin.ReadSector(1560, out sector); - - if(errno != ErrorNumber.NoError) - return false; - - Header cbmHdr = Marshal.ByteArrayToStructureLittleEndian
(sector); - - if(cbmHdr.diskDosVersion == 0x44 && - cbmHdr.dosVersion == 0x33 && - cbmHdr.diskVersion == 0x44) - return true; - } - else - { - ErrorNumber errno = imagePlugin.ReadSector(357, out sector); - - if(errno != ErrorNumber.NoError) - return false; - - BAM cbmBam = Marshal.ByteArrayToStructureLittleEndian(sector); - - if(cbmBam.dosVersion == 0x41 && - (cbmBam.doubleSided == 0x00 || cbmBam.doubleSided == 0x80) && - cbmBam.unused1 == 0x00 && - cbmBam.directoryTrack == 0x12) - return true; - } - + if(partition.Start > 0) return false; - } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + if(imagePlugin.Info.SectorSize != 256) + return false; + + if(imagePlugin.Info.Sectors != 683 && + imagePlugin.Info.Sectors != 768 && + imagePlugin.Info.Sectors != 1366 && + imagePlugin.Info.Sectors != 3200) + return false; + + byte[] sector; + + if(imagePlugin.Info.Sectors == 3200) { - Encoding = new PETSCII(); - information = ""; - byte[] sector; + ErrorNumber errno = imagePlugin.ReadSector(1560, out sector); - var sbInformation = new StringBuilder(); + if(errno != ErrorNumber.NoError) + return false; - sbInformation.AppendLine("Commodore file system"); + Header cbmHdr = Marshal.ByteArrayToStructureLittleEndian
(sector); - XmlFsType = new FileSystemType - { - Type = "Commodore file system", - Clusters = imagePlugin.Info.Sectors, - ClusterSize = 256 - }; - - if(imagePlugin.Info.Sectors == 3200) - { - ErrorNumber errno = imagePlugin.ReadSector(1560, out sector); - - if(errno != ErrorNumber.NoError) - return; - - Header cbmHdr = Marshal.ByteArrayToStructureLittleEndian
(sector); - - sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmHdr.directoryTrack, - cbmHdr.directorySector).AppendLine(); - - sbInformation.AppendFormat("Disk DOS Version: {0}", Encoding.ASCII.GetString(new[] - { - cbmHdr.diskDosVersion - })).AppendLine(); - - sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] - { - cbmHdr.dosVersion - })).AppendLine(); - - sbInformation.AppendFormat("Disk Version: {0}", Encoding.ASCII.GetString(new[] - { - cbmHdr.diskVersion - })).AppendLine(); - - sbInformation.AppendFormat("Disk ID: {0}", cbmHdr.diskId).AppendLine(); - - sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmHdr.name, Encoding)). - AppendLine(); - - XmlFsType.VolumeName = StringHandlers.CToString(cbmHdr.name, Encoding); - XmlFsType.VolumeSerial = $"{cbmHdr.diskId}"; - } - else - { - ErrorNumber errno = imagePlugin.ReadSector(357, out sector); - - if(errno != ErrorNumber.NoError) - return; - - BAM cbmBam = Marshal.ByteArrayToStructureLittleEndian(sector); - - sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmBam.directoryTrack, - cbmBam.directorySector).AppendLine(); - - sbInformation.AppendFormat("Disk DOS type: {0}", - Encoding.ASCII.GetString(BitConverter.GetBytes(cbmBam.dosType))). - AppendLine(); - - sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] - { - cbmBam.dosVersion - })).AppendLine(); - - sbInformation.AppendFormat("Disk ID: {0}", cbmBam.diskId).AppendLine(); - - sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmBam.name, Encoding)). - AppendLine(); - - XmlFsType.VolumeName = StringHandlers.CToString(cbmBam.name, Encoding); - XmlFsType.VolumeSerial = $"{cbmBam.diskId}"; - } - - information = sbInformation.ToString(); + if(cbmHdr.diskDosVersion == 0x44 && + cbmHdr.dosVersion == 0x33 && + cbmHdr.diskVersion == 0x44) + return true; } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BAM + else { - /// Track where directory starts - public readonly byte directoryTrack; - /// Sector where directory starts - public readonly byte directorySector; - /// Disk DOS version, 0x41 - public readonly byte dosVersion; - /// Set to 0x80 if 1571, 0x00 if not - public readonly byte doubleSided; - /// Block allocation map - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 140)] - public readonly byte[] bam; - /// Disk name - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] name; - /// Filled with 0xA0 - public readonly ushort fill1; - /// Disk ID - public readonly ushort diskId; - /// Filled with 0xA0 - public readonly byte fill2; - /// DOS type - public readonly ushort dosType; - /// Filled with 0xA0 - public readonly uint fill3; - /// Unused - public readonly byte unused1; - /// Block allocation map for Dolphin DOS extended tracks - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly byte[] dolphinBam; - /// Block allocation map for Speed DOS extended tracks - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly byte[] speedBam; - /// Unused - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] - public readonly byte[] unused2; - /// Free sector count for second side in 1571 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] - public readonly byte[] freeCount; + ErrorNumber errno = imagePlugin.ReadSector(357, out sector); + + if(errno != ErrorNumber.NoError) + return false; + + BAM cbmBam = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(cbmBam.dosVersion == 0x41 && + (cbmBam.doubleSided == 0x00 || cbmBam.doubleSided == 0x80) && + cbmBam.unused1 == 0x00 && + cbmBam.directoryTrack == 0x12) + return true; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Header + return false; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = new PETSCII(); + information = ""; + byte[] sector; + + var sbInformation = new StringBuilder(); + + sbInformation.AppendLine("Commodore file system"); + + XmlFsType = new FileSystemType { - /// Track where directory starts - public readonly byte directoryTrack; - /// Sector where directory starts - public readonly byte directorySector; - /// Disk DOS version, 0x44 - public readonly byte diskDosVersion; - /// Unusued - public readonly byte unused1; - /// Disk name - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] name; - /// Filled with 0xA0 - public readonly ushort fill1; - /// Disk ID - public readonly ushort diskId; - /// Filled with 0xA0 - public readonly byte fill2; - /// DOS version ('3') - public readonly byte dosVersion; - /// Disk version ('D') - public readonly byte diskVersion; - /// Filled with 0xA0 - public readonly short fill3; + Type = "Commodore file system", + Clusters = imagePlugin.Info.Sectors, + ClusterSize = 256 + }; + + if(imagePlugin.Info.Sectors == 3200) + { + ErrorNumber errno = imagePlugin.ReadSector(1560, out sector); + + if(errno != ErrorNumber.NoError) + return; + + Header cbmHdr = Marshal.ByteArrayToStructureLittleEndian
(sector); + + sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmHdr.directoryTrack, + cbmHdr.directorySector).AppendLine(); + + sbInformation.AppendFormat("Disk DOS Version: {0}", Encoding.ASCII.GetString(new[] + { + cbmHdr.diskDosVersion + })).AppendLine(); + + sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] + { + cbmHdr.dosVersion + })).AppendLine(); + + sbInformation.AppendFormat("Disk Version: {0}", Encoding.ASCII.GetString(new[] + { + cbmHdr.diskVersion + })).AppendLine(); + + sbInformation.AppendFormat("Disk ID: {0}", cbmHdr.diskId).AppendLine(); + + sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmHdr.name, Encoding)). + AppendLine(); + + XmlFsType.VolumeName = StringHandlers.CToString(cbmHdr.name, Encoding); + XmlFsType.VolumeSerial = $"{cbmHdr.diskId}"; } + else + { + ErrorNumber errno = imagePlugin.ReadSector(357, out sector); + + if(errno != ErrorNumber.NoError) + return; + + BAM cbmBam = Marshal.ByteArrayToStructureLittleEndian(sector); + + sbInformation.AppendFormat("Directory starts at track {0} sector {1}", cbmBam.directoryTrack, + cbmBam.directorySector).AppendLine(); + + sbInformation.AppendFormat("Disk DOS type: {0}", + Encoding.ASCII.GetString(BitConverter.GetBytes(cbmBam.dosType))). + AppendLine(); + + sbInformation.AppendFormat("DOS Version: {0}", Encoding.ASCII.GetString(new[] + { + cbmBam.dosVersion + })).AppendLine(); + + sbInformation.AppendFormat("Disk ID: {0}", cbmBam.diskId).AppendLine(); + + sbInformation.AppendFormat("Disk name: {0}", StringHandlers.CToString(cbmBam.name, Encoding)). + AppendLine(); + + XmlFsType.VolumeName = StringHandlers.CToString(cbmBam.name, Encoding); + XmlFsType.VolumeSerial = $"{cbmBam.diskId}"; + } + + information = sbInformation.ToString(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BAM + { + /// Track where directory starts + public readonly byte directoryTrack; + /// Sector where directory starts + public readonly byte directorySector; + /// Disk DOS version, 0x41 + public readonly byte dosVersion; + /// Set to 0x80 if 1571, 0x00 if not + public readonly byte doubleSided; + /// Block allocation map + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 140)] + public readonly byte[] bam; + /// Disk name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] name; + /// Filled with 0xA0 + public readonly ushort fill1; + /// Disk ID + public readonly ushort diskId; + /// Filled with 0xA0 + public readonly byte fill2; + /// DOS type + public readonly ushort dosType; + /// Filled with 0xA0 + public readonly uint fill3; + /// Unused + public readonly byte unused1; + /// Block allocation map for Dolphin DOS extended tracks + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly byte[] dolphinBam; + /// Block allocation map for Speed DOS extended tracks + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly byte[] speedBam; + /// Unused + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] + public readonly byte[] unused2; + /// Free sector count for second side in 1571 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 9)] + public readonly byte[] freeCount; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Header + { + /// Track where directory starts + public readonly byte directoryTrack; + /// Sector where directory starts + public readonly byte directorySector; + /// Disk DOS version, 0x44 + public readonly byte diskDosVersion; + /// Unusued + public readonly byte unused1; + /// Disk name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] name; + /// Filled with 0xA0 + public readonly ushort fill1; + /// Disk ID + public readonly ushort diskId; + /// Filled with 0xA0 + public readonly byte fill2; + /// DOS version ('3') + public readonly byte dosVersion; + /// Disk version ('D') + public readonly byte diskVersion; + /// Filled with 0xA0 + public readonly short fill3; } } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/CPM.cs b/Aaru.Filesystems/CPM/CPM.cs index d84b2690c..e62982bd6 100644 --- a/Aaru.Filesystems/CPM/CPM.cs +++ b/Aaru.Filesystems/CPM/CPM.cs @@ -37,74 +37,73 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements the CP/M filesystem +public sealed partial class CPM : IReadOnlyFilesystem { + /// True if thinks this is a CP/M filesystem + bool _cpmFound; + + /// Cached + FileSystemInfo _cpmStat; + + /// Cached file passwords, decoded + Dictionary _decodedPasswordCache; + + /// Stores all known CP/M disk definitions + CpmDefinitions _definitions; + IMediaImage _device; + /// Cached directory listing + List _dirList; + /// CP/M disc parameter block (on-memory) + DiscParameterBlock _dpb; + /// Cached file data + Dictionary _fileCache; + /// The volume label, if the CP/M filesystem contains one + string _label; + /// Timestamp in volume label for creation + byte[] _labelCreationDate; + /// Timestamp in volume label for update + byte[] _labelUpdateDate; + bool _mounted; + /// Cached file passwords + Dictionary _passwordCache; + /// Sector deinterleaving mask + int[] _sectorMask; + /// True if there are CP/M 3 timestamps + bool _standardTimestamps; + /// Cached file + Dictionary _statCache; + /// True if there are timestamps in Z80DOS or DOS+ format + bool _thirdPartyTimestamps; + /// If thinks this is a CP/M filesystem, this is the definition for it + CpmDefinition _workingDefinition; + /// - /// Implements the CP/M filesystem - public sealed partial class CPM : IReadOnlyFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "CP/M File System"; + /// + public Guid Id => new("AA2B8585-41DF-4E3B-8A35-D1A935E2F8A1"); + /// + public string Author => "Natalia Portillo"; + + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + {}; + + /// + public Dictionary Namespaces => null; + + static Dictionary GetDefaultOptions() => new() { - /// True if thinks this is a CP/M filesystem - bool _cpmFound; - - /// Cached - FileSystemInfo _cpmStat; - - /// Cached file passwords, decoded - Dictionary _decodedPasswordCache; - - /// Stores all known CP/M disk definitions - CpmDefinitions _definitions; - IMediaImage _device; - /// Cached directory listing - List _dirList; - /// CP/M disc parameter block (on-memory) - DiscParameterBlock _dpb; - /// Cached file data - Dictionary _fileCache; - /// The volume label, if the CP/M filesystem contains one - string _label; - /// Timestamp in volume label for creation - byte[] _labelCreationDate; - /// Timestamp in volume label for update - byte[] _labelUpdateDate; - bool _mounted; - /// Cached file passwords - Dictionary _passwordCache; - /// Sector deinterleaving mask - int[] _sectorMask; - /// True if there are CP/M 3 timestamps - bool _standardTimestamps; - /// Cached file - Dictionary _statCache; - /// True if there are timestamps in Z80DOS or DOS+ format - bool _thirdPartyTimestamps; - /// If thinks this is a CP/M filesystem, this is the definition for it - CpmDefinition _workingDefinition; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "CP/M File System"; - /// - public Guid Id => new("AA2B8585-41DF-4E3B-8A35-D1A935E2F8A1"); - /// - public string Author => "Natalia Portillo"; - - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - {}; - - /// - public Dictionary Namespaces => null; - - static Dictionary GetDefaultOptions() => new() { - { - "debug", false.ToString() - } - }; - } + "debug", false.ToString() + } + }; } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/Consts.cs b/Aaru.Filesystems/CPM/Consts.cs index 7886e772f..a021bf068 100644 --- a/Aaru.Filesystems/CPM/Consts.cs +++ b/Aaru.Filesystems/CPM/Consts.cs @@ -32,33 +32,32 @@ // ReSharper disable InconsistentNaming -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class CPM { - public sealed partial class CPM + /// Enumerates the format identification byte used by CP/M-86 + enum FormatByte : byte { - /// Enumerates the format identification byte used by CP/M-86 - enum FormatByte : byte - { - /// 5.25" double-density single-side 8 sectors/track - k160 = 0, - /// 5.25" double-density double-side 8 sectors/track - k320 = 1, - /// 5.25" double-density double-side 9 sectors/track - k360 = 0x10, - /// 5.25" double-density double-side 9 sectors/track - k360Alt = 0x40, - /// 3.5" double-density double-side 9 sectors/track - k720 = 0x11, - /// 3.5" double-density double-side 9 sectors/track using FEAT144 - f720 = 0x48, - /// 5.25" high-density double-side 15 sectors/track using FEAT144 - f1200 = 0x0C, - /// 3.5" high-density double-side 18 sectors/track using FEAT144 - f1440 = 0x90, - /// 5.25" double-density double-side 9 sectors/track - k360Alt2 = 0x26, - /// 3.5" double-density double-side 9 sectors/track - k720Alt = 0x94 - } + /// 5.25" double-density single-side 8 sectors/track + k160 = 0, + /// 5.25" double-density double-side 8 sectors/track + k320 = 1, + /// 5.25" double-density double-side 9 sectors/track + k360 = 0x10, + /// 5.25" double-density double-side 9 sectors/track + k360Alt = 0x40, + /// 3.5" double-density double-side 9 sectors/track + k720 = 0x11, + /// 3.5" double-density double-side 9 sectors/track using FEAT144 + f720 = 0x48, + /// 5.25" high-density double-side 15 sectors/track using FEAT144 + f1200 = 0x0C, + /// 3.5" high-density double-side 18 sectors/track using FEAT144 + f1440 = 0x90, + /// 5.25" double-density double-side 9 sectors/track + k360Alt2 = 0x26, + /// 3.5" double-density double-side 9 sectors/track + k720Alt = 0x94 } } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/Definitions.cs b/Aaru.Filesystems/CPM/Definitions.cs index 2eb069e11..990de5281 100644 --- a/Aaru.Filesystems/CPM/Definitions.cs +++ b/Aaru.Filesystems/CPM/Definitions.cs @@ -38,136 +38,135 @@ using System.Reflection; using System.Xml; using System.Xml.Serialization; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class CPM { - public sealed partial class CPM + /// Loads all the known CP/M disk definitions from an XML stored as an embedded resource. + /// The definitions. + bool LoadDefinitions() { - /// Loads all the known CP/M disk definitions from an XML stored as an embedded resource. - /// The definitions. - bool LoadDefinitions() + try { - try + var defsReader = + XmlReader.Create(Assembly.GetExecutingAssembly(). + GetManifestResourceStream("Aaru.Filesystems.CPM.cpmdefs.xml") ?? + new MemoryStream()); + + var defsSerializer = new XmlSerializer(typeof(CpmDefinitions)); + _definitions = (CpmDefinitions)defsSerializer.Deserialize(defsReader); + + // Patch definitions + foreach(CpmDefinition def in _definitions.definitions) { - var defsReader = - XmlReader.Create(Assembly.GetExecutingAssembly(). - GetManifestResourceStream("Aaru.Filesystems.CPM.cpmdefs.xml") ?? - new MemoryStream()); - - var defsSerializer = new XmlSerializer(typeof(CpmDefinitions)); - _definitions = (CpmDefinitions)defsSerializer.Deserialize(defsReader); - - // Patch definitions - foreach(CpmDefinition def in _definitions.definitions) + if(def.side1 == null) { - if(def.side1 == null) + def.side1 = new Side { - def.side1 = new Side - { - sideId = 0, - sectorIds = new int[def.sectorsPerTrack] - }; + sideId = 0, + sectorIds = new int[def.sectorsPerTrack] + }; - for(int i = 0; i < def.sectorsPerTrack; i++) - def.side1.sectorIds[i] = i + 1; - } - - if(def.sides != 2 || - def.side2 != null) - continue; - - { - def.side2 = new Side - { - sideId = 1, - sectorIds = new int[def.sectorsPerTrack] - }; - - for(int i = 0; i < def.sectorsPerTrack; i++) - def.side2.sectorIds[i] = i + 1; - } + for(int i = 0; i < def.sectorsPerTrack; i++) + def.side1.sectorIds[i] = i + 1; } - return true; - } - catch - { - return false; + if(def.sides != 2 || + def.side2 != null) + continue; + + { + def.side2 = new Side + { + sideId = 1, + sectorIds = new int[def.sectorsPerTrack] + }; + + for(int i = 0; i < def.sectorsPerTrack; i++) + def.side2.sectorIds[i] = i + 1; + } } + + return true; + } + catch + { + return false; } } +} - /// CP/M disk definitions - [SuppressMessage("ReSharper", "InconsistentNaming")] - public sealed class CpmDefinitions - { - /// Timestamp of creation of the CP/M disk definitions list - public DateTime creation; - /// List of all CP/M disk definitions - public List definitions; - } +/// CP/M disk definitions +[SuppressMessage("ReSharper", "InconsistentNaming")] +public sealed class CpmDefinitions +{ + /// Timestamp of creation of the CP/M disk definitions list + public DateTime creation; + /// List of all CP/M disk definitions + public List definitions; +} - /// CP/M disk definition - [SuppressMessage("ReSharper", "InconsistentNaming")] - public sealed class CpmDefinition - { - /// Maps the first 16 allocation blocks for reservation, high byte - public int al0; - /// Maps the first 16 allocation blocks for reservation, low byte - public int al1; - /// Controller bitrate - public string bitrate; - /// Block mask for - public int blm; - /// Left shifts needed to translate allocation block number to lba - public int bsh; - /// Physical bytes per sector - public int bytesPerSector; - /// Comment and description - public string comment; - /// If true, all bytes written on disk are negated - public bool complement; - /// Total cylinders - public int cylinders; - /// Total number of available directory entries - public int drm; - /// Total number of 128 byte records on disk - public int dsm; - /// Encoding, "FM", "MFM", "GCR" - public string encoding; - /// Absolutely unknown? - public bool evenOdd; - /// Extent mask - public int exm; - /// Disk definition label - public string label; - /// Tracks at the beginning of disk reserved for BIOS/BDOS - public int ofs; - /// - /// Cylinder/side ordering. SIDES = change side after each track, CYLINDERS = change side after whole side, EAGLE - /// and COLUMBIA unknown - /// - public string order; - /// Physical sectors per side - public int sectorsPerTrack; - /// Description of controller's side 0 (usually, upper side) - public Side side1; - /// Description of controller's side 1 (usually, lower side) - public Side side2; - /// Total sides - public int sides; - /// Physical sector interleaving - public int skew; - /// Sectors at the beginning of disk reserved for BIOS/BDOS - public int sofs; - } +/// CP/M disk definition +[SuppressMessage("ReSharper", "InconsistentNaming")] +public sealed class CpmDefinition +{ + /// Maps the first 16 allocation blocks for reservation, high byte + public int al0; + /// Maps the first 16 allocation blocks for reservation, low byte + public int al1; + /// Controller bitrate + public string bitrate; + /// Block mask for + public int blm; + /// Left shifts needed to translate allocation block number to lba + public int bsh; + /// Physical bytes per sector + public int bytesPerSector; + /// Comment and description + public string comment; + /// If true, all bytes written on disk are negated + public bool complement; + /// Total cylinders + public int cylinders; + /// Total number of available directory entries + public int drm; + /// Total number of 128 byte records on disk + public int dsm; + /// Encoding, "FM", "MFM", "GCR" + public string encoding; + /// Absolutely unknown? + public bool evenOdd; + /// Extent mask + public int exm; + /// Disk definition label + public string label; + /// Tracks at the beginning of disk reserved for BIOS/BDOS + public int ofs; + /// + /// Cylinder/side ordering. SIDES = change side after each track, CYLINDERS = change side after whole side, EAGLE + /// and COLUMBIA unknown + /// + public string order; + /// Physical sectors per side + public int sectorsPerTrack; + /// Description of controller's side 0 (usually, upper side) + public Side side1; + /// Description of controller's side 1 (usually, lower side) + public Side side2; + /// Total sides + public int sides; + /// Physical sector interleaving + public int skew; + /// Sectors at the beginning of disk reserved for BIOS/BDOS + public int sofs; +} - /// Side descriptions - [SuppressMessage("ReSharper", "InconsistentNaming")] - public sealed class Side - { - /// Software interleaving mask, [1,3,0,2] means CP/M LBA 0 is physical sector 1, LBA 1 = 3, so on - public int[] sectorIds; - /// Side ID as found in each sector address mark - public int sideId; - } +/// Side descriptions +[SuppressMessage("ReSharper", "InconsistentNaming")] +public sealed class Side +{ + /// Software interleaving mask, [1,3,0,2] means CP/M LBA 0 is physical sector 1, LBA 1 = 3, so on + public int[] sectorIds; + /// Side ID as found in each sector address mark + public int sideId; } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/Dir.cs b/Aaru.Filesystems/CPM/Dir.cs index 987158805..d5afbb0f8 100644 --- a/Aaru.Filesystems/CPM/Dir.cs +++ b/Aaru.Filesystems/CPM/Dir.cs @@ -37,93 +37,92 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class CPM { - public sealed partial class CPM + /// + public ErrorNumber ReadDir(string path, out List contents) { - /// - public ErrorNumber ReadDir(string path, out List contents) + contents = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(!string.IsNullOrEmpty(path) && + string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) + return ErrorNumber.NotSupported; + + contents = new List(_dirList); + + return ErrorNumber.NoError; + } + + /// + /// Checks that the given directory blocks follow the CP/M filesystem directory specification Corrupted + /// directories will fail. FAT directories will false positive if all files start with 0x05, and do not use full + /// extensions, for example: "σAFILE.GZ" (using code page 437) + /// + /// False if the directory does not follow the directory specification + /// Directory blocks. + bool CheckDir(byte[] directory) + { + try { - contents = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - if(!string.IsNullOrEmpty(path) && - string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) - return ErrorNumber.NotSupported; - - contents = new List(_dirList); - - return ErrorNumber.NoError; - } - - /// - /// Checks that the given directory blocks follow the CP/M filesystem directory specification Corrupted - /// directories will fail. FAT directories will false positive if all files start with 0x05, and do not use full - /// extensions, for example: "σAFILE.GZ" (using code page 437) - /// - /// False if the directory does not follow the directory specification - /// Directory blocks. - bool CheckDir(byte[] directory) - { - try - { - if(directory == null) - return false; - - int fileCount = 0; - - for(int off = 0; off < directory.Length; off += 32) - { - DirectoryEntry entry = Marshal.ByteArrayToStructureLittleEndian(directory, off, 32); - - if((entry.statusUser & 0x7F) < 0x20) - { - for(int f = 0; f < 8; f++) - if(entry.filename[f] < 0x20 && - entry.filename[f] != 0x00) - return false; - - for(int e = 0; e < 3; e++) - if(entry.extension[e] < 0x20 && - entry.extension[e] != 0x00) - return false; - - if(!ArrayHelpers.ArrayIsNullOrWhiteSpace(entry.filename)) - fileCount++; - } - else if(entry.statusUser == 0x20) - { - for(int f = 0; f < 8; f++) - if(entry.filename[f] < 0x20 && - entry.filename[f] != 0x00) - return false; - - for(int e = 0; e < 3; e++) - if(entry.extension[e] < 0x20 && - entry.extension[e] != 0x00) - return false; - - _label = Encoding.ASCII.GetString(directory, off + 1, 11).Trim(); - _labelCreationDate = new byte[4]; - _labelUpdateDate = new byte[4]; - Array.Copy(directory, off + 24, _labelCreationDate, 0, 4); - Array.Copy(directory, off + 28, _labelUpdateDate, 0, 4); - } - else if(entry.statusUser == 0x21) - if(directory[off + 1] == 0x00) - _thirdPartyTimestamps = true; - else - _standardTimestamps |= directory[off + 21] == 0x00 && directory[off + 31] == 0x00; - } - - return fileCount > 0; - } - catch - { + if(directory == null) return false; + + int fileCount = 0; + + for(int off = 0; off < directory.Length; off += 32) + { + DirectoryEntry entry = Marshal.ByteArrayToStructureLittleEndian(directory, off, 32); + + if((entry.statusUser & 0x7F) < 0x20) + { + for(int f = 0; f < 8; f++) + if(entry.filename[f] < 0x20 && + entry.filename[f] != 0x00) + return false; + + for(int e = 0; e < 3; e++) + if(entry.extension[e] < 0x20 && + entry.extension[e] != 0x00) + return false; + + if(!ArrayHelpers.ArrayIsNullOrWhiteSpace(entry.filename)) + fileCount++; + } + else if(entry.statusUser == 0x20) + { + for(int f = 0; f < 8; f++) + if(entry.filename[f] < 0x20 && + entry.filename[f] != 0x00) + return false; + + for(int e = 0; e < 3; e++) + if(entry.extension[e] < 0x20 && + entry.extension[e] != 0x00) + return false; + + _label = Encoding.ASCII.GetString(directory, off + 1, 11).Trim(); + _labelCreationDate = new byte[4]; + _labelUpdateDate = new byte[4]; + Array.Copy(directory, off + 24, _labelCreationDate, 0, 4); + Array.Copy(directory, off + 28, _labelUpdateDate, 0, 4); + } + else if(entry.statusUser == 0x21) + if(directory[off + 1] == 0x00) + _thirdPartyTimestamps = true; + else + _standardTimestamps |= directory[off + 21] == 0x00 && directory[off + 31] == 0x00; } + + return fileCount > 0; + } + catch + { + return false; } } } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/File.cs b/Aaru.Filesystems/CPM/File.cs index e226f3b66..ebdfa3991 100644 --- a/Aaru.Filesystems/CPM/File.cs +++ b/Aaru.Filesystems/CPM/File.cs @@ -35,133 +35,132 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class CPM { - public sealed partial class CPM + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) { - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; + + if(string.IsNullOrEmpty(pathElements[0]) || + string.Compare(pathElements[0], "/", StringComparison.OrdinalIgnoreCase) == 0) { attributes = new FileAttributes(); - - if(!_mounted) - return ErrorNumber.AccessDenied; - - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - if(string.IsNullOrEmpty(pathElements[0]) || - string.Compare(pathElements[0], "/", StringComparison.OrdinalIgnoreCase) == 0) - { - attributes = new FileAttributes(); - attributes = FileAttributes.Directory; - - return ErrorNumber.NoError; - } - - if(!_statCache.TryGetValue(pathElements[0].ToUpperInvariant(), out FileEntryInfo fInfo)) - return ErrorNumber.NoSuchFile; - - attributes = fInfo.Attributes; + attributes = FileAttributes.Directory; return ErrorNumber.NoError; } - // TODO: Implementing this would require storing the interleaving - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + if(!_statCache.TryGetValue(pathElements[0].ToUpperInvariant(), out FileEntryInfo fInfo)) + return ErrorNumber.NoSuchFile; + + attributes = fInfo.Attributes; + + return ErrorNumber.NoError; + } + + // TODO: Implementing this would require storing the interleaving + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + { + deviceBlock = 0; + + return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotImplemented; + } + + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(size == 0) { - deviceBlock = 0; - - return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotImplemented; - } - - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) - { - if(!_mounted) - return ErrorNumber.AccessDenied; - - if(size == 0) - { - buf = Array.Empty(); - - return ErrorNumber.NoError; - } - - if(offset < 0) - return ErrorNumber.InvalidArgument; - - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - if(!_fileCache.TryGetValue(pathElements[0].ToUpperInvariant(), out byte[] file)) - return ErrorNumber.NoSuchFile; - - if(offset >= file.Length) - return ErrorNumber.EINVAL; - - if(size + offset >= file.Length) - size = file.Length - offset; - - buf = new byte[size]; - Array.Copy(file, offset, buf, 0, size); + buf = Array.Empty(); return ErrorNumber.NoError; } - /// - public ErrorNumber ReadLink(string path, out string dest) + if(offset < 0) + return ErrorNumber.InvalidArgument; + + string[] pathElements = path.Split(new[] { - dest = null; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotSupported; - } + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) + if(!_fileCache.TryGetValue(pathElements[0].ToUpperInvariant(), out byte[] file)) + return ErrorNumber.NoSuchFile; + + if(offset >= file.Length) + return ErrorNumber.EINVAL; + + if(size + offset >= file.Length) + size = file.Length - offset; + + buf = new byte[size]; + Array.Copy(file, offset, buf, 0, size); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber ReadLink(string path, out string dest) + { + dest = null; + + return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotSupported; + } + + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - stat = null; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + if(!string.IsNullOrEmpty(path) && + string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) + return _statCache.TryGetValue(pathElements[0].ToUpperInvariant(), out stat) ? ErrorNumber.NoError + : ErrorNumber.NoSuchFile; - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + stat = new FileEntryInfo + { + Attributes = FileAttributes.Directory, + BlockSize = XmlFsType.ClusterSize + }; - if(!string.IsNullOrEmpty(path) && - string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) - return _statCache.TryGetValue(pathElements[0].ToUpperInvariant(), out stat) ? ErrorNumber.NoError - : ErrorNumber.NoSuchFile; + if(_labelCreationDate != null) + stat.CreationTime = DateHandlers.CpmToDateTime(_labelCreationDate); - stat = new FileEntryInfo - { - Attributes = FileAttributes.Directory, - BlockSize = XmlFsType.ClusterSize - }; + if(_labelUpdateDate != null) + stat.StatusChangeTime = DateHandlers.CpmToDateTime(_labelUpdateDate); - if(_labelCreationDate != null) - stat.CreationTime = DateHandlers.CpmToDateTime(_labelCreationDate); - - if(_labelUpdateDate != null) - stat.StatusChangeTime = DateHandlers.CpmToDateTime(_labelUpdateDate); - - return ErrorNumber.NoError; - } + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/Info.cs b/Aaru.Filesystems/CPM/Info.cs index 796bb88ca..0f5781c38 100644 --- a/Aaru.Filesystems/CPM/Info.cs +++ b/Aaru.Filesystems/CPM/Info.cs @@ -41,214 +41,443 @@ using Aaru.Console; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class CPM { - public sealed partial class CPM + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = ErrorNumber.NoError; + + // This will only continue on devices with a chance to have ever been used by CP/M while failing on all others + // It's ugly, but will stop a lot of false positives + switch(imagePlugin.Info.MediaType) { - ErrorNumber errno = ErrorNumber.NoError; + case MediaType.Unknown: + case MediaType.Apple32SS: + case MediaType.Apple32DS: + case MediaType.Apple33SS: + case MediaType.Apple33DS: + case MediaType.DOS_525_SS_DD_8: + case MediaType.DOS_525_SS_DD_9: + case MediaType.DOS_525_DS_DD_8: + case MediaType.DOS_525_DS_DD_9: + case MediaType.DOS_525_HD: + case MediaType.DOS_35_SS_DD_8: + case MediaType.DOS_35_SS_DD_9: + case MediaType.DOS_35_DS_DD_8: + case MediaType.DOS_35_DS_DD_9: + case MediaType.DOS_35_HD: + case MediaType.DOS_35_ED: + case MediaType.IBM23FD: + case MediaType.IBM33FD_128: + case MediaType.IBM33FD_256: + case MediaType.IBM33FD_512: + case MediaType.IBM43FD_128: + case MediaType.IBM43FD_256: + case MediaType.IBM53FD_256: + case MediaType.IBM53FD_512: + case MediaType.IBM53FD_1024: + case MediaType.RX01: + case MediaType.RX02: + case MediaType.RX03: + case MediaType.RX50: + case MediaType.ACORN_525_SS_SD_40: + case MediaType.ACORN_525_SS_SD_80: + case MediaType.ACORN_525_SS_DD_40: + case MediaType.ACORN_525_SS_DD_80: + case MediaType.ACORN_525_DS_DD: + case MediaType.ATARI_525_SD: + case MediaType.ATARI_525_ED: + case MediaType.ATARI_525_DD: + case MediaType.CBM_35_DD: + case MediaType.CBM_1540: + case MediaType.CBM_1540_Ext: + case MediaType.CBM_1571: + case MediaType.NEC_8_SD: + case MediaType.NEC_8_DD: + case MediaType.NEC_525_SS: + case MediaType.NEC_525_DS: + case MediaType.NEC_525_HD: + case MediaType.NEC_35_HD_8: + case MediaType.NEC_35_HD_15: + case MediaType.SHARP_525_9: + case MediaType.SHARP_35_9: + case MediaType.ECMA_99_8: + case MediaType.ECMA_99_15: + case MediaType.ECMA_99_26: + case MediaType.ECMA_54: + case MediaType.ECMA_59: + case MediaType.ECMA_66: + case MediaType.ECMA_69_8: + case MediaType.ECMA_69_15: + case MediaType.ECMA_69_26: + case MediaType.ECMA_70: + case MediaType.ECMA_78: + case MediaType.ECMA_78_2: + case MediaType.Apricot_35: + case MediaType.CompactFloppy: + case MediaType.DemiDiskette: + case MediaType.QuickDisk: + case MediaType.Wafer: + case MediaType.ZXMicrodrive: + case MediaType.AppleProfile: + case MediaType.AppleWidget: + case MediaType.AppleHD20: + case MediaType.RA60: + case MediaType.RA80: + case MediaType.RA81: + case MediaType.RC25: + case MediaType.RD31: + case MediaType.RD32: + case MediaType.RD51: + case MediaType.RD52: + case MediaType.RD53: + case MediaType.RD54: + case MediaType.RK06: + case MediaType.RK06_18: + case MediaType.RK07: + case MediaType.RK07_18: + case MediaType.RM02: + case MediaType.RM03: + case MediaType.RM05: + case MediaType.RP02: + case MediaType.RP02_18: + case MediaType.RP03: + case MediaType.RP03_18: + case MediaType.RP04: + case MediaType.RP04_18: + case MediaType.RP05: + case MediaType.RP05_18: + case MediaType.RP06: + case MediaType.RP06_18: + case MediaType.GENERIC_HDD: + case MediaType.FlashDrive: + case MediaType.MetaFloppy_Mod_I: + case MediaType.MetaFloppy_Mod_II: break; + default: return false; + } - // This will only continue on devices with a chance to have ever been used by CP/M while failing on all others - // It's ugly, but will stop a lot of false positives - switch(imagePlugin.Info.MediaType) + // This will try to identify a CP/M filesystem + // However as it contains no identification marks whatsoever it's more something of trial-and-error + // As anything can happen, better try{}catch{} than sorry ;) + try + { + byte[] sector; + ulong sectorSize; + ulong firstDirectorySector; + byte[] directory = null; + _workingDefinition = null; + _label = null; + + // Try Amstrad superblock + if(!_cpmFound) { - case MediaType.Unknown: - case MediaType.Apple32SS: - case MediaType.Apple32DS: - case MediaType.Apple33SS: - case MediaType.Apple33DS: - case MediaType.DOS_525_SS_DD_8: - case MediaType.DOS_525_SS_DD_9: - case MediaType.DOS_525_DS_DD_8: - case MediaType.DOS_525_DS_DD_9: - case MediaType.DOS_525_HD: - case MediaType.DOS_35_SS_DD_8: - case MediaType.DOS_35_SS_DD_9: - case MediaType.DOS_35_DS_DD_8: - case MediaType.DOS_35_DS_DD_9: - case MediaType.DOS_35_HD: - case MediaType.DOS_35_ED: - case MediaType.IBM23FD: - case MediaType.IBM33FD_128: - case MediaType.IBM33FD_256: - case MediaType.IBM33FD_512: - case MediaType.IBM43FD_128: - case MediaType.IBM43FD_256: - case MediaType.IBM53FD_256: - case MediaType.IBM53FD_512: - case MediaType.IBM53FD_1024: - case MediaType.RX01: - case MediaType.RX02: - case MediaType.RX03: - case MediaType.RX50: - case MediaType.ACORN_525_SS_SD_40: - case MediaType.ACORN_525_SS_SD_80: - case MediaType.ACORN_525_SS_DD_40: - case MediaType.ACORN_525_SS_DD_80: - case MediaType.ACORN_525_DS_DD: - case MediaType.ATARI_525_SD: - case MediaType.ATARI_525_ED: - case MediaType.ATARI_525_DD: - case MediaType.CBM_35_DD: - case MediaType.CBM_1540: - case MediaType.CBM_1540_Ext: - case MediaType.CBM_1571: - case MediaType.NEC_8_SD: - case MediaType.NEC_8_DD: - case MediaType.NEC_525_SS: - case MediaType.NEC_525_DS: - case MediaType.NEC_525_HD: - case MediaType.NEC_35_HD_8: - case MediaType.NEC_35_HD_15: - case MediaType.SHARP_525_9: - case MediaType.SHARP_35_9: - case MediaType.ECMA_99_8: - case MediaType.ECMA_99_15: - case MediaType.ECMA_99_26: - case MediaType.ECMA_54: - case MediaType.ECMA_59: - case MediaType.ECMA_66: - case MediaType.ECMA_69_8: - case MediaType.ECMA_69_15: - case MediaType.ECMA_69_26: - case MediaType.ECMA_70: - case MediaType.ECMA_78: - case MediaType.ECMA_78_2: - case MediaType.Apricot_35: - case MediaType.CompactFloppy: - case MediaType.DemiDiskette: - case MediaType.QuickDisk: - case MediaType.Wafer: - case MediaType.ZXMicrodrive: - case MediaType.AppleProfile: - case MediaType.AppleWidget: - case MediaType.AppleHD20: - case MediaType.RA60: - case MediaType.RA80: - case MediaType.RA81: - case MediaType.RC25: - case MediaType.RD31: - case MediaType.RD32: - case MediaType.RD51: - case MediaType.RD52: - case MediaType.RD53: - case MediaType.RD54: - case MediaType.RK06: - case MediaType.RK06_18: - case MediaType.RK07: - case MediaType.RK07_18: - case MediaType.RM02: - case MediaType.RM03: - case MediaType.RM05: - case MediaType.RP02: - case MediaType.RP02_18: - case MediaType.RP03: - case MediaType.RP03_18: - case MediaType.RP04: - case MediaType.RP04_18: - case MediaType.RP05: - case MediaType.RP05_18: - case MediaType.RP06: - case MediaType.RP06_18: - case MediaType.GENERIC_HDD: - case MediaType.FlashDrive: - case MediaType.MetaFloppy_Mod_I: - case MediaType.MetaFloppy_Mod_II: break; - default: return false; - } + // Read CHS = {0,0,1} + errno = imagePlugin.ReadSector(0 + partition.Start, out sector); - // This will try to identify a CP/M filesystem - // However as it contains no identification marks whatsoever it's more something of trial-and-error - // As anything can happen, better try{}catch{} than sorry ;) - try - { - byte[] sector; - ulong sectorSize; - ulong firstDirectorySector; - byte[] directory = null; - _workingDefinition = null; - _label = null; - - // Try Amstrad superblock - if(!_cpmFound) + if(errno == ErrorNumber.NoError) { - // Read CHS = {0,0,1} - errno = imagePlugin.ReadSector(0 + partition.Start, out sector); + int amsSbOffset = 0; - if(errno == ErrorNumber.NoError) + uint sig1 = BitConverter.ToUInt32(sector, 0x2B); + uint sig2 = BitConverter.ToUInt32(sector, 0x33) & 0x00FFFFFF; + uint sig3 = BitConverter.ToUInt32(sector, 0x7C); + + // PCW16 extended boot record + if(sig1 == 0x4D2F5043 && + sig2 == 0x004B5344 && + sig3 == sig1) + amsSbOffset = 0x80; + + // Read the superblock + AmstradSuperBlock amsSb = + Marshal.ByteArrayToStructureLittleEndian(sector, amsSbOffset, 16); + + // Check that format byte and sidedness indicate the same number of sizes + if((amsSb.format == 0 && (amsSb.sidedness & 0x02) == 0) || + (amsSb.format == 2 && (amsSb.sidedness & 0x02) == 1) || + (amsSb.format == 2 && (amsSb.sidedness & 0x02) == 2)) { - int amsSbOffset = 0; + // Calculate device limits + ulong sides = (ulong)(amsSb.format == 0 ? 1 : 2); + ulong sectorCount = (ulong)(amsSb.tps * amsSb.spt * (byte)sides); + sectorSize = (ulong)(128 << amsSb.psh); - uint sig1 = BitConverter.ToUInt32(sector, 0x2B); - uint sig2 = BitConverter.ToUInt32(sector, 0x33) & 0x00FFFFFF; - uint sig3 = BitConverter.ToUInt32(sector, 0x7C); - - // PCW16 extended boot record - if(sig1 == 0x4D2F5043 && - sig2 == 0x004B5344 && - sig3 == sig1) - amsSbOffset = 0x80; - - // Read the superblock - AmstradSuperBlock amsSb = - Marshal.ByteArrayToStructureLittleEndian(sector, amsSbOffset, 16); - - // Check that format byte and sidedness indicate the same number of sizes - if((amsSb.format == 0 && (amsSb.sidedness & 0x02) == 0) || - (amsSb.format == 2 && (amsSb.sidedness & 0x02) == 1) || - (amsSb.format == 2 && (amsSb.sidedness & 0x02) == 2)) + // Compare device limits from superblock to real limits + if(sectorSize == imagePlugin.Info.SectorSize && + sectorCount == imagePlugin.Info.Sectors) { - // Calculate device limits - ulong sides = (ulong)(amsSb.format == 0 ? 1 : 2); - ulong sectorCount = (ulong)(amsSb.tps * amsSb.spt * (byte)sides); - sectorSize = (ulong)(128 << amsSb.psh); + _cpmFound = true; + firstDirectorySector = (ulong)(amsSb.off * amsSb.spt); - // Compare device limits from superblock to real limits - if(sectorSize == imagePlugin.Info.SectorSize && - sectorCount == imagePlugin.Info.Sectors) + // Build a DiscParameterBlock + _dpb = new DiscParameterBlock { - _cpmFound = true; - firstDirectorySector = (ulong)(amsSb.off * amsSb.spt); + al0 = sectorCount == 1440 ? (byte)0xF0 : (byte)0xC0, + spt = amsSb.spt, + bsh = amsSb.bsh + }; - // Build a DiscParameterBlock - _dpb = new DiscParameterBlock + for(int i = 0; i < _dpb.bsh; i++) + _dpb.blm += (byte)Math.Pow(2, i); + + if(sectorCount >= 1440) + { + _dpb.cks = 0x40; + _dpb.drm = 0xFF; + } + else + { + _dpb.cks = 0x10; + _dpb.drm = 0x3F; + } + + _dpb.dsm = 0; // I don't care + _dpb.exm = sectorCount == 2880 ? (byte)1 : (byte)0; + _dpb.off = amsSb.off; + _dpb.psh = amsSb.psh; + + for(int i = 0; i < _dpb.psh; i++) + _dpb.phm += (byte)Math.Pow(2, i); + + _dpb.spt = (ushort)(amsSb.spt * (sectorSize / 128)); + uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / sectorSize); + + errno = imagePlugin.ReadSectors(firstDirectorySector + partition.Start, directoryLength, + out directory); + + // Build a CP/M disk definition + _workingDefinition = new CpmDefinition + { + al0 = _dpb.al0, + al1 = _dpb.al1, + bitrate = "LOW", + blm = _dpb.blm, + bsh = _dpb.bsh, + bytesPerSector = 512, + cylinders = amsSb.tps, + drm = _dpb.drm, + dsm = _dpb.dsm, + encoding = "MFM", + evenOdd = false, + exm = _dpb.exm, + label = null, + comment = "Amstrad PCW superblock", + ofs = _dpb.off, + sectorsPerTrack = amsSb.spt, + side1 = new Side { - al0 = sectorCount == 1440 ? (byte)0xF0 : (byte)0xC0, - spt = amsSb.spt, - bsh = amsSb.bsh + sideId = 0, + sectorIds = new int[amsSb.spt] + } + }; + + for(int si = 0; si < amsSb.spt; si++) + _workingDefinition.side1.sectorIds[si] = si + 1; + + if(amsSb.format == 2) + { + switch(amsSb.sidedness & 0x02) + { + case 1: + _workingDefinition.order = "SIDES"; + + break; + case 2: + _workingDefinition.order = "CYLINDERS"; + + break; + default: + _workingDefinition.order = null; + + break; + } + + _workingDefinition.side2 = new Side + { + sideId = 1, + sectorIds = new int[amsSb.spt] }; - for(int i = 0; i < _dpb.bsh; i++) - _dpb.blm += (byte)Math.Pow(2, i); + for(int si = 0; si < amsSb.spt; si++) + _workingDefinition.side2.sectorIds[si] = si + 1; + } + else + _workingDefinition.order = null; - if(sectorCount >= 1440) + _workingDefinition.skew = 2; + _workingDefinition.sofs = 0; + + AaruConsole.DebugWriteLine("CP/M Plugin", "Found Amstrad superblock."); + } + } + } + } + + // Try CP/M-86 superblock for hard disks + if(!_cpmFound) + { + // Read CHS = {0,0,4} + errno = imagePlugin.ReadSector(3 + partition.Start, out sector); + + if(errno == ErrorNumber.NoError) + { + ushort sum = 0; + + // Sum of all 16-bit words that make this sector must be 0 + for(int i = 0; i < sector.Length; i += 2) + sum += BitConverter.ToUInt16(sector, i); + + // It may happen that there is a corrupted superblock + // Better to ignore corrupted than to false positive the rest + if(sum == 0) + { + // Read the superblock + HardDiskSuperBlock hddSb = + Marshal.ByteArrayToStructureLittleEndian(sector); + + // Calculate volume size + sectorSize = (ulong)(hddSb.recordsPerSector * 128); + ulong sectorsInPartition = (ulong)(hddSb.cylinders * hddSb.heads * hddSb.sectorsPerTrack); + + ulong startingSector = + (ulong)(((hddSb.firstCylinder * hddSb.heads) + hddSb.heads) * hddSb.sectorsPerTrack); + + // If volume size corresponds with working partition (this variant will be inside MBR partitioning) + if(sectorSize == imagePlugin.Info.SectorSize && + startingSector == partition.Start && + sectorsInPartition + partition.Start <= partition.End) + { + _cpmFound = true; + firstDirectorySector = (ulong)(hddSb.off * hddSb.sectorsPerTrack); + + // Build a DiscParameterBlock + _dpb = new DiscParameterBlock + { + al0 = (byte)hddSb.al0, + al1 = (byte)hddSb.al1, + blm = hddSb.blm, + bsh = hddSb.bsh, + cks = hddSb.cks, + drm = hddSb.drm, + dsm = hddSb.dsm, + exm = hddSb.exm, + off = hddSb.off, + + // Needed? + phm = 0, + + // Needed? + psh = 0, + spt = hddSb.spt + }; + + uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / sectorSize); + + errno = imagePlugin.ReadSectors(firstDirectorySector + partition.Start, directoryLength, + out directory); + + AaruConsole.DebugWriteLine("CP/M Plugin", "Found CP/M-86 hard disk superblock."); + + // Build a CP/M disk definition + _workingDefinition = new CpmDefinition + { + al0 = _dpb.al0, + al1 = _dpb.al1, + bitrate = "HIGH", + blm = _dpb.blm, + bsh = _dpb.bsh, + bytesPerSector = 512, + cylinders = hddSb.cylinders, + drm = _dpb.drm, + dsm = _dpb.dsm, + encoding = "MFM", + evenOdd = false, + exm = _dpb.exm, + label = null, + comment = "CP/M-86 hard disk superblock", + ofs = _dpb.off, + sectorsPerTrack = hddSb.sectorsPerTrack, + side1 = new Side { - _dpb.cks = 0x40; - _dpb.drm = 0xFF; - } - else + sideId = 0, + sectorIds = new int[hddSb.sectorsPerTrack] + }, + order = "SIDES", + side2 = new Side { - _dpb.cks = 0x10; - _dpb.drm = 0x3F; - } + sideId = 1, + sectorIds = new int[hddSb.sectorsPerTrack] + }, + skew = 0, + sofs = 0 + }; - _dpb.dsm = 0; // I don't care - _dpb.exm = sectorCount == 2880 ? (byte)1 : (byte)0; - _dpb.off = amsSb.off; - _dpb.psh = amsSb.psh; + for(int si = 0; si < hddSb.sectorsPerTrack; si++) + _workingDefinition.side1.sectorIds[si] = si + 1; - for(int i = 0; i < _dpb.psh; i++) - _dpb.phm += (byte)Math.Pow(2, i); + for(int si = 0; si < hddSb.spt; si++) + _workingDefinition.side2.sectorIds[si] = si + 1; + } + } + } + } - _dpb.spt = (ushort)(amsSb.spt * (sectorSize / 128)); - uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / sectorSize); + // Try CP/M-86 format ID for floppies + if(!_cpmFound) + { + // Read CHS = {0,0,1} + errno = imagePlugin.ReadSector(0 + partition.Start, out sector); - errno = imagePlugin.ReadSectors(firstDirectorySector + partition.Start, directoryLength, - out directory); + if(errno == ErrorNumber.NoError) + { + byte formatByte; + + // Check for alternate location of format ID + if(sector.Last() == 0x00 || + sector.Last() == 0xFF) + if(sector[0x40] == 0x94 || + sector[0x40] == 0x26) + formatByte = sector[0x40]; + else + formatByte = sector.Last(); + else + formatByte = sector.Last(); + + uint firstDirectorySector86 = 0; + + // Check format ID + // If it is one of the known IDs, check disk size corresponds to the one we expect + // If so, build a DiscParameterBlock and a CP/M disk definition + // Will not work on over-formatted disks (40 cylinder volume on an 80 cylinder disk, + // something that happens a lot in IBM PC 5.25" disks) + switch((FormatByte)formatByte) + { + case FormatByte.k160: + if(imagePlugin.Info.SectorSize == 512 && + imagePlugin.Info.Sectors == 320) + { + _cpmFound = true; + firstDirectorySector86 = 8; + + _dpb = new DiscParameterBlock + { + al0 = 0xC0, + al1 = 0, + blm = 7, + bsh = 3, + cks = 0x10, + drm = 0x3F, + dsm = 0x9B, + exm = 0, + off = 1, + phm = 3, + psh = 2, + spt = 8 * 4 + }; - // Build a CP/M disk definition _workingDefinition = new CpmDefinition { al0 = _dpb.al0, @@ -257,131 +486,312 @@ namespace Aaru.Filesystems blm = _dpb.blm, bsh = _dpb.bsh, bytesPerSector = 512, - cylinders = amsSb.tps, + cylinders = 40, drm = _dpb.drm, dsm = _dpb.dsm, encoding = "MFM", evenOdd = false, exm = _dpb.exm, label = null, - comment = "Amstrad PCW superblock", + comment = "CP/M-86 floppy identifier", ofs = _dpb.off, - sectorsPerTrack = amsSb.spt, + sectorsPerTrack = 8, side1 = new Side { sideId = 0, - sectorIds = new int[amsSb.spt] - } + sectorIds = new int[8] + }, + skew = 0, + sofs = 0 }; - for(int si = 0; si < amsSb.spt; si++) + for(int si = 0; si < 8; si++) _workingDefinition.side1.sectorIds[si] = si + 1; - - if(amsSb.format == 2) - { - switch(amsSb.sidedness & 0x02) - { - case 1: - _workingDefinition.order = "SIDES"; - - break; - case 2: - _workingDefinition.order = "CYLINDERS"; - - break; - default: - _workingDefinition.order = null; - - break; - } - - _workingDefinition.side2 = new Side - { - sideId = 1, - sectorIds = new int[amsSb.spt] - }; - - for(int si = 0; si < amsSb.spt; si++) - _workingDefinition.side2.sectorIds[si] = si + 1; - } - else - _workingDefinition.order = null; - - _workingDefinition.skew = 2; - _workingDefinition.sofs = 0; - - AaruConsole.DebugWriteLine("CP/M Plugin", "Found Amstrad superblock."); } - } - } - } - // Try CP/M-86 superblock for hard disks - if(!_cpmFound) - { - // Read CHS = {0,0,4} - errno = imagePlugin.ReadSector(3 + partition.Start, out sector); - - if(errno == ErrorNumber.NoError) - { - ushort sum = 0; - - // Sum of all 16-bit words that make this sector must be 0 - for(int i = 0; i < sector.Length; i += 2) - sum += BitConverter.ToUInt16(sector, i); - - // It may happen that there is a corrupted superblock - // Better to ignore corrupted than to false positive the rest - if(sum == 0) - { - // Read the superblock - HardDiskSuperBlock hddSb = - Marshal.ByteArrayToStructureLittleEndian(sector); - - // Calculate volume size - sectorSize = (ulong)(hddSb.recordsPerSector * 128); - ulong sectorsInPartition = (ulong)(hddSb.cylinders * hddSb.heads * hddSb.sectorsPerTrack); - - ulong startingSector = - (ulong)(((hddSb.firstCylinder * hddSb.heads) + hddSb.heads) * hddSb.sectorsPerTrack); - - // If volume size corresponds with working partition (this variant will be inside MBR partitioning) - if(sectorSize == imagePlugin.Info.SectorSize && - startingSector == partition.Start && - sectorsInPartition + partition.Start <= partition.End) + break; + case FormatByte.k320: + if(imagePlugin.Info.SectorSize == 512 && + imagePlugin.Info.Sectors == 640) { - _cpmFound = true; - firstDirectorySector = (ulong)(hddSb.off * hddSb.sectorsPerTrack); + _cpmFound = true; + firstDirectorySector86 = 16; - // Build a DiscParameterBlock _dpb = new DiscParameterBlock { - al0 = (byte)hddSb.al0, - al1 = (byte)hddSb.al1, - blm = hddSb.blm, - bsh = hddSb.bsh, - cks = hddSb.cks, - drm = hddSb.drm, - dsm = hddSb.dsm, - exm = hddSb.exm, - off = hddSb.off, - - // Needed? - phm = 0, - - // Needed? - psh = 0, - spt = hddSb.spt + al0 = 0x80, + al1 = 0, + blm = 0x0F, + bsh = 4, + cks = 0x10, + drm = 0x3F, + dsm = 0x9D, + exm = 1, + off = 2, + phm = 3, + psh = 2, + spt = 8 * 4 }; - uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / sectorSize); + _workingDefinition = new CpmDefinition + { + al0 = _dpb.al0, + al1 = _dpb.al1, + bitrate = "LOW", + blm = _dpb.blm, + bsh = _dpb.bsh, + bytesPerSector = 512, + cylinders = 40, + drm = _dpb.drm, + dsm = _dpb.dsm, + encoding = "MFM", + evenOdd = false, + exm = _dpb.exm, + label = null, + comment = "CP/M-86 floppy identifier", + ofs = _dpb.off, + sectorsPerTrack = 8, + side1 = new Side + { + sideId = 0, + sectorIds = new int[8] + }, + order = "SIDES", + side2 = new Side + { + sideId = 1, + sectorIds = new int[8] + }, + skew = 0, + sofs = 0 + }; - errno = imagePlugin.ReadSectors(firstDirectorySector + partition.Start, directoryLength, - out directory); + for(int si = 0; si < 8; si++) + _workingDefinition.side1.sectorIds[si] = si + 1; - AaruConsole.DebugWriteLine("CP/M Plugin", "Found CP/M-86 hard disk superblock."); + for(int si = 0; si < 8; si++) + _workingDefinition.side2.sectorIds[si] = si + 1; + } + + break; + case FormatByte.k360: + case FormatByte.k360Alt: + case FormatByte.k360Alt2: + if(imagePlugin.Info.SectorSize == 512 && + imagePlugin.Info.Sectors == 720) + { + _cpmFound = true; + firstDirectorySector86 = 36; + + _dpb = new DiscParameterBlock + { + al0 = 0x80, + al1 = 0, + blm = 0x0F, + bsh = 4, + cks = 0x10, + drm = 0x3F, + dsm = 0, // Unknown. Needed? + exm = 1, + off = 4, + phm = 3, + psh = 2, + spt = 9 * 4 + }; + + _workingDefinition = new CpmDefinition + { + al0 = _dpb.al0, + al1 = _dpb.al1, + bitrate = "LOW", + blm = _dpb.blm, + bsh = _dpb.bsh, + bytesPerSector = 512, + cylinders = 40, + drm = _dpb.drm, + dsm = _dpb.dsm, + encoding = "MFM", + evenOdd = false, + exm = _dpb.exm, + label = null, + comment = "CP/M-86 floppy identifier", + ofs = _dpb.off, + sectorsPerTrack = 9, + side1 = new Side + { + sideId = 0, + sectorIds = new int[9] + }, + order = "SIDES", + side2 = new Side + { + sideId = 1, + sectorIds = new int[9] + }, + skew = 0, + sofs = 0 + }; + + for(int si = 0; si < 9; si++) + _workingDefinition.side1.sectorIds[si] = si + 1; + + for(int si = 0; si < 9; si++) + _workingDefinition.side2.sectorIds[si] = si + 1; + } + + break; + case FormatByte.k720: + case FormatByte.k720Alt: + if(imagePlugin.Info.SectorSize == 512 && + imagePlugin.Info.Sectors == 1440) + { + _cpmFound = true; + firstDirectorySector86 = 36; + + _dpb = new DiscParameterBlock + { + al0 = 0xF0, + al1 = 0, + blm = 0x0F, + bsh = 4, + cks = 0x40, + drm = 0xFF, + dsm = 0x15E, + exm = 0, + off = 4, + phm = 3, + psh = 2, + spt = 9 * 4 + }; + + _workingDefinition = new CpmDefinition + { + al0 = _dpb.al0, + al1 = _dpb.al1, + bitrate = "LOW", + blm = _dpb.blm, + bsh = _dpb.bsh, + bytesPerSector = 512, + cylinders = 80, + drm = _dpb.drm, + dsm = _dpb.dsm, + encoding = "MFM", + evenOdd = false, + exm = _dpb.exm, + label = null, + comment = "CP/M-86 floppy identifier", + ofs = _dpb.off, + sectorsPerTrack = 9, + side1 = new Side + { + sideId = 0, + sectorIds = new int[9] + }, + order = "SIDES", + side2 = new Side + { + sideId = 1, + sectorIds = new int[9] + }, + skew = 0, + sofs = 0 + }; + + for(int si = 0; si < 9; si++) + _workingDefinition.side1.sectorIds[si] = si + 1; + + for(int si = 0; si < 9; si++) + _workingDefinition.side2.sectorIds[si] = si + 1; + } + + break; + case FormatByte.f720: + if(imagePlugin.Info.SectorSize == 512 && + imagePlugin.Info.Sectors == 1440) + { + _cpmFound = true; + firstDirectorySector86 = 18; + + _dpb = new DiscParameterBlock + { + al0 = 0xF0, + al1 = 0, + blm = 0x0F, + bsh = 4, + cks = 0x40, + drm = 0xFF, + dsm = 0x162, + exm = 0, + off = 2, + phm = 3, + psh = 2, + spt = 9 * 4 + }; + + _workingDefinition = new CpmDefinition + { + al0 = _dpb.al0, + al1 = _dpb.al1, + bitrate = "LOW", + blm = _dpb.blm, + bsh = _dpb.bsh, + bytesPerSector = 512, + cylinders = 80, + drm = _dpb.drm, + dsm = _dpb.dsm, + encoding = "MFM", + evenOdd = false, + exm = _dpb.exm, + label = null, + comment = "CP/M-86 floppy identifier", + ofs = _dpb.off, + sectorsPerTrack = 9, + side1 = new Side + { + sideId = 0, + sectorIds = new int[9] + }, + order = "CYLINDERS", + side2 = new Side + { + sideId = 1, + sectorIds = new int[9] + }, + skew = 0, + sofs = 0 + }; + + for(int si = 0; si < 9; si++) + _workingDefinition.side1.sectorIds[si] = si + 1; + + for(int si = 0; si < 9; si++) + _workingDefinition.side2.sectorIds[si] = si + 1; + } + + break; + case FormatByte.f1200: + if(imagePlugin.Info.SectorSize == 512 && + imagePlugin.Info.Sectors == 2400) + { + _cpmFound = true; + firstDirectorySector86 = 30; + + _dpb = new DiscParameterBlock + { + al0 = 0xC0, + al1 = 0, + blm = 0x1F, + bsh = 5, + cks = 0x40, + drm = 0xFF, + dsm = 0x127, + exm = 1, + off = 2, + phm = 3, + psh = 2, + spt = 15 * 4 + }; - // Build a CP/M disk definition _workingDefinition = new CpmDefinition { al0 = _dpb.al0, @@ -390,888 +800,477 @@ namespace Aaru.Filesystems blm = _dpb.blm, bsh = _dpb.bsh, bytesPerSector = 512, - cylinders = hddSb.cylinders, + cylinders = 80, drm = _dpb.drm, dsm = _dpb.dsm, encoding = "MFM", evenOdd = false, exm = _dpb.exm, label = null, - comment = "CP/M-86 hard disk superblock", + comment = "CP/M-86 floppy identifier", ofs = _dpb.off, - sectorsPerTrack = hddSb.sectorsPerTrack, + sectorsPerTrack = 15, side1 = new Side { sideId = 0, - sectorIds = new int[hddSb.sectorsPerTrack] + sectorIds = new int[15] }, - order = "SIDES", + order = "CYLINDERS", side2 = new Side { sideId = 1, - sectorIds = new int[hddSb.sectorsPerTrack] + sectorIds = new int[15] }, skew = 0, sofs = 0 }; - for(int si = 0; si < hddSb.sectorsPerTrack; si++) + for(int si = 0; si < 15; si++) _workingDefinition.side1.sectorIds[si] = si + 1; - for(int si = 0; si < hddSb.spt; si++) + for(int si = 0; si < 15; si++) _workingDefinition.side2.sectorIds[si] = si + 1; } - } - } - } - // Try CP/M-86 format ID for floppies - if(!_cpmFound) - { - // Read CHS = {0,0,1} - errno = imagePlugin.ReadSector(0 + partition.Start, out sector); - - if(errno == ErrorNumber.NoError) - { - byte formatByte; - - // Check for alternate location of format ID - if(sector.Last() == 0x00 || - sector.Last() == 0xFF) - if(sector[0x40] == 0x94 || - sector[0x40] == 0x26) - formatByte = sector[0x40]; - else - formatByte = sector.Last(); - else - formatByte = sector.Last(); - - uint firstDirectorySector86 = 0; - - // Check format ID - // If it is one of the known IDs, check disk size corresponds to the one we expect - // If so, build a DiscParameterBlock and a CP/M disk definition - // Will not work on over-formatted disks (40 cylinder volume on an 80 cylinder disk, - // something that happens a lot in IBM PC 5.25" disks) - switch((FormatByte)formatByte) - { - case FormatByte.k160: - if(imagePlugin.Info.SectorSize == 512 && - imagePlugin.Info.Sectors == 320) - { - _cpmFound = true; - firstDirectorySector86 = 8; - - _dpb = new DiscParameterBlock - { - al0 = 0xC0, - al1 = 0, - blm = 7, - bsh = 3, - cks = 0x10, - drm = 0x3F, - dsm = 0x9B, - exm = 0, - off = 1, - phm = 3, - psh = 2, - spt = 8 * 4 - }; - - _workingDefinition = new CpmDefinition - { - al0 = _dpb.al0, - al1 = _dpb.al1, - bitrate = "LOW", - blm = _dpb.blm, - bsh = _dpb.bsh, - bytesPerSector = 512, - cylinders = 40, - drm = _dpb.drm, - dsm = _dpb.dsm, - encoding = "MFM", - evenOdd = false, - exm = _dpb.exm, - label = null, - comment = "CP/M-86 floppy identifier", - ofs = _dpb.off, - sectorsPerTrack = 8, - side1 = new Side - { - sideId = 0, - sectorIds = new int[8] - }, - skew = 0, - sofs = 0 - }; - - for(int si = 0; si < 8; si++) - _workingDefinition.side1.sectorIds[si] = si + 1; - } - - break; - case FormatByte.k320: - if(imagePlugin.Info.SectorSize == 512 && - imagePlugin.Info.Sectors == 640) - { - _cpmFound = true; - firstDirectorySector86 = 16; - - _dpb = new DiscParameterBlock - { - al0 = 0x80, - al1 = 0, - blm = 0x0F, - bsh = 4, - cks = 0x10, - drm = 0x3F, - dsm = 0x9D, - exm = 1, - off = 2, - phm = 3, - psh = 2, - spt = 8 * 4 - }; - - _workingDefinition = new CpmDefinition - { - al0 = _dpb.al0, - al1 = _dpb.al1, - bitrate = "LOW", - blm = _dpb.blm, - bsh = _dpb.bsh, - bytesPerSector = 512, - cylinders = 40, - drm = _dpb.drm, - dsm = _dpb.dsm, - encoding = "MFM", - evenOdd = false, - exm = _dpb.exm, - label = null, - comment = "CP/M-86 floppy identifier", - ofs = _dpb.off, - sectorsPerTrack = 8, - side1 = new Side - { - sideId = 0, - sectorIds = new int[8] - }, - order = "SIDES", - side2 = new Side - { - sideId = 1, - sectorIds = new int[8] - }, - skew = 0, - sofs = 0 - }; - - for(int si = 0; si < 8; si++) - _workingDefinition.side1.sectorIds[si] = si + 1; - - for(int si = 0; si < 8; si++) - _workingDefinition.side2.sectorIds[si] = si + 1; - } - - break; - case FormatByte.k360: - case FormatByte.k360Alt: - case FormatByte.k360Alt2: - if(imagePlugin.Info.SectorSize == 512 && - imagePlugin.Info.Sectors == 720) - { - _cpmFound = true; - firstDirectorySector86 = 36; - - _dpb = new DiscParameterBlock - { - al0 = 0x80, - al1 = 0, - blm = 0x0F, - bsh = 4, - cks = 0x10, - drm = 0x3F, - dsm = 0, // Unknown. Needed? - exm = 1, - off = 4, - phm = 3, - psh = 2, - spt = 9 * 4 - }; - - _workingDefinition = new CpmDefinition - { - al0 = _dpb.al0, - al1 = _dpb.al1, - bitrate = "LOW", - blm = _dpb.blm, - bsh = _dpb.bsh, - bytesPerSector = 512, - cylinders = 40, - drm = _dpb.drm, - dsm = _dpb.dsm, - encoding = "MFM", - evenOdd = false, - exm = _dpb.exm, - label = null, - comment = "CP/M-86 floppy identifier", - ofs = _dpb.off, - sectorsPerTrack = 9, - side1 = new Side - { - sideId = 0, - sectorIds = new int[9] - }, - order = "SIDES", - side2 = new Side - { - sideId = 1, - sectorIds = new int[9] - }, - skew = 0, - sofs = 0 - }; - - for(int si = 0; si < 9; si++) - _workingDefinition.side1.sectorIds[si] = si + 1; - - for(int si = 0; si < 9; si++) - _workingDefinition.side2.sectorIds[si] = si + 1; - } - - break; - case FormatByte.k720: - case FormatByte.k720Alt: - if(imagePlugin.Info.SectorSize == 512 && - imagePlugin.Info.Sectors == 1440) - { - _cpmFound = true; - firstDirectorySector86 = 36; - - _dpb = new DiscParameterBlock - { - al0 = 0xF0, - al1 = 0, - blm = 0x0F, - bsh = 4, - cks = 0x40, - drm = 0xFF, - dsm = 0x15E, - exm = 0, - off = 4, - phm = 3, - psh = 2, - spt = 9 * 4 - }; - - _workingDefinition = new CpmDefinition - { - al0 = _dpb.al0, - al1 = _dpb.al1, - bitrate = "LOW", - blm = _dpb.blm, - bsh = _dpb.bsh, - bytesPerSector = 512, - cylinders = 80, - drm = _dpb.drm, - dsm = _dpb.dsm, - encoding = "MFM", - evenOdd = false, - exm = _dpb.exm, - label = null, - comment = "CP/M-86 floppy identifier", - ofs = _dpb.off, - sectorsPerTrack = 9, - side1 = new Side - { - sideId = 0, - sectorIds = new int[9] - }, - order = "SIDES", - side2 = new Side - { - sideId = 1, - sectorIds = new int[9] - }, - skew = 0, - sofs = 0 - }; - - for(int si = 0; si < 9; si++) - _workingDefinition.side1.sectorIds[si] = si + 1; - - for(int si = 0; si < 9; si++) - _workingDefinition.side2.sectorIds[si] = si + 1; - } - - break; - case FormatByte.f720: - if(imagePlugin.Info.SectorSize == 512 && - imagePlugin.Info.Sectors == 1440) - { - _cpmFound = true; - firstDirectorySector86 = 18; - - _dpb = new DiscParameterBlock - { - al0 = 0xF0, - al1 = 0, - blm = 0x0F, - bsh = 4, - cks = 0x40, - drm = 0xFF, - dsm = 0x162, - exm = 0, - off = 2, - phm = 3, - psh = 2, - spt = 9 * 4 - }; - - _workingDefinition = new CpmDefinition - { - al0 = _dpb.al0, - al1 = _dpb.al1, - bitrate = "LOW", - blm = _dpb.blm, - bsh = _dpb.bsh, - bytesPerSector = 512, - cylinders = 80, - drm = _dpb.drm, - dsm = _dpb.dsm, - encoding = "MFM", - evenOdd = false, - exm = _dpb.exm, - label = null, - comment = "CP/M-86 floppy identifier", - ofs = _dpb.off, - sectorsPerTrack = 9, - side1 = new Side - { - sideId = 0, - sectorIds = new int[9] - }, - order = "CYLINDERS", - side2 = new Side - { - sideId = 1, - sectorIds = new int[9] - }, - skew = 0, - sofs = 0 - }; - - for(int si = 0; si < 9; si++) - _workingDefinition.side1.sectorIds[si] = si + 1; - - for(int si = 0; si < 9; si++) - _workingDefinition.side2.sectorIds[si] = si + 1; - } - - break; - case FormatByte.f1200: - if(imagePlugin.Info.SectorSize == 512 && - imagePlugin.Info.Sectors == 2400) - { - _cpmFound = true; - firstDirectorySector86 = 30; - - _dpb = new DiscParameterBlock - { - al0 = 0xC0, - al1 = 0, - blm = 0x1F, - bsh = 5, - cks = 0x40, - drm = 0xFF, - dsm = 0x127, - exm = 1, - off = 2, - phm = 3, - psh = 2, - spt = 15 * 4 - }; - - _workingDefinition = new CpmDefinition - { - al0 = _dpb.al0, - al1 = _dpb.al1, - bitrate = "HIGH", - blm = _dpb.blm, - bsh = _dpb.bsh, - bytesPerSector = 512, - cylinders = 80, - drm = _dpb.drm, - dsm = _dpb.dsm, - encoding = "MFM", - evenOdd = false, - exm = _dpb.exm, - label = null, - comment = "CP/M-86 floppy identifier", - ofs = _dpb.off, - sectorsPerTrack = 15, - side1 = new Side - { - sideId = 0, - sectorIds = new int[15] - }, - order = "CYLINDERS", - side2 = new Side - { - sideId = 1, - sectorIds = new int[15] - }, - skew = 0, - sofs = 0 - }; - - for(int si = 0; si < 15; si++) - _workingDefinition.side1.sectorIds[si] = si + 1; - - for(int si = 0; si < 15; si++) - _workingDefinition.side2.sectorIds[si] = si + 1; - } - - break; - case FormatByte.f1440: - if(imagePlugin.Info.SectorSize == 512 && - imagePlugin.Info.Sectors == 2880) - { - _cpmFound = true; - firstDirectorySector86 = 36; - - _dpb = new DiscParameterBlock - { - al0 = 0xC0, - al1 = 0, - blm = 0x1F, - bsh = 5, - cks = 0x40, - drm = 0xFF, - dsm = 0x162, - exm = 1, - off = 2, - phm = 3, - psh = 2, - spt = 18 * 4 - }; - - _workingDefinition = new CpmDefinition - { - al0 = _dpb.al0, - al1 = _dpb.al1, - bitrate = "LOW", - blm = _dpb.blm, - bsh = _dpb.bsh, - bytesPerSector = 512, - cylinders = 80, - drm = _dpb.drm, - dsm = _dpb.dsm, - encoding = "MFM", - evenOdd = false, - exm = _dpb.exm, - label = null, - comment = "CP/M-86 floppy identifier", - ofs = _dpb.off, - sectorsPerTrack = 18, - side1 = new Side - { - sideId = 0, - sectorIds = new int[18] - }, - order = "CYLINDERS", - side2 = new Side - { - sideId = 1, - sectorIds = new int[18] - }, - skew = 0, - sofs = 0 - }; - - for(int si = 0; si < 18; si++) - _workingDefinition.side1.sectorIds[si] = si + 1; - - for(int si = 0; si < 18; si++) - _workingDefinition.side2.sectorIds[si] = si + 1; - } - - break; - } - - if(_cpmFound) - { - uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / imagePlugin.Info.SectorSize); - - errno = imagePlugin.ReadSectors(firstDirectorySector86 + partition.Start, directoryLength, - out directory); - - AaruConsole.DebugWriteLine("CP/M Plugin", "Found CP/M-86 floppy identifier."); - } - } - } - - // One of the few CP/M filesystem marks has been found, try for correcteness checking the whole directory - if(_cpmFound) - { - if(CheckDir(directory)) - { - AaruConsole.DebugWriteLine("CP/M Plugin", "First directory block seems correct."); - - return true; - } - - _cpmFound = false; - } - - // Try all definitions - if(!_cpmFound) - { - // Load all definitions - AaruConsole.DebugWriteLine("CP/M Plugin", "Trying to load definitions."); - - if(LoadDefinitions() && - _definitions?.definitions != null && - _definitions.definitions.Count > 0) - { - AaruConsole.DebugWriteLine("CP/M Plugin", "Trying all known definitions."); - - foreach(CpmDefinition def in from def in _definitions.definitions let sectors = - (ulong)(def.cylinders * def.sides * def.sectorsPerTrack) - where sectors == imagePlugin.Info.Sectors && - def.bytesPerSector == imagePlugin.Info.SectorSize select def) - { - // Definition seems to describe current disk, at least, same number of volume sectors and bytes per sector - AaruConsole.DebugWriteLine("CP/M Plugin", "Trying definition \"{0}\"", def.comment); - ulong offset; - - if(def.sofs != 0) - offset = (ulong)def.sofs; - else - offset = (ulong)(def.ofs * def.sectorsPerTrack); - - int dirLen = (def.drm + 1) * 32 / def.bytesPerSector; - - if(def.sides == 1) + break; + case FormatByte.f1440: + if(imagePlugin.Info.SectorSize == 512 && + imagePlugin.Info.Sectors == 2880) { - _sectorMask = new int[def.side1.sectorIds.Length]; - - for(int m = 0; m < _sectorMask.Length; m++) - _sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0]; - } - else - { - // Head changes after every track - if(string.Compare(def.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) - { - _sectorMask = new int[def.side1.sectorIds.Length + def.side2.sectorIds.Length]; - - for(int m = 0; m < def.side1.sectorIds.Length; m++) - _sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0]; - - // Skip first track (first side) - for(int m = 0; m < def.side2.sectorIds.Length; m++) - _sectorMask[m + def.side1.sectorIds.Length] = - def.side2.sectorIds[m] - def.side2.sectorIds[0] + - def.side1.sectorIds.Length; - } - - // Head changes after whole side - else if(string.Compare(def.order, "CYLINDERS", - StringComparison.InvariantCultureIgnoreCase) == 0) - { - for(int m = 0; m < def.side1.sectorIds.Length; m++) - _sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0]; - - // Skip first track (first side) and first track (second side) - for(int m = 0; m < def.side1.sectorIds.Length; m++) - _sectorMask[m + def.side1.sectorIds.Length] = - def.side1.sectorIds[m] - def.side1.sectorIds[0] + - def.side1.sectorIds.Length + def.side2.sectorIds.Length; - } - - // TODO: Implement COLUMBIA ordering - else if(string.Compare(def.order, "COLUMBIA", - StringComparison.InvariantCultureIgnoreCase) == 0) - { - AaruConsole.DebugWriteLine("CP/M Plugin", - "Don't know how to handle COLUMBIA ordering, not proceeding with this definition."); - - continue; - } - - // TODO: Implement EAGLE ordering - else if(string.Compare(def.order, "EAGLE", - StringComparison.InvariantCultureIgnoreCase) == 0) - { - AaruConsole.DebugWriteLine("CP/M Plugin", - "Don't know how to handle EAGLE ordering, not proceeding with this definition."); - - continue; - } - else - { - AaruConsole.DebugWriteLine("CP/M Plugin", - "Unknown order type \"{0}\", not proceeding with this definition.", - def.order); - - continue; - } - } - - // Read the directory marked by this definition - var ms = new MemoryStream(); - - for(int p = 0; p < dirLen; p++) - { - errno = - imagePlugin. - ReadSector((ulong)((int)offset + (int)partition.Start + (p / _sectorMask.Length * _sectorMask.Length) + _sectorMask[p % _sectorMask.Length]), - out byte[] dirSector); - - if(errno != ErrorNumber.NoError) - break; - - ms.Write(dirSector, 0, dirSector.Length); - } - - directory = ms.ToArray(); - - if(def.evenOdd) - AaruConsole.DebugWriteLine("CP/M Plugin", - "Definition contains EVEN-ODD field, with unknown meaning, detection may be wrong."); - - // Complement of the directory bytes if needed - if(def.complement) - for(int b = 0; b < directory.Length; b++) - directory[b] = (byte)(~directory[b] & 0xFF); - - // Check the directory - if(CheckDir(directory)) - { - AaruConsole.DebugWriteLine("CP/M Plugin", "Definition \"{0}\" has a correct directory", - def.comment); - - // Build a Disc Parameter Block - _workingDefinition = def; + _cpmFound = true; + firstDirectorySector86 = 36; _dpb = new DiscParameterBlock { - al0 = (byte)def.al0, - al1 = (byte)def.al1, - blm = (byte)def.blm, - bsh = (byte)def.bsh, - - // Needed? - cks = 0, - drm = (ushort)def.drm, - dsm = (ushort)def.dsm, - exm = (byte)def.exm, - off = (ushort)def.ofs, - spt = (ushort)(def.sectorsPerTrack * def.bytesPerSector / 128) + al0 = 0xC0, + al1 = 0, + blm = 0x1F, + bsh = 5, + cks = 0x40, + drm = 0xFF, + dsm = 0x162, + exm = 1, + off = 2, + phm = 3, + psh = 2, + spt = 18 * 4 }; - switch(def.bytesPerSector) + _workingDefinition = new CpmDefinition { - case 128: - _dpb.psh = 0; - _dpb.phm = 0; + al0 = _dpb.al0, + al1 = _dpb.al1, + bitrate = "LOW", + blm = _dpb.blm, + bsh = _dpb.bsh, + bytesPerSector = 512, + cylinders = 80, + drm = _dpb.drm, + dsm = _dpb.dsm, + encoding = "MFM", + evenOdd = false, + exm = _dpb.exm, + label = null, + comment = "CP/M-86 floppy identifier", + ofs = _dpb.off, + sectorsPerTrack = 18, + side1 = new Side + { + sideId = 0, + sectorIds = new int[18] + }, + order = "CYLINDERS", + side2 = new Side + { + sideId = 1, + sectorIds = new int[18] + }, + skew = 0, + sofs = 0 + }; - break; - case 256: - _dpb.psh = 1; - _dpb.phm = 1; + for(int si = 0; si < 18; si++) + _workingDefinition.side1.sectorIds[si] = si + 1; - break; - case 512: - _dpb.psh = 2; - _dpb.phm = 3; - - break; - case 1024: - _dpb.psh = 3; - _dpb.phm = 7; - - break; - case 2048: - _dpb.psh = 4; - _dpb.phm = 15; - - break; - case 4096: - _dpb.psh = 5; - _dpb.phm = 31; - - break; - case 8192: - _dpb.psh = 6; - _dpb.phm = 63; - - break; - case 16384: - _dpb.psh = 7; - _dpb.phm = 127; - - break; - case 32768: - _dpb.psh = 8; - _dpb.phm = 255; - - break; - } - - _cpmFound = true; - _workingDefinition = def; - - return true; + for(int si = 0; si < 18; si++) + _workingDefinition.side2.sectorIds[si] = si + 1; } - _label = null; - _labelCreationDate = null; - _labelUpdateDate = null; - } + break; + } + + if(_cpmFound) + { + uint directoryLength = (uint)(((ulong)_dpb.drm + 1) * 32 / imagePlugin.Info.SectorSize); + + errno = imagePlugin.ReadSectors(firstDirectorySector86 + partition.Start, directoryLength, + out directory); + + AaruConsole.DebugWriteLine("CP/M Plugin", "Found CP/M-86 floppy identifier."); } } - - // Clear class variables - _cpmFound = false; - _workingDefinition = null; - _dpb = null; - _label = null; - _standardTimestamps = false; - _thirdPartyTimestamps = false; - - return false; } - catch + + // One of the few CP/M filesystem marks has been found, try for correcteness checking the whole directory + if(_cpmFound) { - //throw ex; - return false; + if(CheckDir(directory)) + { + AaruConsole.DebugWriteLine("CP/M Plugin", "First directory block seems correct."); + + return true; + } + + _cpmFound = false; } + + // Try all definitions + if(!_cpmFound) + { + // Load all definitions + AaruConsole.DebugWriteLine("CP/M Plugin", "Trying to load definitions."); + + if(LoadDefinitions() && + _definitions?.definitions != null && + _definitions.definitions.Count > 0) + { + AaruConsole.DebugWriteLine("CP/M Plugin", "Trying all known definitions."); + + foreach(CpmDefinition def in from def in _definitions.definitions let sectors = + (ulong)(def.cylinders * def.sides * def.sectorsPerTrack) + where sectors == imagePlugin.Info.Sectors && + def.bytesPerSector == imagePlugin.Info.SectorSize select def) + { + // Definition seems to describe current disk, at least, same number of volume sectors and bytes per sector + AaruConsole.DebugWriteLine("CP/M Plugin", "Trying definition \"{0}\"", def.comment); + ulong offset; + + if(def.sofs != 0) + offset = (ulong)def.sofs; + else + offset = (ulong)(def.ofs * def.sectorsPerTrack); + + int dirLen = (def.drm + 1) * 32 / def.bytesPerSector; + + if(def.sides == 1) + { + _sectorMask = new int[def.side1.sectorIds.Length]; + + for(int m = 0; m < _sectorMask.Length; m++) + _sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0]; + } + else + { + // Head changes after every track + if(string.Compare(def.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) + { + _sectorMask = new int[def.side1.sectorIds.Length + def.side2.sectorIds.Length]; + + for(int m = 0; m < def.side1.sectorIds.Length; m++) + _sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0]; + + // Skip first track (first side) + for(int m = 0; m < def.side2.sectorIds.Length; m++) + _sectorMask[m + def.side1.sectorIds.Length] = + def.side2.sectorIds[m] - def.side2.sectorIds[0] + + def.side1.sectorIds.Length; + } + + // Head changes after whole side + else if(string.Compare(def.order, "CYLINDERS", + StringComparison.InvariantCultureIgnoreCase) == 0) + { + for(int m = 0; m < def.side1.sectorIds.Length; m++) + _sectorMask[m] = def.side1.sectorIds[m] - def.side1.sectorIds[0]; + + // Skip first track (first side) and first track (second side) + for(int m = 0; m < def.side1.sectorIds.Length; m++) + _sectorMask[m + def.side1.sectorIds.Length] = + def.side1.sectorIds[m] - def.side1.sectorIds[0] + + def.side1.sectorIds.Length + def.side2.sectorIds.Length; + } + + // TODO: Implement COLUMBIA ordering + else if(string.Compare(def.order, "COLUMBIA", + StringComparison.InvariantCultureIgnoreCase) == 0) + { + AaruConsole.DebugWriteLine("CP/M Plugin", + "Don't know how to handle COLUMBIA ordering, not proceeding with this definition."); + + continue; + } + + // TODO: Implement EAGLE ordering + else if(string.Compare(def.order, "EAGLE", + StringComparison.InvariantCultureIgnoreCase) == 0) + { + AaruConsole.DebugWriteLine("CP/M Plugin", + "Don't know how to handle EAGLE ordering, not proceeding with this definition."); + + continue; + } + else + { + AaruConsole.DebugWriteLine("CP/M Plugin", + "Unknown order type \"{0}\", not proceeding with this definition.", + def.order); + + continue; + } + } + + // Read the directory marked by this definition + var ms = new MemoryStream(); + + for(int p = 0; p < dirLen; p++) + { + errno = + imagePlugin. + ReadSector((ulong)((int)offset + (int)partition.Start + (p / _sectorMask.Length * _sectorMask.Length) + _sectorMask[p % _sectorMask.Length]), + out byte[] dirSector); + + if(errno != ErrorNumber.NoError) + break; + + ms.Write(dirSector, 0, dirSector.Length); + } + + directory = ms.ToArray(); + + if(def.evenOdd) + AaruConsole.DebugWriteLine("CP/M Plugin", + "Definition contains EVEN-ODD field, with unknown meaning, detection may be wrong."); + + // Complement of the directory bytes if needed + if(def.complement) + for(int b = 0; b < directory.Length; b++) + directory[b] = (byte)(~directory[b] & 0xFF); + + // Check the directory + if(CheckDir(directory)) + { + AaruConsole.DebugWriteLine("CP/M Plugin", "Definition \"{0}\" has a correct directory", + def.comment); + + // Build a Disc Parameter Block + _workingDefinition = def; + + _dpb = new DiscParameterBlock + { + al0 = (byte)def.al0, + al1 = (byte)def.al1, + blm = (byte)def.blm, + bsh = (byte)def.bsh, + + // Needed? + cks = 0, + drm = (ushort)def.drm, + dsm = (ushort)def.dsm, + exm = (byte)def.exm, + off = (ushort)def.ofs, + spt = (ushort)(def.sectorsPerTrack * def.bytesPerSector / 128) + }; + + switch(def.bytesPerSector) + { + case 128: + _dpb.psh = 0; + _dpb.phm = 0; + + break; + case 256: + _dpb.psh = 1; + _dpb.phm = 1; + + break; + case 512: + _dpb.psh = 2; + _dpb.phm = 3; + + break; + case 1024: + _dpb.psh = 3; + _dpb.phm = 7; + + break; + case 2048: + _dpb.psh = 4; + _dpb.phm = 15; + + break; + case 4096: + _dpb.psh = 5; + _dpb.phm = 31; + + break; + case 8192: + _dpb.psh = 6; + _dpb.phm = 63; + + break; + case 16384: + _dpb.psh = 7; + _dpb.phm = 127; + + break; + case 32768: + _dpb.psh = 8; + _dpb.phm = 255; + + break; + } + + _cpmFound = true; + _workingDefinition = def; + + return true; + } + + _label = null; + _labelCreationDate = null; + _labelUpdateDate = null; + } + } + } + + // Clear class variables + _cpmFound = false; + _workingDefinition = null; + _dpb = null; + _label = null; + _standardTimestamps = false; + _thirdPartyTimestamps = false; + + return false; } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + catch { - Encoding = encoding ?? Encoding.GetEncoding("IBM437"); - information = ""; - - // As the identification is so complex, just call Identify() and relay on its findings - if(!Identify(imagePlugin, partition) || - !_cpmFound || - _workingDefinition == null || - _dpb == null) - return; - - var sb = new StringBuilder(); - sb.AppendLine("CP/M filesystem"); - - if(!string.IsNullOrEmpty(_workingDefinition.comment)) - sb.AppendFormat("Identified as {0}", _workingDefinition.comment).AppendLine(); - - sb.AppendFormat("Volume block is {0} bytes", 128 << _dpb.bsh).AppendLine(); - - if(_dpb.dsm > 0) - sb.AppendFormat("Volume contains {0} blocks ({1} bytes)", _dpb.dsm, _dpb.dsm * (128 << _dpb.bsh)). - AppendLine(); - - sb.AppendFormat("Volume contains {0} directory entries", _dpb.drm + 1).AppendLine(); - - if(_workingDefinition.sofs > 0) - sb.AppendFormat("Volume reserves {0} sectors for system", _workingDefinition.sofs).AppendLine(); - else - sb.AppendFormat("Volume reserves {1} tracks ({0} sectors) for system", - _workingDefinition.ofs * _workingDefinition.sectorsPerTrack, _workingDefinition.ofs). - AppendLine(); - - if(_workingDefinition.side1.sectorIds.Length >= 2) - { - int interleaveSide1 = _workingDefinition.side1.sectorIds[1] - _workingDefinition.side1.sectorIds[0]; - - if(interleaveSide1 > 1) - sb.AppendFormat("Side 0 uses {0}:1 software interleaving", interleaveSide1).AppendLine(); - } - - if(_workingDefinition.sides == 2) - { - if(_workingDefinition.side2.sectorIds.Length >= 2) - { - int interleaveSide2 = _workingDefinition.side2.sectorIds[1] - _workingDefinition.side2.sectorIds[0]; - - if(interleaveSide2 > 1) - sb.AppendFormat("Side 1 uses {0}:1 software interleaving", interleaveSide2).AppendLine(); - } - - switch(_workingDefinition.order) - { - case "SIDES": - sb.AppendLine("Head changes after each whole track"); - - break; - case "CYLINDERS": - sb.AppendLine("Head changes after whole side"); - - break; - default: - sb.AppendFormat("Unknown how {0} side ordering works", _workingDefinition.order).AppendLine(); - - break; - } - } - - if(_workingDefinition.skew > 0) - sb.AppendFormat("Device uses {0}:1 hardware interleaving", _workingDefinition.skew).AppendLine(); - - if(_workingDefinition.sofs > 0) - sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H SOFS {7}", _dpb.bsh, - _dpb.blm, _dpb.exm, _dpb.dsm, _dpb.drm, _dpb.al0, _dpb.al1, _workingDefinition.sofs). - AppendLine(); - else - sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H OFS {7}", _dpb.bsh, - _dpb.blm, _dpb.exm, _dpb.dsm, _dpb.drm, _dpb.al0, _dpb.al1, _workingDefinition.ofs). - AppendLine(); - - if(_label != null) - sb.AppendFormat("Volume label {0}", _label).AppendLine(); - - if(_standardTimestamps) - sb.AppendLine("Volume uses standard CP/M timestamps"); - - if(_thirdPartyTimestamps) - sb.AppendLine("Volume uses third party timestamps"); - - if(_labelCreationDate != null) - sb.AppendFormat("Volume created on {0}", DateHandlers.CpmToDateTime(_labelCreationDate)).AppendLine(); - - if(_labelUpdateDate != null) - sb.AppendFormat("Volume updated on {0}", DateHandlers.CpmToDateTime(_labelUpdateDate)).AppendLine(); - - XmlFsType = new FileSystemType(); - XmlFsType.Bootable |= _workingDefinition.sofs > 0 || _workingDefinition.ofs > 0; - XmlFsType.ClusterSize = (uint)(128 << _dpb.bsh); - - if(_dpb.dsm > 0) - XmlFsType.Clusters = _dpb.dsm; - else - XmlFsType.Clusters = partition.End - partition.Start; - - if(_labelCreationDate != null) - { - XmlFsType.CreationDate = DateHandlers.CpmToDateTime(_labelCreationDate); - XmlFsType.CreationDateSpecified = true; - } - - if(_labelUpdateDate != null) - { - XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(_labelUpdateDate); - XmlFsType.ModificationDateSpecified = true; - } - - XmlFsType.Type = "CP/M"; - XmlFsType.VolumeName = _label; - - information = sb.ToString(); + //throw ex; + return false; } } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("IBM437"); + information = ""; + + // As the identification is so complex, just call Identify() and relay on its findings + if(!Identify(imagePlugin, partition) || + !_cpmFound || + _workingDefinition == null || + _dpb == null) + return; + + var sb = new StringBuilder(); + sb.AppendLine("CP/M filesystem"); + + if(!string.IsNullOrEmpty(_workingDefinition.comment)) + sb.AppendFormat("Identified as {0}", _workingDefinition.comment).AppendLine(); + + sb.AppendFormat("Volume block is {0} bytes", 128 << _dpb.bsh).AppendLine(); + + if(_dpb.dsm > 0) + sb.AppendFormat("Volume contains {0} blocks ({1} bytes)", _dpb.dsm, _dpb.dsm * (128 << _dpb.bsh)). + AppendLine(); + + sb.AppendFormat("Volume contains {0} directory entries", _dpb.drm + 1).AppendLine(); + + if(_workingDefinition.sofs > 0) + sb.AppendFormat("Volume reserves {0} sectors for system", _workingDefinition.sofs).AppendLine(); + else + sb.AppendFormat("Volume reserves {1} tracks ({0} sectors) for system", + _workingDefinition.ofs * _workingDefinition.sectorsPerTrack, _workingDefinition.ofs). + AppendLine(); + + if(_workingDefinition.side1.sectorIds.Length >= 2) + { + int interleaveSide1 = _workingDefinition.side1.sectorIds[1] - _workingDefinition.side1.sectorIds[0]; + + if(interleaveSide1 > 1) + sb.AppendFormat("Side 0 uses {0}:1 software interleaving", interleaveSide1).AppendLine(); + } + + if(_workingDefinition.sides == 2) + { + if(_workingDefinition.side2.sectorIds.Length >= 2) + { + int interleaveSide2 = _workingDefinition.side2.sectorIds[1] - _workingDefinition.side2.sectorIds[0]; + + if(interleaveSide2 > 1) + sb.AppendFormat("Side 1 uses {0}:1 software interleaving", interleaveSide2).AppendLine(); + } + + switch(_workingDefinition.order) + { + case "SIDES": + sb.AppendLine("Head changes after each whole track"); + + break; + case "CYLINDERS": + sb.AppendLine("Head changes after whole side"); + + break; + default: + sb.AppendFormat("Unknown how {0} side ordering works", _workingDefinition.order).AppendLine(); + + break; + } + } + + if(_workingDefinition.skew > 0) + sb.AppendFormat("Device uses {0}:1 hardware interleaving", _workingDefinition.skew).AppendLine(); + + if(_workingDefinition.sofs > 0) + sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H SOFS {7}", _dpb.bsh, + _dpb.blm, _dpb.exm, _dpb.dsm, _dpb.drm, _dpb.al0, _dpb.al1, _workingDefinition.sofs). + AppendLine(); + else + sb.AppendFormat("BSH {0} BLM {1} EXM {2} DSM {3} DRM {4} AL0 {5:X2}H AL1 {6:X2}H OFS {7}", _dpb.bsh, + _dpb.blm, _dpb.exm, _dpb.dsm, _dpb.drm, _dpb.al0, _dpb.al1, _workingDefinition.ofs). + AppendLine(); + + if(_label != null) + sb.AppendFormat("Volume label {0}", _label).AppendLine(); + + if(_standardTimestamps) + sb.AppendLine("Volume uses standard CP/M timestamps"); + + if(_thirdPartyTimestamps) + sb.AppendLine("Volume uses third party timestamps"); + + if(_labelCreationDate != null) + sb.AppendFormat("Volume created on {0}", DateHandlers.CpmToDateTime(_labelCreationDate)).AppendLine(); + + if(_labelUpdateDate != null) + sb.AppendFormat("Volume updated on {0}", DateHandlers.CpmToDateTime(_labelUpdateDate)).AppendLine(); + + XmlFsType = new FileSystemType(); + XmlFsType.Bootable |= _workingDefinition.sofs > 0 || _workingDefinition.ofs > 0; + XmlFsType.ClusterSize = (uint)(128 << _dpb.bsh); + + if(_dpb.dsm > 0) + XmlFsType.Clusters = _dpb.dsm; + else + XmlFsType.Clusters = partition.End - partition.Start; + + if(_labelCreationDate != null) + { + XmlFsType.CreationDate = DateHandlers.CpmToDateTime(_labelCreationDate); + XmlFsType.CreationDateSpecified = true; + } + + if(_labelUpdateDate != null) + { + XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(_labelUpdateDate); + XmlFsType.ModificationDateSpecified = true; + } + + XmlFsType.Type = "CP/M"; + XmlFsType.VolumeName = _label; + + information = sb.ToString(); + } } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/Structs.cs b/Aaru.Filesystems/CPM/Structs.cs index 79a0efc23..80339a4c4 100644 --- a/Aaru.Filesystems/CPM/Structs.cs +++ b/Aaru.Filesystems/CPM/Structs.cs @@ -35,336 +35,335 @@ using System.Runtime.InteropServices; // ReSharper disable NotAccessedField.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class CPM { - public sealed partial class CPM + /// Most of the times this structure is hard wired or generated by CP/M, not stored on disk + [SuppressMessage("ReSharper", "InconsistentNaming")] + class DiscParameterBlock { - /// Most of the times this structure is hard wired or generated by CP/M, not stored on disk - [SuppressMessage("ReSharper", "InconsistentNaming")] - class DiscParameterBlock - { - /// First byte of allocation bitmap - public byte al0; - /// Second byte of allocation bitmap - public byte al1; - /// Block mask - public byte blm; - /// Block shift - public byte bsh; - /// Checksum vector size - public ushort cks; - /// Directory entries - 1 - public ushort drm; - /// Blocks on disk - 1 - public ushort dsm; - /// Extent mask - public byte exm; - /// Reserved tracks - public ushort off; - /// Physical sector mask - public byte phm; - /// Physical sector shift - public byte psh; - /// Sectors per track - public ushort spt; - } + /// First byte of allocation bitmap + public byte al0; + /// Second byte of allocation bitmap + public byte al1; + /// Block mask + public byte blm; + /// Block shift + public byte bsh; + /// Checksum vector size + public ushort cks; + /// Directory entries - 1 + public ushort drm; + /// Blocks on disk - 1 + public ushort dsm; + /// Extent mask + public byte exm; + /// Reserved tracks + public ushort off; + /// Physical sector mask + public byte phm; + /// Physical sector shift + public byte psh; + /// Sectors per track + public ushort spt; + } - /// Amstrad superblock, for PCW - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AmstradSuperBlock - { - /// Format ID. 0 single-side, 3 double-side. 1 and 2 are for CPC but they don't use the superblock - public readonly byte format; - /// Gives information about side ordering - public readonly byte sidedness; - /// Tracks per side, aka, cylinders - public readonly byte tps; - /// Sectors per track - public readonly byte spt; - /// Physical sector shift - public readonly byte psh; - /// Reserved tracks - public readonly byte off; - /// Block size shift - public readonly byte bsh; - /// How many blocks does the directory take - public readonly byte dirBlocks; - /// GAP#3 length (intersector) - public readonly byte gapLen; - /// GAP#4 length (end-of-track) - public readonly byte formatGap; - /// Must be 0 - public readonly byte zero1; - /// Must be 0 - public readonly byte zero2; - /// Must be 0 - public readonly byte zero3; - /// Must be 0 - public readonly byte zero4; - /// Must be 0 - public readonly byte zero5; - /// Indicates machine the boot code following the superblock is designed to boot - public readonly byte fiddle; - } + /// Amstrad superblock, for PCW + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AmstradSuperBlock + { + /// Format ID. 0 single-side, 3 double-side. 1 and 2 are for CPC but they don't use the superblock + public readonly byte format; + /// Gives information about side ordering + public readonly byte sidedness; + /// Tracks per side, aka, cylinders + public readonly byte tps; + /// Sectors per track + public readonly byte spt; + /// Physical sector shift + public readonly byte psh; + /// Reserved tracks + public readonly byte off; + /// Block size shift + public readonly byte bsh; + /// How many blocks does the directory take + public readonly byte dirBlocks; + /// GAP#3 length (intersector) + public readonly byte gapLen; + /// GAP#4 length (end-of-track) + public readonly byte formatGap; + /// Must be 0 + public readonly byte zero1; + /// Must be 0 + public readonly byte zero2; + /// Must be 0 + public readonly byte zero3; + /// Must be 0 + public readonly byte zero4; + /// Must be 0 + public readonly byte zero5; + /// Indicates machine the boot code following the superblock is designed to boot + public readonly byte fiddle; + } - /// Superblock found on CP/M-86 hard disk volumes - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HardDiskSuperBlock - { - /// Value so the sum of all the superblock's sector bytes taken as 16-bit values gives 0 - public readonly ushort checksum; - /// Copyright string - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1F)] - public readonly byte[] copyright; - /// First cylinder of disk where this volume resides - public readonly ushort firstCylinder; - /// How many cylinders does this volume span - public readonly ushort cylinders; - /// Heads on hard disk - public readonly byte heads; - /// Sectors per track - public readonly byte sectorsPerTrack; - /// Flags, only use by CCP/M where bit 0 equals verify on write - public readonly byte flags; - /// Sector size / 128 - public readonly byte recordsPerSector; - /// - /// - /// - public readonly ushort spt; - /// - /// - /// - public readonly byte bsh; - /// - /// - /// - public readonly byte blm; - /// - /// - /// - public readonly byte exm; - /// - /// - /// - public readonly ushort dsm; - /// - /// - /// - public readonly ushort drm; - /// - /// - /// - public readonly ushort al0; - /// - /// - /// - public readonly ushort al1; - /// - /// - /// - public readonly ushort cks; - /// - /// - /// - public readonly ushort off; - /// Must be zero - public readonly ushort zero1; - /// Must be zero - public readonly ushort zero2; - /// Must be zero - public readonly ushort zero3; - /// Must be zero - public readonly ushort zero4; - /// How many 128 bytes are in a block - public readonly ushort recordsPerBlock; - /// Maximum number of bad blocks in the bad block list - public readonly ushort badBlockWordsMax; - /// Used number of bad blocks in the bad block list - public readonly ushort badBlockWords; - /// First block after the blocks reserved for bad block substitution - public readonly ushort firstSub; - } + /// Superblock found on CP/M-86 hard disk volumes + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HardDiskSuperBlock + { + /// Value so the sum of all the superblock's sector bytes taken as 16-bit values gives 0 + public readonly ushort checksum; + /// Copyright string + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x1F)] + public readonly byte[] copyright; + /// First cylinder of disk where this volume resides + public readonly ushort firstCylinder; + /// How many cylinders does this volume span + public readonly ushort cylinders; + /// Heads on hard disk + public readonly byte heads; + /// Sectors per track + public readonly byte sectorsPerTrack; + /// Flags, only use by CCP/M where bit 0 equals verify on write + public readonly byte flags; + /// Sector size / 128 + public readonly byte recordsPerSector; + /// + /// + /// + public readonly ushort spt; + /// + /// + /// + public readonly byte bsh; + /// + /// + /// + public readonly byte blm; + /// + /// + /// + public readonly byte exm; + /// + /// + /// + public readonly ushort dsm; + /// + /// + /// + public readonly ushort drm; + /// + /// + /// + public readonly ushort al0; + /// + /// + /// + public readonly ushort al1; + /// + /// + /// + public readonly ushort cks; + /// + /// + /// + public readonly ushort off; + /// Must be zero + public readonly ushort zero1; + /// Must be zero + public readonly ushort zero2; + /// Must be zero + public readonly ushort zero3; + /// Must be zero + public readonly ushort zero4; + /// How many 128 bytes are in a block + public readonly ushort recordsPerBlock; + /// Maximum number of bad blocks in the bad block list + public readonly ushort badBlockWordsMax; + /// Used number of bad blocks in the bad block list + public readonly ushort badBlockWords; + /// First block after the blocks reserved for bad block substitution + public readonly ushort firstSub; + } - /// Volume label entry - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct LabelEntry - { - /// Must be 0x20 - public readonly byte signature; - /// Label in ASCII - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] label; - /// - /// Label flags. Bit 0 = label exists, bit 4 = creation timestamp, bit 5 = modification timestamp, bit 6 = access - /// timestamp, bit 7 = password enabled - /// - public readonly byte flags; - /// Password decoder byte - public readonly byte passwordDecoder; - /// Must be 0 - public readonly ushort reserved; - /// Password XOR'ed with - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] password; - /// Label creation time - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] ctime; - /// Label modification time - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] mtime; - } + /// Volume label entry + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct LabelEntry + { + /// Must be 0x20 + public readonly byte signature; + /// Label in ASCII + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] label; + /// + /// Label flags. Bit 0 = label exists, bit 4 = creation timestamp, bit 5 = modification timestamp, bit 6 = access + /// timestamp, bit 7 = password enabled + /// + public readonly byte flags; + /// Password decoder byte + public readonly byte passwordDecoder; + /// Must be 0 + public readonly ushort reserved; + /// Password XOR'ed with + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] password; + /// Label creation time + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] ctime; + /// Label modification time + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] mtime; + } - /// CP/M 3 timestamp entry - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DateEntry - { - /// Must be 0x21 - public readonly byte signature; - /// File 1 create/access timestamp - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] date1; - /// File 1 modification timestamp - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] date2; - /// File 1 password mode - public readonly byte mode1; - public readonly byte zero1; - /// File 2 create/access timestamp - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] date3; - /// File 2 modification timestamp - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] date4; - /// File 2 password mode - public readonly byte mode2; - public readonly byte zero2; - /// File 3 create/access timestamp - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] date5; - /// File 3 modification timestamp - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] date6; - /// File 3 password mode - public readonly byte mode3; - public readonly ushort zero3; - } + /// CP/M 3 timestamp entry + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DateEntry + { + /// Must be 0x21 + public readonly byte signature; + /// File 1 create/access timestamp + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] date1; + /// File 1 modification timestamp + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] date2; + /// File 1 password mode + public readonly byte mode1; + public readonly byte zero1; + /// File 2 create/access timestamp + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] date3; + /// File 2 modification timestamp + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] date4; + /// File 2 password mode + public readonly byte mode2; + public readonly byte zero2; + /// File 3 create/access timestamp + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] date5; + /// File 3 modification timestamp + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] date6; + /// File 3 password mode + public readonly byte mode3; + public readonly ushort zero3; + } - /// Password entry - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PasswordEntry - { - /// 16 + user number - public readonly byte userNumber; - /// Filename - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] filename; - /// Extension - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] extension; - /// Password mode. Bit 7 = required for read, bit 6 = required for write, bit 5 = required for delete - public readonly byte mode; - /// Password decoder byte - public readonly byte passwordDecoder; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] reserved; - /// Password XOR'ed with - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] password; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] reserved2; - } + /// Password entry + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PasswordEntry + { + /// 16 + user number + public readonly byte userNumber; + /// Filename + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] filename; + /// Extension + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] extension; + /// Password mode. Bit 7 = required for read, bit 6 = required for write, bit 5 = required for delete + public readonly byte mode; + /// Password decoder byte + public readonly byte passwordDecoder; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] reserved; + /// Password XOR'ed with + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] password; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] reserved2; + } - /// Timestamp for Z80DOS or DOS+ - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct TrdPartyDateEntry - { - /// Must be 0x21 - public readonly byte signature; - public readonly byte zero; - /// Creation year for file 1 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] create1; - /// Modification time for file 1 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] modify1; - /// Access time for file 1 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] access1; - /// Creation year for file 2 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] create2; - /// Modification time for file 2 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] modify2; - /// Access time for file 2 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] access2; - /// Creation year for file 3 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] create3; - /// Modification time for file 3 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] modify3; - /// Access time for file 3 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] access3; - } + /// Timestamp for Z80DOS or DOS+ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct TrdPartyDateEntry + { + /// Must be 0x21 + public readonly byte signature; + public readonly byte zero; + /// Creation year for file 1 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] create1; + /// Modification time for file 1 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] modify1; + /// Access time for file 1 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] access1; + /// Creation year for file 2 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] create2; + /// Modification time for file 2 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] modify2; + /// Access time for file 2 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] access2; + /// Creation year for file 3 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] create3; + /// Modification time for file 3 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] modify3; + /// Access time for file 3 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] access3; + } - /// Directory entry for <256 allocation blocks - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryEntry - { - /// User number. Bit 7 set in CP/M 1 means hidden - public readonly byte statusUser; - /// Filename and bit 7 as flags - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] filename; - /// Extension and bit 7 as flags - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] extension; - /// Low byte of extent number - public readonly byte extentCounter; - /// - /// Last record bytes. In some implementations it means how many bytes are used in the last record, in others how - /// many bytes are free. It always refer to 128 byte records even if blocks are way bigger, so it's mostly useless. - /// - public readonly byte lastRecordBytes; - /// High byte of extent number - public readonly byte extentCounterHigh; - /// How many records are used in this entry. 0x80 if all are used. - public readonly byte records; - /// Allocation blocks - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] allocations; - } + /// Directory entry for <256 allocation blocks + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryEntry + { + /// User number. Bit 7 set in CP/M 1 means hidden + public readonly byte statusUser; + /// Filename and bit 7 as flags + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] filename; + /// Extension and bit 7 as flags + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] extension; + /// Low byte of extent number + public readonly byte extentCounter; + /// + /// Last record bytes. In some implementations it means how many bytes are used in the last record, in others how + /// many bytes are free. It always refer to 128 byte records even if blocks are way bigger, so it's mostly useless. + /// + public readonly byte lastRecordBytes; + /// High byte of extent number + public readonly byte extentCounterHigh; + /// How many records are used in this entry. 0x80 if all are used. + public readonly byte records; + /// Allocation blocks + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] allocations; + } - /// Directory entry for >256 allocation blocks - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryEntry16 - { - /// User number. Bit 7 set in CP/M 1 means hidden - public readonly byte statusUser; - /// Filename and bit 7 as flags - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] filename; - /// Extension and bit 7 as flags - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] extension; - /// Low byte of extent number - public readonly byte extentCounter; - /// - /// Last record bytes. In some implementations it means how many bytes are used in the last record, in others how - /// many bytes are free. It always refer to 128 byte records even if blocks are way bigger, so it's mostly useless. - /// - public readonly byte lastRecordBytes; - /// High byte of extent number - public readonly byte extentCounterHigh; - /// How many records are used in this entry. 0x80 if all are used. - public readonly byte records; - /// Allocation blocks - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly ushort[] allocations; - } + /// Directory entry for >256 allocation blocks + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryEntry16 + { + /// User number. Bit 7 set in CP/M 1 means hidden + public readonly byte statusUser; + /// Filename and bit 7 as flags + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] filename; + /// Extension and bit 7 as flags + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] extension; + /// Low byte of extent number + public readonly byte extentCounter; + /// + /// Last record bytes. In some implementations it means how many bytes are used in the last record, in others how + /// many bytes are free. It always refer to 128 byte records even if blocks are way bigger, so it's mostly useless. + /// + public readonly byte lastRecordBytes; + /// High byte of extent number + public readonly byte extentCounterHigh; + /// How many records are used in this entry. 0x80 if all are used. + public readonly byte records; + /// Allocation blocks + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly ushort[] allocations; } } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/Super.cs b/Aaru.Filesystems/CPM/Super.cs index 919b608a9..133d31ebb 100644 --- a/Aaru.Filesystems/CPM/Super.cs +++ b/Aaru.Filesystems/CPM/Super.cs @@ -47,447 +47,239 @@ using Schemas; using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; using FileSystemInfo = Aaru.CommonTypes.Structs.FileSystemInfo; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class CPM { - public sealed partial class CPM + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + _device = imagePlugin; + Encoding = encoding ?? Encoding.GetEncoding("IBM437"); + + // As the identification is so complex, just call Identify() and relay on its findings + if(!Identify(_device, partition) || + !_cpmFound || + _workingDefinition == null || + _dpb == null) + return ErrorNumber.InvalidArgument; + + // Build the software interleaving sector mask + if(_workingDefinition.sides == 1) { - _device = imagePlugin; - Encoding = encoding ?? Encoding.GetEncoding("IBM437"); + _sectorMask = new int[_workingDefinition.side1.sectorIds.Length]; - // As the identification is so complex, just call Identify() and relay on its findings - if(!Identify(_device, partition) || - !_cpmFound || - _workingDefinition == null || - _dpb == null) - return ErrorNumber.InvalidArgument; - - // Build the software interleaving sector mask - if(_workingDefinition.sides == 1) + for(int m = 0; m < _sectorMask.Length; m++) + _sectorMask[m] = _workingDefinition.side1.sectorIds[m] - _workingDefinition.side1.sectorIds[0]; + } + else + { + // Head changes after every track + if(string.Compare(_workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) { - _sectorMask = new int[_workingDefinition.side1.sectorIds.Length]; + _sectorMask = new int[_workingDefinition.side1.sectorIds.Length + + _workingDefinition.side2.sectorIds.Length]; - for(int m = 0; m < _sectorMask.Length; m++) + for(int m = 0; m < _workingDefinition.side1.sectorIds.Length; m++) _sectorMask[m] = _workingDefinition.side1.sectorIds[m] - _workingDefinition.side1.sectorIds[0]; + + // Skip first track (first side) + for(int m = 0; m < _workingDefinition.side2.sectorIds.Length; m++) + _sectorMask[m + _workingDefinition.side1.sectorIds.Length] = + _workingDefinition.side2.sectorIds[m] - _workingDefinition.side2.sectorIds[0] + + _workingDefinition.side1.sectorIds.Length; + } + + // Head changes after whole side + else if(string.Compare(_workingDefinition.order, "CYLINDERS", + StringComparison.InvariantCultureIgnoreCase) == 0) + { + for(int m = 0; m < _workingDefinition.side1.sectorIds.Length; m++) + _sectorMask[m] = _workingDefinition.side1.sectorIds[m] - _workingDefinition.side1.sectorIds[0]; + + // Skip first track (first side) and first track (second side) + for(int m = 0; m < _workingDefinition.side1.sectorIds.Length; m++) + _sectorMask[m + _workingDefinition.side1.sectorIds.Length] = + _workingDefinition.side1.sectorIds[m] - _workingDefinition.side1.sectorIds[0] + + _workingDefinition.side1.sectorIds.Length + _workingDefinition.side2.sectorIds.Length; + + // TODO: Implement CYLINDERS ordering + AaruConsole.DebugWriteLine("CP/M Plugin", "CYLINDERS ordering not yet implemented."); + + return ErrorNumber.NotImplemented; + } + + // TODO: Implement COLUMBIA ordering + else if(string.Compare(_workingDefinition.order, "COLUMBIA", + StringComparison.InvariantCultureIgnoreCase) == 0) + { + AaruConsole.DebugWriteLine("CP/M Plugin", + "Don't know how to handle COLUMBIA ordering, not proceeding with this definition."); + + return ErrorNumber.NotImplemented; + } + + // TODO: Implement EAGLE ordering + else if(string.Compare(_workingDefinition.order, "EAGLE", + StringComparison.InvariantCultureIgnoreCase) == 0) + { + AaruConsole.DebugWriteLine("CP/M Plugin", + "Don't know how to handle EAGLE ordering, not proceeding with this definition."); + + return ErrorNumber.NotImplemented; } else { - // Head changes after every track - if(string.Compare(_workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) - { - _sectorMask = new int[_workingDefinition.side1.sectorIds.Length + - _workingDefinition.side2.sectorIds.Length]; + AaruConsole.DebugWriteLine("CP/M Plugin", + "Unknown order type \"{0}\", not proceeding with this definition.", + _workingDefinition.order); - for(int m = 0; m < _workingDefinition.side1.sectorIds.Length; m++) - _sectorMask[m] = _workingDefinition.side1.sectorIds[m] - _workingDefinition.side1.sectorIds[0]; - - // Skip first track (first side) - for(int m = 0; m < _workingDefinition.side2.sectorIds.Length; m++) - _sectorMask[m + _workingDefinition.side1.sectorIds.Length] = - _workingDefinition.side2.sectorIds[m] - _workingDefinition.side2.sectorIds[0] + - _workingDefinition.side1.sectorIds.Length; - } - - // Head changes after whole side - else if(string.Compare(_workingDefinition.order, "CYLINDERS", - StringComparison.InvariantCultureIgnoreCase) == 0) - { - for(int m = 0; m < _workingDefinition.side1.sectorIds.Length; m++) - _sectorMask[m] = _workingDefinition.side1.sectorIds[m] - _workingDefinition.side1.sectorIds[0]; - - // Skip first track (first side) and first track (second side) - for(int m = 0; m < _workingDefinition.side1.sectorIds.Length; m++) - _sectorMask[m + _workingDefinition.side1.sectorIds.Length] = - _workingDefinition.side1.sectorIds[m] - _workingDefinition.side1.sectorIds[0] + - _workingDefinition.side1.sectorIds.Length + _workingDefinition.side2.sectorIds.Length; - - // TODO: Implement CYLINDERS ordering - AaruConsole.DebugWriteLine("CP/M Plugin", "CYLINDERS ordering not yet implemented."); - - return ErrorNumber.NotImplemented; - } - - // TODO: Implement COLUMBIA ordering - else if(string.Compare(_workingDefinition.order, "COLUMBIA", - StringComparison.InvariantCultureIgnoreCase) == 0) - { - AaruConsole.DebugWriteLine("CP/M Plugin", - "Don't know how to handle COLUMBIA ordering, not proceeding with this definition."); - - return ErrorNumber.NotImplemented; - } - - // TODO: Implement EAGLE ordering - else if(string.Compare(_workingDefinition.order, "EAGLE", - StringComparison.InvariantCultureIgnoreCase) == 0) - { - AaruConsole.DebugWriteLine("CP/M Plugin", - "Don't know how to handle EAGLE ordering, not proceeding with this definition."); - - return ErrorNumber.NotImplemented; - } - else - { - AaruConsole.DebugWriteLine("CP/M Plugin", - "Unknown order type \"{0}\", not proceeding with this definition.", - _workingDefinition.order); - - return ErrorNumber.NotSupported; - } + return ErrorNumber.NotSupported; } + } - // Deinterleave whole volume - Dictionary deinterleavedSectors = new(); + // Deinterleave whole volume + Dictionary deinterleavedSectors = new(); - if(_workingDefinition.sides == 1 || - string.Compare(_workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) + if(_workingDefinition.sides == 1 || + string.Compare(_workingDefinition.order, "SIDES", StringComparison.InvariantCultureIgnoreCase) == 0) + { + AaruConsole.DebugWriteLine("CP/M Plugin", "Deinterleaving whole volume."); + + for(int p = 0; p <= (int)(partition.End - partition.Start); p++) { - AaruConsole.DebugWriteLine("CP/M Plugin", "Deinterleaving whole volume."); + ErrorNumber errno = + _device. + ReadSector((ulong)((int)partition.Start + (p / _sectorMask.Length * _sectorMask.Length) + _sectorMask[p % _sectorMask.Length]), + out byte[] readSector); - for(int p = 0; p <= (int)(partition.End - partition.Start); p++) - { - ErrorNumber errno = - _device. - ReadSector((ulong)((int)partition.Start + (p / _sectorMask.Length * _sectorMask.Length) + _sectorMask[p % _sectorMask.Length]), - out byte[] readSector); + if(errno != ErrorNumber.NoError) + return errno; - if(errno != ErrorNumber.NoError) - return errno; + if(_workingDefinition.complement) + for(int b = 0; b < readSector.Length; b++) + readSector[b] = (byte)(~readSector[b] & 0xFF); - if(_workingDefinition.complement) - for(int b = 0; b < readSector.Length; b++) - readSector[b] = (byte)(~readSector[b] & 0xFF); - - deinterleavedSectors.Add((ulong)p, readSector); - } + deinterleavedSectors.Add((ulong)p, readSector); } + } - int blockSize = 128 << _dpb.bsh; - var blockMs = new MemoryStream(); - ulong blockNo = 0; - int sectorsPerBlock = 0; - Dictionary allocationBlocks = new(); + int blockSize = 128 << _dpb.bsh; + var blockMs = new MemoryStream(); + ulong blockNo = 0; + int sectorsPerBlock = 0; + Dictionary allocationBlocks = new(); - AaruConsole.DebugWriteLine("CP/M Plugin", "Creating allocation blocks."); + AaruConsole.DebugWriteLine("CP/M Plugin", "Creating allocation blocks."); - // For each volume sector - for(ulong a = 0; a < (ulong)deinterleavedSectors.Count; a++) + // For each volume sector + for(ulong a = 0; a < (ulong)deinterleavedSectors.Count; a++) + { + deinterleavedSectors.TryGetValue(a, out byte[] sector); + + // May it happen? Just in case, CP/M blocks are smaller than physical sectors + if(sector.Length > blockSize) + for(int i = 0; i < sector.Length / blockSize; i++) + { + byte[] tmp = new byte[blockSize]; + Array.Copy(sector, blockSize * i, tmp, 0, blockSize); + allocationBlocks.Add(blockNo++, tmp); + } + + // CP/M blocks are larger than physical sectors + else if(sector.Length < blockSize) { - deinterleavedSectors.TryGetValue(a, out byte[] sector); + blockMs.Write(sector, 0, sector.Length); + sectorsPerBlock++; - // May it happen? Just in case, CP/M blocks are smaller than physical sectors - if(sector.Length > blockSize) - for(int i = 0; i < sector.Length / blockSize; i++) - { - byte[] tmp = new byte[blockSize]; - Array.Copy(sector, blockSize * i, tmp, 0, blockSize); - allocationBlocks.Add(blockNo++, tmp); - } + if(sectorsPerBlock != blockSize / sector.Length) + continue; - // CP/M blocks are larger than physical sectors - else if(sector.Length < blockSize) - { - blockMs.Write(sector, 0, sector.Length); - sectorsPerBlock++; - - if(sectorsPerBlock != blockSize / sector.Length) - continue; - - allocationBlocks.Add(blockNo++, blockMs.ToArray()); - sectorsPerBlock = 0; - blockMs = new MemoryStream(); - } - - // CP/M blocks are same size than physical sectors - else - allocationBlocks.Add(blockNo++, sector); + allocationBlocks.Add(blockNo++, blockMs.ToArray()); + sectorsPerBlock = 0; + blockMs = new MemoryStream(); } - AaruConsole.DebugWriteLine("CP/M Plugin", "Reading directory."); - - int dirOff; - int dirSectors = (_dpb.drm + 1) * 32 / _workingDefinition.bytesPerSector; - - if(_workingDefinition.sofs > 0) - dirOff = _workingDefinition.sofs; + // CP/M blocks are same size than physical sectors else - dirOff = _workingDefinition.ofs * _workingDefinition.sectorsPerTrack; + allocationBlocks.Add(blockNo++, sector); + } - // Read the whole directory blocks - var dirMs = new MemoryStream(); + AaruConsole.DebugWriteLine("CP/M Plugin", "Reading directory."); - for(int d = 0; d < dirSectors; d++) - { - deinterleavedSectors.TryGetValue((ulong)(d + dirOff), out byte[] sector); - dirMs.Write(sector, 0, sector.Length); - } + int dirOff; + int dirSectors = (_dpb.drm + 1) * 32 / _workingDefinition.bytesPerSector; - byte[] directory = dirMs.ToArray(); + if(_workingDefinition.sofs > 0) + dirOff = _workingDefinition.sofs; + else + dirOff = _workingDefinition.ofs * _workingDefinition.sectorsPerTrack; - if(directory == null) - return ErrorNumber.InvalidArgument; + // Read the whole directory blocks + var dirMs = new MemoryStream(); - int dirCnt = 0; - string file1 = null; - string file2 = null; - string file3 = null; + for(int d = 0; d < dirSectors; d++) + { + deinterleavedSectors.TryGetValue((ulong)(d + dirOff), out byte[] sector); + dirMs.Write(sector, 0, sector.Length); + } - Dictionary>> fileExtents = new(); + byte[] directory = dirMs.ToArray(); - _statCache = new Dictionary(); - _cpmStat = new FileSystemInfo(); - bool atime = false; - _dirList = new List(); - _labelCreationDate = null; - _labelUpdateDate = null; - _passwordCache = new Dictionary(); + if(directory == null) + return ErrorNumber.InvalidArgument; - AaruConsole.DebugWriteLine("CP/M Plugin", "Traversing directory."); + int dirCnt = 0; + string file1 = null; + string file2 = null; + string file3 = null; - // For each directory entry - for(int dOff = 0; dOff < directory.Length; dOff += 32) + Dictionary>> fileExtents = new(); - // Describes a file (does not support PDOS entries with user >= 16, because they're identical to password entries - if((directory[dOff] & 0x7F) < 0x10) - if(allocationBlocks.Count > 256) - { - DirectoryEntry16 entry = - Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); + _statCache = new Dictionary(); + _cpmStat = new FileSystemInfo(); + bool atime = false; + _dirList = new List(); + _labelCreationDate = null; + _labelUpdateDate = null; + _passwordCache = new Dictionary(); - bool hidden = (entry.statusUser & 0x80) == 0x80; - bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80; - bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80; + AaruConsole.DebugWriteLine("CP/M Plugin", "Traversing directory."); - //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80; - int user = entry.statusUser & 0x0F; + // For each directory entry + for(int dOff = 0; dOff < directory.Length; dOff += 32) - bool validEntry = true; - - for(int i = 0; i < 8; i++) - { - entry.filename[i] &= 0x7F; - validEntry &= entry.filename[i] >= 0x20; - } - - for(int i = 0; i < 3; i++) - { - entry.extension[i] &= 0x7F; - validEntry &= entry.extension[i] >= 0x20; - } - - if(!validEntry) - continue; - - string filename = Encoding.ASCII.GetString(entry.filename).Trim(); - string extension = Encoding.ASCII.GetString(entry.extension).Trim(); - - // If user is != 0, append user to name to have identical filenames - if(user > 0) - filename = $"{user:X1}:{filename}"; - - if(!string.IsNullOrEmpty(extension)) - filename = filename + "." + extension; - - filename = filename.Replace('/', '\u2215'); - - int entryNo = ((32 * entry.extentCounter) + entry.extentCounterHigh) / (_dpb.exm + 1); - - // Do we have a stat for the file already? - if(_statCache.TryGetValue(filename, out FileEntryInfo fInfo)) - _statCache.Remove(filename); - else - fInfo = new FileEntryInfo - { - Attributes = new FileAttributes() - }; - - // And any extent? - if(fileExtents.TryGetValue(filename, out Dictionary> extentBlocks)) - fileExtents.Remove(filename); - else - extentBlocks = new Dictionary>(); - - // Do we already have this extent? Should never happen - if(extentBlocks.TryGetValue(entryNo, out List blocks)) - extentBlocks.Remove(entryNo); - else - blocks = new List(); - - // Attributes - if(hidden) - fInfo.Attributes |= FileAttributes.Hidden; - - if(rdOnly) - fInfo.Attributes |= FileAttributes.ReadOnly; - - if(system) - fInfo.Attributes |= FileAttributes.System; - - // Supposedly there is a value in the directory entry telling how many blocks are designated in - // this entry. However some implementations tend to do whatever they wish, but none will ever - // allocate block 0 for a file because that's where the directory resides. - // There is also a field telling how many bytes are used in the last block, but its meaning is - // non-standard so we must ignore it. - foreach(ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0)) - blocks.Add(blk); - - // Save the file - fInfo.UID = (ulong)user; - extentBlocks.Add(entryNo, blocks); - fileExtents.Add(filename, extentBlocks); - _statCache.Add(filename, fInfo); - - // Add the file to the directory listing - if(!_dirList.Contains(filename)) - _dirList.Add(filename); - - // Count entries 3 by 3 for timestamps - switch(dirCnt % 3) - { - case 0: - file1 = filename; - - break; - case 1: - file2 = filename; - - break; - case 2: - file3 = filename; - - break; - } - - dirCnt++; - } - else - { - DirectoryEntry entry = - Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); - - bool hidden = (entry.statusUser & 0x80) == 0x80; - bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80; - bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80; - - //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80; - int user = entry.statusUser & 0x0F; - - bool validEntry = true; - - for(int i = 0; i < 8; i++) - { - entry.filename[i] &= 0x7F; - validEntry &= entry.filename[i] >= 0x20; - } - - for(int i = 0; i < 3; i++) - { - entry.extension[i] &= 0x7F; - validEntry &= entry.extension[i] >= 0x20; - } - - if(!validEntry) - continue; - - string filename = Encoding.ASCII.GetString(entry.filename).Trim(); - string extension = Encoding.ASCII.GetString(entry.extension).Trim(); - - // If user is != 0, append user to name to have identical filenames - if(user > 0) - filename = $"{user:X1}:{filename}"; - - if(!string.IsNullOrEmpty(extension)) - filename = filename + "." + extension; - - filename = filename.Replace('/', '\u2215'); - - int entryNo = ((32 * entry.extentCounterHigh) + entry.extentCounter) / (_dpb.exm + 1); - - // Do we have a stat for the file already? - if(_statCache.TryGetValue(filename, out FileEntryInfo fInfo)) - _statCache.Remove(filename); - else - fInfo = new FileEntryInfo - { - Attributes = new FileAttributes() - }; - - // And any extent? - if(fileExtents.TryGetValue(filename, out Dictionary> extentBlocks)) - fileExtents.Remove(filename); - else - extentBlocks = new Dictionary>(); - - // Do we already have this extent? Should never happen - if(extentBlocks.TryGetValue(entryNo, out List blocks)) - extentBlocks.Remove(entryNo); - else - blocks = new List(); - - // Attributes - if(hidden) - fInfo.Attributes |= FileAttributes.Hidden; - - if(rdOnly) - fInfo.Attributes |= FileAttributes.ReadOnly; - - if(system) - fInfo.Attributes |= FileAttributes.System; - - // Supposedly there is a value in the directory entry telling how many blocks are designated in - // this entry. However some implementations tend to do whatever they wish, but none will ever - // allocate block 0 for a file because that's where the directory resides. - // There is also a field telling how many bytes are used in the last block, but its meaning is - // non-standard so we must ignore it. - foreach(ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0)) - blocks.Add(blk); - - // Save the file - fInfo.UID = (ulong)user; - extentBlocks.Add(entryNo, blocks); - fileExtents.Add(filename, extentBlocks); - _statCache.Add(filename, fInfo); - - // Add the file to the directory listing - if(!_dirList.Contains(filename)) - _dirList.Add(filename); - - // Count entries 3 by 3 for timestamps - switch(dirCnt % 3) - { - case 0: - file1 = filename; - - break; - case 1: - file2 = filename; - - break; - case 2: - file3 = filename; - - break; - } - - dirCnt++; - } - - // A password entry (or a file entry in PDOS, but this does not handle that case) - else if((directory[dOff] & 0x7F) >= 0x10 && - (directory[dOff] & 0x7F) < 0x20) + // Describes a file (does not support PDOS entries with user >= 16, because they're identical to password entries + if((directory[dOff] & 0x7F) < 0x10) + if(allocationBlocks.Count > 256) { - PasswordEntry entry = Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); + DirectoryEntry16 entry = + Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); - int user = entry.userNumber & 0x0F; + bool hidden = (entry.statusUser & 0x80) == 0x80; + bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80; + bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80; + + //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80; + int user = entry.statusUser & 0x0F; + + bool validEntry = true; for(int i = 0; i < 8; i++) + { entry.filename[i] &= 0x7F; + validEntry &= entry.filename[i] >= 0x20; + } for(int i = 0; i < 3; i++) + { entry.extension[i] &= 0x7F; + validEntry &= entry.extension[i] >= 0x20; + } + + if(!validEntry) + continue; string filename = Encoding.ASCII.GetString(entry.filename).Trim(); string extension = Encoding.ASCII.GetString(entry.extension).Trim(); @@ -501,14 +293,167 @@ namespace Aaru.Filesystems filename = filename.Replace('/', '\u2215'); - // Do not repeat passwords - if(_passwordCache.ContainsKey(filename)) - _passwordCache.Remove(filename); + int entryNo = ((32 * entry.extentCounter) + entry.extentCounterHigh) / (_dpb.exm + 1); - // Copy whole password entry - byte[] tmp = new byte[32]; - Array.Copy(directory, dOff, tmp, 0, 32); - _passwordCache.Add(filename, tmp); + // Do we have a stat for the file already? + if(_statCache.TryGetValue(filename, out FileEntryInfo fInfo)) + _statCache.Remove(filename); + else + fInfo = new FileEntryInfo + { + Attributes = new FileAttributes() + }; + + // And any extent? + if(fileExtents.TryGetValue(filename, out Dictionary> extentBlocks)) + fileExtents.Remove(filename); + else + extentBlocks = new Dictionary>(); + + // Do we already have this extent? Should never happen + if(extentBlocks.TryGetValue(entryNo, out List blocks)) + extentBlocks.Remove(entryNo); + else + blocks = new List(); + + // Attributes + if(hidden) + fInfo.Attributes |= FileAttributes.Hidden; + + if(rdOnly) + fInfo.Attributes |= FileAttributes.ReadOnly; + + if(system) + fInfo.Attributes |= FileAttributes.System; + + // Supposedly there is a value in the directory entry telling how many blocks are designated in + // this entry. However some implementations tend to do whatever they wish, but none will ever + // allocate block 0 for a file because that's where the directory resides. + // There is also a field telling how many bytes are used in the last block, but its meaning is + // non-standard so we must ignore it. + foreach(ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0)) + blocks.Add(blk); + + // Save the file + fInfo.UID = (ulong)user; + extentBlocks.Add(entryNo, blocks); + fileExtents.Add(filename, extentBlocks); + _statCache.Add(filename, fInfo); + + // Add the file to the directory listing + if(!_dirList.Contains(filename)) + _dirList.Add(filename); + + // Count entries 3 by 3 for timestamps + switch(dirCnt % 3) + { + case 0: + file1 = filename; + + break; + case 1: + file2 = filename; + + break; + case 2: + file3 = filename; + + break; + } + + dirCnt++; + } + else + { + DirectoryEntry entry = + Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); + + bool hidden = (entry.statusUser & 0x80) == 0x80; + bool rdOnly = (entry.filename[0] & 0x80) == 0x80 || (entry.extension[0] & 0x80) == 0x80; + bool system = (entry.filename[1] & 0x80) == 0x80 || (entry.extension[2] & 0x80) == 0x80; + + //bool backed = (entry.filename[3] & 0x80) == 0x80 || (entry.extension[3] & 0x80) == 0x80; + int user = entry.statusUser & 0x0F; + + bool validEntry = true; + + for(int i = 0; i < 8; i++) + { + entry.filename[i] &= 0x7F; + validEntry &= entry.filename[i] >= 0x20; + } + + for(int i = 0; i < 3; i++) + { + entry.extension[i] &= 0x7F; + validEntry &= entry.extension[i] >= 0x20; + } + + if(!validEntry) + continue; + + string filename = Encoding.ASCII.GetString(entry.filename).Trim(); + string extension = Encoding.ASCII.GetString(entry.extension).Trim(); + + // If user is != 0, append user to name to have identical filenames + if(user > 0) + filename = $"{user:X1}:{filename}"; + + if(!string.IsNullOrEmpty(extension)) + filename = filename + "." + extension; + + filename = filename.Replace('/', '\u2215'); + + int entryNo = ((32 * entry.extentCounterHigh) + entry.extentCounter) / (_dpb.exm + 1); + + // Do we have a stat for the file already? + if(_statCache.TryGetValue(filename, out FileEntryInfo fInfo)) + _statCache.Remove(filename); + else + fInfo = new FileEntryInfo + { + Attributes = new FileAttributes() + }; + + // And any extent? + if(fileExtents.TryGetValue(filename, out Dictionary> extentBlocks)) + fileExtents.Remove(filename); + else + extentBlocks = new Dictionary>(); + + // Do we already have this extent? Should never happen + if(extentBlocks.TryGetValue(entryNo, out List blocks)) + extentBlocks.Remove(entryNo); + else + blocks = new List(); + + // Attributes + if(hidden) + fInfo.Attributes |= FileAttributes.Hidden; + + if(rdOnly) + fInfo.Attributes |= FileAttributes.ReadOnly; + + if(system) + fInfo.Attributes |= FileAttributes.System; + + // Supposedly there is a value in the directory entry telling how many blocks are designated in + // this entry. However some implementations tend to do whatever they wish, but none will ever + // allocate block 0 for a file because that's where the directory resides. + // There is also a field telling how many bytes are used in the last block, but its meaning is + // non-standard so we must ignore it. + foreach(ushort blk in entry.allocations.Where(blk => !blocks.Contains(blk) && blk != 0)) + blocks.Add(blk); + + // Save the file + fInfo.UID = (ulong)user; + extentBlocks.Add(entryNo, blocks); + fileExtents.Add(filename, extentBlocks); + _statCache.Add(filename, fInfo); + + // Add the file to the directory listing + if(!_dirList.Contains(filename)) + _dirList.Add(filename); // Count entries 3 by 3 for timestamps switch(dirCnt % 3) @@ -530,309 +475,363 @@ namespace Aaru.Filesystems dirCnt++; } - // Volume label and password entry. Volume password is ignored. - else - switch(directory[dOff] & 0x7F) - { - case 0x20: - LabelEntry labelEntry = - Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); - - // The volume label defines if one of the fields in CP/M 3 timestamp is a creation or an - // access time - atime |= (labelEntry.flags & 0x40) == 0x40; - - _label = Encoding.ASCII.GetString(directory, dOff + 1, 11).Trim(); - _labelCreationDate = new byte[4]; - _labelUpdateDate = new byte[4]; - Array.Copy(directory, dOff + 24, _labelCreationDate, 0, 4); - Array.Copy(directory, dOff + 28, _labelUpdateDate, 0, 4); - - // Count entries 3 by 3 for timestamps - switch(dirCnt % 3) - { - case 0: - file1 = null; - - break; - case 1: - file2 = null; - - break; - case 2: - file3 = null; - - break; - } - - dirCnt++; - - break; - case 0x21: - if(directory[dOff + 10] == 0x00 && - directory[dOff + 20] == 0x00 && - directory[dOff + 30] == 0x00 && - directory[dOff + 31] == 0x00) - { - DateEntry dateEntry = - Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); - - FileEntryInfo fInfo; - - // Entry contains timestamps for last 3 entries, whatever the kind they are. - if(!string.IsNullOrEmpty(file1)) - { - if(_statCache.TryGetValue(file1, out fInfo)) - _statCache.Remove(file1); - else - fInfo = new FileEntryInfo(); - - if(atime) - fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date1); - else - fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date1); - - fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date2); - - _statCache.Add(file1, fInfo); - } - - if(!string.IsNullOrEmpty(file2)) - { - if(_statCache.TryGetValue(file2, out fInfo)) - _statCache.Remove(file2); - else - fInfo = new FileEntryInfo(); - - if(atime) - fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date3); - else - fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date3); - - fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date4); - - _statCache.Add(file2, fInfo); - } - - if(!string.IsNullOrEmpty(file3)) - { - if(_statCache.TryGetValue(file3, out fInfo)) - _statCache.Remove(file3); - else - fInfo = new FileEntryInfo(); - - if(atime) - fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date5); - else - fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date5); - - fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date6); - - _statCache.Add(file3, fInfo); - } - - file1 = null; - file2 = null; - file3 = null; - dirCnt = 0; - } - - // However, if this byte is 0, timestamp is in Z80DOS or DOS+ format - else if(directory[dOff + 1] == 0x00) - { - TrdPartyDateEntry trdPartyDateEntry = - Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); - - FileEntryInfo fInfo; - - // Entry contains timestamps for last 3 entries, whatever the kind they are. - if(!string.IsNullOrEmpty(file1)) - { - if(_statCache.TryGetValue(file1, out fInfo)) - _statCache.Remove(file1); - else - fInfo = new FileEntryInfo(); - - byte[] ctime = new byte[4]; - ctime[0] = trdPartyDateEntry.create1[0]; - ctime[1] = trdPartyDateEntry.create1[1]; - - fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access1); - fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); - fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify1); - - _statCache.Add(file1, fInfo); - } - - if(!string.IsNullOrEmpty(file2)) - { - if(_statCache.TryGetValue(file2, out fInfo)) - _statCache.Remove(file2); - else - fInfo = new FileEntryInfo(); - - byte[] ctime = new byte[4]; - ctime[0] = trdPartyDateEntry.create2[0]; - ctime[1] = trdPartyDateEntry.create2[1]; - - fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access2); - fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); - fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify2); - - _statCache.Add(file2, fInfo); - } - - if(!string.IsNullOrEmpty(file3)) - { - if(_statCache.TryGetValue(file1, out fInfo)) - _statCache.Remove(file3); - else - fInfo = new FileEntryInfo(); - - byte[] ctime = new byte[4]; - ctime[0] = trdPartyDateEntry.create3[0]; - ctime[1] = trdPartyDateEntry.create3[1]; - - fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access3); - fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); - fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify3); - - _statCache.Add(file3, fInfo); - } - - file1 = null; - file2 = null; - file3 = null; - dirCnt = 0; - } - - break; - } - - // Cache all files. As CP/M maximum volume size is 8 Mib - // this should not be a problem - AaruConsole.DebugWriteLine("CP/M Plugin", "Reading files."); - long usedBlocks = 0; - _fileCache = new Dictionary(); - - foreach(string filename in _dirList) + // A password entry (or a file entry in PDOS, but this does not handle that case) + else if((directory[dOff] & 0x7F) >= 0x10 && + (directory[dOff] & 0x7F) < 0x20) { - var fileMs = new MemoryStream(); + PasswordEntry entry = Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); - if(_statCache.TryGetValue(filename, out FileEntryInfo fInfo)) - _statCache.Remove(filename); + int user = entry.userNumber & 0x0F; - fInfo.Blocks = 0; + for(int i = 0; i < 8; i++) + entry.filename[i] &= 0x7F; - if(fileExtents.TryGetValue(filename, out Dictionary> extents)) - for(int ex = 0; ex < extents.Count; ex++) - { - if(!extents.TryGetValue(ex, out List alBlks)) - continue; + for(int i = 0; i < 3; i++) + entry.extension[i] &= 0x7F; - foreach(ushort alBlk in alBlks) - { - allocationBlocks.TryGetValue(alBlk, out byte[] blk); - fileMs.Write(blk, 0, blk.Length); - fInfo.Blocks++; - } - } + string filename = Encoding.ASCII.GetString(entry.filename).Trim(); + string extension = Encoding.ASCII.GetString(entry.extension).Trim(); - // If you insist to call CP/M "extent based" - fInfo.Attributes |= FileAttributes.Extents; - fInfo.BlockSize = blockSize; - fInfo.Length = fileMs.Length; - _cpmStat.Files++; - usedBlocks += fInfo.Blocks; + // If user is != 0, append user to name to have identical filenames + if(user > 0) + filename = $"{user:X1}:{filename}"; - _statCache.Add(filename, fInfo); - _fileCache.Add(filename, fileMs.ToArray()); - } + if(!string.IsNullOrEmpty(extension)) + filename = filename + "." + extension; - _decodedPasswordCache = new Dictionary(); + filename = filename.Replace('/', '\u2215'); - // For each stored password, store a decoded version of it - if(_passwordCache.Count > 0) - foreach(KeyValuePair kvp in _passwordCache) + // Do not repeat passwords + if(_passwordCache.ContainsKey(filename)) + _passwordCache.Remove(filename); + + // Copy whole password entry + byte[] tmp = new byte[32]; + Array.Copy(directory, dOff, tmp, 0, 32); + _passwordCache.Add(filename, tmp); + + // Count entries 3 by 3 for timestamps + switch(dirCnt % 3) { - byte[] tmp = new byte[8]; - Array.Copy(kvp.Value, 16, tmp, 0, 8); + case 0: + file1 = filename; - for(int t = 0; t < 8; t++) - tmp[t] ^= kvp.Value[13]; + break; + case 1: + file2 = filename; - _decodedPasswordCache.Add(kvp.Key, tmp); + break; + case 2: + file3 = filename; + + break; } - // Generate statfs. - _cpmStat.Blocks = (ulong)(_dpb.dsm + 1); - _cpmStat.FilenameLength = 11; - _cpmStat.Files = (ulong)_fileCache.Count; - _cpmStat.FreeBlocks = _cpmStat.Blocks - (ulong)usedBlocks; - _cpmStat.PluginId = Id; - _cpmStat.Type = "CP/M filesystem"; - - // Generate XML info - XmlFsType = new FileSystemType - { - Clusters = _cpmStat.Blocks, - ClusterSize = (uint)blockSize, - Files = (ulong)_fileCache.Count, - FilesSpecified = true, - FreeClusters = _cpmStat.FreeBlocks, - FreeClustersSpecified = true, - Type = "CP/M filesystem" - }; - - if(_labelCreationDate != null) - { - XmlFsType.CreationDate = DateHandlers.CpmToDateTime(_labelCreationDate); - XmlFsType.CreationDateSpecified = true; + dirCnt++; } - if(_labelUpdateDate != null) + // Volume label and password entry. Volume password is ignored. + else + switch(directory[dOff] & 0x7F) + { + case 0x20: + LabelEntry labelEntry = + Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); + + // The volume label defines if one of the fields in CP/M 3 timestamp is a creation or an + // access time + atime |= (labelEntry.flags & 0x40) == 0x40; + + _label = Encoding.ASCII.GetString(directory, dOff + 1, 11).Trim(); + _labelCreationDate = new byte[4]; + _labelUpdateDate = new byte[4]; + Array.Copy(directory, dOff + 24, _labelCreationDate, 0, 4); + Array.Copy(directory, dOff + 28, _labelUpdateDate, 0, 4); + + // Count entries 3 by 3 for timestamps + switch(dirCnt % 3) + { + case 0: + file1 = null; + + break; + case 1: + file2 = null; + + break; + case 2: + file3 = null; + + break; + } + + dirCnt++; + + break; + case 0x21: + if(directory[dOff + 10] == 0x00 && + directory[dOff + 20] == 0x00 && + directory[dOff + 30] == 0x00 && + directory[dOff + 31] == 0x00) + { + DateEntry dateEntry = + Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); + + FileEntryInfo fInfo; + + // Entry contains timestamps for last 3 entries, whatever the kind they are. + if(!string.IsNullOrEmpty(file1)) + { + if(_statCache.TryGetValue(file1, out fInfo)) + _statCache.Remove(file1); + else + fInfo = new FileEntryInfo(); + + if(atime) + fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date1); + else + fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date1); + + fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date2); + + _statCache.Add(file1, fInfo); + } + + if(!string.IsNullOrEmpty(file2)) + { + if(_statCache.TryGetValue(file2, out fInfo)) + _statCache.Remove(file2); + else + fInfo = new FileEntryInfo(); + + if(atime) + fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date3); + else + fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date3); + + fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date4); + + _statCache.Add(file2, fInfo); + } + + if(!string.IsNullOrEmpty(file3)) + { + if(_statCache.TryGetValue(file3, out fInfo)) + _statCache.Remove(file3); + else + fInfo = new FileEntryInfo(); + + if(atime) + fInfo.AccessTime = DateHandlers.CpmToDateTime(dateEntry.date5); + else + fInfo.CreationTime = DateHandlers.CpmToDateTime(dateEntry.date5); + + fInfo.LastWriteTime = DateHandlers.CpmToDateTime(dateEntry.date6); + + _statCache.Add(file3, fInfo); + } + + file1 = null; + file2 = null; + file3 = null; + dirCnt = 0; + } + + // However, if this byte is 0, timestamp is in Z80DOS or DOS+ format + else if(directory[dOff + 1] == 0x00) + { + TrdPartyDateEntry trdPartyDateEntry = + Marshal.ByteArrayToStructureLittleEndian(directory, dOff, 32); + + FileEntryInfo fInfo; + + // Entry contains timestamps for last 3 entries, whatever the kind they are. + if(!string.IsNullOrEmpty(file1)) + { + if(_statCache.TryGetValue(file1, out fInfo)) + _statCache.Remove(file1); + else + fInfo = new FileEntryInfo(); + + byte[] ctime = new byte[4]; + ctime[0] = trdPartyDateEntry.create1[0]; + ctime[1] = trdPartyDateEntry.create1[1]; + + fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access1); + fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); + fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify1); + + _statCache.Add(file1, fInfo); + } + + if(!string.IsNullOrEmpty(file2)) + { + if(_statCache.TryGetValue(file2, out fInfo)) + _statCache.Remove(file2); + else + fInfo = new FileEntryInfo(); + + byte[] ctime = new byte[4]; + ctime[0] = trdPartyDateEntry.create2[0]; + ctime[1] = trdPartyDateEntry.create2[1]; + + fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access2); + fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); + fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify2); + + _statCache.Add(file2, fInfo); + } + + if(!string.IsNullOrEmpty(file3)) + { + if(_statCache.TryGetValue(file1, out fInfo)) + _statCache.Remove(file3); + else + fInfo = new FileEntryInfo(); + + byte[] ctime = new byte[4]; + ctime[0] = trdPartyDateEntry.create3[0]; + ctime[1] = trdPartyDateEntry.create3[1]; + + fInfo.AccessTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.access3); + fInfo.CreationTime = DateHandlers.CpmToDateTime(ctime); + fInfo.LastWriteTime = DateHandlers.CpmToDateTime(trdPartyDateEntry.modify3); + + _statCache.Add(file3, fInfo); + } + + file1 = null; + file2 = null; + file3 = null; + dirCnt = 0; + } + + break; + } + + // Cache all files. As CP/M maximum volume size is 8 Mib + // this should not be a problem + AaruConsole.DebugWriteLine("CP/M Plugin", "Reading files."); + long usedBlocks = 0; + _fileCache = new Dictionary(); + + foreach(string filename in _dirList) + { + var fileMs = new MemoryStream(); + + if(_statCache.TryGetValue(filename, out FileEntryInfo fInfo)) + _statCache.Remove(filename); + + fInfo.Blocks = 0; + + if(fileExtents.TryGetValue(filename, out Dictionary> extents)) + for(int ex = 0; ex < extents.Count; ex++) + { + if(!extents.TryGetValue(ex, out List alBlks)) + continue; + + foreach(ushort alBlk in alBlks) + { + allocationBlocks.TryGetValue(alBlk, out byte[] blk); + fileMs.Write(blk, 0, blk.Length); + fInfo.Blocks++; + } + } + + // If you insist to call CP/M "extent based" + fInfo.Attributes |= FileAttributes.Extents; + fInfo.BlockSize = blockSize; + fInfo.Length = fileMs.Length; + _cpmStat.Files++; + usedBlocks += fInfo.Blocks; + + _statCache.Add(filename, fInfo); + _fileCache.Add(filename, fileMs.ToArray()); + } + + _decodedPasswordCache = new Dictionary(); + + // For each stored password, store a decoded version of it + if(_passwordCache.Count > 0) + foreach(KeyValuePair kvp in _passwordCache) { - XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(_labelUpdateDate); - XmlFsType.ModificationDateSpecified = true; + byte[] tmp = new byte[8]; + Array.Copy(kvp.Value, 16, tmp, 0, 8); + + for(int t = 0; t < 8; t++) + tmp[t] ^= kvp.Value[13]; + + _decodedPasswordCache.Add(kvp.Key, tmp); } - if(!string.IsNullOrEmpty(_label)) - XmlFsType.VolumeName = _label; + // Generate statfs. + _cpmStat.Blocks = (ulong)(_dpb.dsm + 1); + _cpmStat.FilenameLength = 11; + _cpmStat.Files = (ulong)_fileCache.Count; + _cpmStat.FreeBlocks = _cpmStat.Blocks - (ulong)usedBlocks; + _cpmStat.PluginId = Id; + _cpmStat.Type = "CP/M filesystem"; - _mounted = true; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber StatFs(out FileSystemInfo stat) + // Generate XML info + XmlFsType = new FileSystemType { - stat = null; + Clusters = _cpmStat.Blocks, + ClusterSize = (uint)blockSize, + Files = (ulong)_fileCache.Count, + FilesSpecified = true, + FreeClusters = _cpmStat.FreeBlocks, + FreeClustersSpecified = true, + Type = "CP/M filesystem" + }; - if(!_mounted) - return ErrorNumber.AccessDenied; - - stat = _cpmStat; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Unmount() + if(_labelCreationDate != null) { - _mounted = false; - _definitions = null; - _cpmFound = false; - _workingDefinition = null; - _dpb = null; - _sectorMask = null; - _label = null; - _thirdPartyTimestamps = false; - _standardTimestamps = false; - _labelCreationDate = null; - _labelUpdateDate = null; - - return ErrorNumber.NoError; + XmlFsType.CreationDate = DateHandlers.CpmToDateTime(_labelCreationDate); + XmlFsType.CreationDateSpecified = true; } + + if(_labelUpdateDate != null) + { + XmlFsType.ModificationDate = DateHandlers.CpmToDateTime(_labelUpdateDate); + XmlFsType.ModificationDateSpecified = true; + } + + if(!string.IsNullOrEmpty(_label)) + XmlFsType.VolumeName = _label; + + _mounted = true; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + stat = _cpmStat; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Unmount() + { + _mounted = false; + _definitions = null; + _cpmFound = false; + _workingDefinition = null; + _dpb = null; + _sectorMask = null; + _label = null; + _thirdPartyTimestamps = false; + _standardTimestamps = false; + _labelCreationDate = null; + _labelUpdateDate = null; + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/CPM/Xattr.cs b/Aaru.Filesystems/CPM/Xattr.cs index 778fce6fa..2ee64a82f 100644 --- a/Aaru.Filesystems/CPM/Xattr.cs +++ b/Aaru.Filesystems/CPM/Xattr.cs @@ -34,66 +34,65 @@ using System; using System.Collections.Generic; using Aaru.CommonTypes.Enums; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class CPM { - public sealed partial class CPM + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) { - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - if(!_mounted) - return ErrorNumber.AccessDenied; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + if(!_fileCache.ContainsKey(pathElements[0].ToUpperInvariant())) + return ErrorNumber.NoSuchFile; - if(!_fileCache.ContainsKey(pathElements[0].ToUpperInvariant())) - return ErrorNumber.NoSuchFile; + if(string.Compare(xattr, "com.caldera.cpm.password", StringComparison.InvariantCulture) == 0) + if(!_passwordCache.TryGetValue(pathElements[0].ToUpperInvariant(), out buf)) + return ErrorNumber.NoError; - if(string.Compare(xattr, "com.caldera.cpm.password", StringComparison.InvariantCulture) == 0) - if(!_passwordCache.TryGetValue(pathElements[0].ToUpperInvariant(), out buf)) - return ErrorNumber.NoError; + if(string.Compare(xattr, "com.caldera.cpm.password.text", StringComparison.InvariantCulture) != 0) + return ErrorNumber.NoSuchExtendedAttribute; - if(string.Compare(xattr, "com.caldera.cpm.password.text", StringComparison.InvariantCulture) != 0) - return ErrorNumber.NoSuchExtendedAttribute; + return !_passwordCache.TryGetValue(pathElements[0].ToUpperInvariant(), out buf) ? ErrorNumber.NoError + : ErrorNumber.NoSuchExtendedAttribute; + } - return !_passwordCache.TryGetValue(pathElements[0].ToUpperInvariant(), out buf) ? ErrorNumber.NoError - : ErrorNumber.NoSuchExtendedAttribute; - } + /// + public ErrorNumber ListXAttr(string path, out List xattrs) + { + xattrs = null; - /// - public ErrorNumber ListXAttr(string path, out List xattrs) + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - xattrs = null; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + if(!_fileCache.ContainsKey(pathElements[0].ToUpperInvariant())) + return ErrorNumber.NoSuchFile; - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + xattrs = new List(); - if(!_fileCache.ContainsKey(pathElements[0].ToUpperInvariant())) - return ErrorNumber.NoSuchFile; + if(_passwordCache.ContainsKey(pathElements[0].ToUpperInvariant())) + xattrs.Add("com.caldera.cpm.password"); - xattrs = new List(); + if(_decodedPasswordCache.ContainsKey(pathElements[0].ToUpperInvariant())) + xattrs.Add("com.caldera.cpm.password.text"); - if(_passwordCache.ContainsKey(pathElements[0].ToUpperInvariant())) - xattrs.Add("com.caldera.cpm.password"); - - if(_decodedPasswordCache.ContainsKey(pathElements[0].ToUpperInvariant())) - xattrs.Add("com.caldera.cpm.password.text"); - - return ErrorNumber.NoError; - } + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Cram.cs b/Aaru.Filesystems/Cram.cs index c714ddbab..14665e274 100644 --- a/Aaru.Filesystems/Cram.cs +++ b/Aaru.Filesystems/Cram.cs @@ -43,118 +43,117 @@ using Marshal = Aaru.Helpers.Marshal; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the CRAM filesystem +[SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed class Cram : IFilesystem { + /// Identifier for Cram + const uint CRAM_MAGIC = 0x28CD3D45; + const uint CRAM_CIGAM = 0x453DCD28; + /// - /// Implements detection of the CRAM filesystem - [SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed class Cram : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Cram filesystem"; + /// + public Guid Id => new("F8F6E46F-7A2A-48E3-9C0A-46AF4DC29E09"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Identifier for Cram - const uint CRAM_MAGIC = 0x28CD3D45; - const uint CRAM_CIGAM = 0x453DCD28; + if(partition.Start >= partition.End) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Cram filesystem"; - /// - public Guid Id => new("F8F6E46F-7A2A-48E3-9C0A-46AF4DC29E09"); - /// - public string Author => "Natalia Portillo"; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(errno != ErrorNumber.NoError) + return false; + + uint magic = BitConverter.ToUInt32(sector, 0x00); + + return magic == CRAM_MAGIC || magic == CRAM_CIGAM; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + uint magic = BitConverter.ToUInt32(sector, 0x00); + + var crSb = new SuperBlock(); + bool littleEndian = true; + + switch(magic) { - if(partition.Start >= partition.End) - return false; + case CRAM_MAGIC: + crSb = Marshal.ByteArrayToStructureLittleEndian(sector); - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + break; + case CRAM_CIGAM: + crSb = Marshal.ByteArrayToStructureBigEndian(sector); + littleEndian = false; - if(errno != ErrorNumber.NoError) - return false; - - uint magic = BitConverter.ToUInt32(sector, 0x00); - - return magic == CRAM_MAGIC || magic == CRAM_CIGAM; + break; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + var sbInformation = new StringBuilder(); + + sbInformation.AppendLine("Cram file system"); + sbInformation.AppendLine(littleEndian ? "Little-endian" : "Big-endian"); + sbInformation.AppendFormat("Volume edition {0}", crSb.edition).AppendLine(); + sbInformation.AppendFormat("Volume name: {0}", StringHandlers.CToString(crSb.name, Encoding)).AppendLine(); + sbInformation.AppendFormat("Volume has {0} bytes", crSb.size).AppendLine(); + sbInformation.AppendFormat("Volume has {0} blocks", crSb.blocks).AppendLine(); + sbInformation.AppendFormat("Volume has {0} files", crSb.files).AppendLine(); + + information = sbInformation.ToString(); + + XmlFsType = new FileSystemType { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + VolumeName = StringHandlers.CToString(crSb.name, Encoding), + Type = "Cram file system", + Clusters = crSb.blocks, + Files = crSb.files, + FilesSpecified = true, + FreeClusters = 0, + FreeClustersSpecified = true + }; + } - if(errno != ErrorNumber.NoError) - return; + enum CramCompression : ushort + { + Zlib = 1, Lzma = 2, Lzo = 3, + Xz = 4, Lz4 = 5 + } - uint magic = BitConverter.ToUInt32(sector, 0x00); - - var crSb = new SuperBlock(); - bool littleEndian = true; - - switch(magic) - { - case CRAM_MAGIC: - crSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - break; - case CRAM_CIGAM: - crSb = Marshal.ByteArrayToStructureBigEndian(sector); - littleEndian = false; - - break; - } - - var sbInformation = new StringBuilder(); - - sbInformation.AppendLine("Cram file system"); - sbInformation.AppendLine(littleEndian ? "Little-endian" : "Big-endian"); - sbInformation.AppendFormat("Volume edition {0}", crSb.edition).AppendLine(); - sbInformation.AppendFormat("Volume name: {0}", StringHandlers.CToString(crSb.name, Encoding)).AppendLine(); - sbInformation.AppendFormat("Volume has {0} bytes", crSb.size).AppendLine(); - sbInformation.AppendFormat("Volume has {0} blocks", crSb.blocks).AppendLine(); - sbInformation.AppendFormat("Volume has {0} files", crSb.files).AppendLine(); - - information = sbInformation.ToString(); - - XmlFsType = new FileSystemType - { - VolumeName = StringHandlers.CToString(crSb.name, Encoding), - Type = "Cram file system", - Clusters = crSb.blocks, - Files = crSb.files, - FilesSpecified = true, - FreeClusters = 0, - FreeClustersSpecified = true - }; - } - - enum CramCompression : ushort - { - Zlib = 1, Lzma = 2, Lzo = 3, - Xz = 4, Lz4 = 5 - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - public readonly uint magic; - public readonly uint size; - public readonly uint flags; - public readonly uint future; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] signature; - public readonly uint crc; - public readonly uint edition; - public readonly uint blocks; - public readonly uint files; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] name; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + public readonly uint magic; + public readonly uint size; + public readonly uint flags; + public readonly uint future; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] signature; + public readonly uint crc; + public readonly uint edition; + public readonly uint blocks; + public readonly uint files; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] name; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ECMA67.cs b/Aaru.Filesystems/ECMA67.cs index 53cd902ba..ee1ce94b7 100644 --- a/Aaru.Filesystems/ECMA67.cs +++ b/Aaru.Filesystems/ECMA67.cs @@ -40,106 +40,105 @@ using Aaru.CommonTypes.Interfaces; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the filesystem described in ECMA-67 +public sealed class ECMA67 : IFilesystem { - /// - /// Implements detection of the filesystem described in ECMA-67 - public sealed class ECMA67 : IFilesystem + readonly byte[] _magic = { - readonly byte[] _magic = + 0x56, 0x4F, 0x4C + }; + + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "ECMA-67"; + /// + public Guid Id => new("62A2D44A-CBC1-4377-B4B6-28C5C92034A1"); + /// + public FileSystemType XmlFsType { get; private set; } + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + if(partition.Start > 0) + return false; + + if(partition.End < 8) + return false; + + ErrorNumber errno = imagePlugin.ReadSector(6, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length != 128) + return false; + + VolumeLabel vol = Marshal.ByteArrayToStructureLittleEndian(sector); + + return _magic.SequenceEqual(vol.labelIdentifier) && vol.labelNumber == 1 && vol.recordLength == 0x31; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); + information = ""; + ErrorNumber errno = imagePlugin.ReadSector(6, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + var sbInformation = new StringBuilder(); + + VolumeLabel vol = Marshal.ByteArrayToStructureLittleEndian(sector); + + sbInformation.AppendLine("ECMA-67"); + + sbInformation.AppendFormat("Volume name: {0}", Encoding.ASCII.GetString(vol.volumeIdentifier)).AppendLine(); + sbInformation.AppendFormat("Volume owner: {0}", Encoding.ASCII.GetString(vol.owner)).AppendLine(); + + XmlFsType = new FileSystemType { - 0x56, 0x4F, 0x4C + Type = "ECMA-67", + ClusterSize = 256, + Clusters = partition.End - partition.Start + 1, + VolumeName = Encoding.ASCII.GetString(vol.volumeIdentifier) }; - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "ECMA-67"; - /// - public Guid Id => new("62A2D44A-CBC1-4377-B4B6-28C5C92034A1"); - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Author => "Natalia Portillo"; + information = sbInformation.ToString(); + } - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(partition.Start > 0) - return false; - - if(partition.End < 8) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(6, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length != 128) - return false; - - VolumeLabel vol = Marshal.ByteArrayToStructureLittleEndian(sector); - - return _magic.SequenceEqual(vol.labelIdentifier) && vol.labelNumber == 1 && vol.recordLength == 0x31; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); - information = ""; - ErrorNumber errno = imagePlugin.ReadSector(6, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - var sbInformation = new StringBuilder(); - - VolumeLabel vol = Marshal.ByteArrayToStructureLittleEndian(sector); - - sbInformation.AppendLine("ECMA-67"); - - sbInformation.AppendFormat("Volume name: {0}", Encoding.ASCII.GetString(vol.volumeIdentifier)).AppendLine(); - sbInformation.AppendFormat("Volume owner: {0}", Encoding.ASCII.GetString(vol.owner)).AppendLine(); - - XmlFsType = new FileSystemType - { - Type = "ECMA-67", - ClusterSize = 256, - Clusters = partition.End - partition.Start + 1, - VolumeName = Encoding.ASCII.GetString(vol.volumeIdentifier) - }; - - information = sbInformation.ToString(); - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct VolumeLabel - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] labelIdentifier; - public readonly byte labelNumber; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] volumeIdentifier; - public readonly byte volumeAccessibility; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)] - public readonly byte[] reserved1; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] - public readonly byte[] owner; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly byte[] reserved2; - public readonly byte surface; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] reserved3; - public readonly byte recordLength; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] reserved4; - public readonly byte fileLabelAllocation; - public readonly byte labelStandardVersion; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)] - public readonly byte[] reserved5; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct VolumeLabel + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] labelIdentifier; + public readonly byte labelNumber; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] volumeIdentifier; + public readonly byte volumeAccessibility; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 26)] + public readonly byte[] reserved1; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] + public readonly byte[] owner; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly byte[] reserved2; + public readonly byte surface; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] reserved3; + public readonly byte recordLength; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] reserved4; + public readonly byte fileLabelAllocation; + public readonly byte labelStandardVersion; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)] + public readonly byte[] reserved5; } } \ No newline at end of file diff --git a/Aaru.Filesystems/EFS.cs b/Aaru.Filesystems/EFS.cs index dfa8f2a1c..781ab8d38 100644 --- a/Aaru.Filesystems/EFS.cs +++ b/Aaru.Filesystems/EFS.cs @@ -42,243 +42,242 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements identification for the SGI Extent FileSystem +public sealed class EFS : IFilesystem { + const uint EFS_MAGIC = 0x00072959; + const uint EFS_MAGIC_NEW = 0x0007295A; + /// - /// Implements identification for the SGI Extent FileSystem - public sealed class EFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Extent File System Plugin"; + /// + public Guid Id => new Guid("52A43F90-9AF3-4391-ADFE-65598DEEABAB"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint EFS_MAGIC = 0x00072959; - const uint EFS_MAGIC_NEW = 0x0007295A; + if(imagePlugin.Info.SectorSize < 512) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Extent File System Plugin"; - /// - public Guid Id => new Guid("52A43F90-9AF3-4391-ADFE-65598DEEABAB"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + // Misaligned + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - if(imagePlugin.Info.SectorSize < 512) + uint sbSize = (uint)((Marshal.SizeOf() + 0x200) / imagePlugin.Info.SectorSize); + + if((Marshal.SizeOf() + 0x200) % imagePlugin.Info.SectorSize != 0) + sbSize++; + + var errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + if(errno != ErrorNumber.NoError) return false; + + if(sector.Length < Marshal.SizeOf()) return false; - // Misaligned - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - uint sbSize = (uint)((Marshal.SizeOf() + 0x200) / imagePlugin.Info.SectorSize); + byte[] sbpiece = new byte[Marshal.SizeOf()]; - if((Marshal.SizeOf() + 0x200) % imagePlugin.Info.SectorSize != 0) - sbSize++; + Array.Copy(sector, 0x200, sbpiece, 0, Marshal.SizeOf()); - var errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); - if(errno != ErrorNumber.NoError) return false; + Superblock sb = Marshal.ByteArrayToStructureBigEndian(sbpiece); - if(sector.Length < Marshal.SizeOf()) - return false; + AaruConsole.DebugWriteLine("EFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", + 0x200, sb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); - byte[] sbpiece = new byte[Marshal.SizeOf()]; + if(sb.sb_magic == EFS_MAGIC || + sb.sb_magic == EFS_MAGIC_NEW) + return true; + } + else + { + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - Array.Copy(sector, 0x200, sbpiece, 0, Marshal.SizeOf()); + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; - Superblock sb = Marshal.ByteArrayToStructureBigEndian(sbpiece); + var errno = imagePlugin.ReadSectors(partition.Start + 1, sbSize, out byte[] sector); + if(errno != ErrorNumber.NoError) return false; - AaruConsole.DebugWriteLine("EFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", - 0x200, sb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); + if(sector.Length < Marshal.SizeOf()) + return false; - if(sb.sb_magic == EFS_MAGIC || - sb.sb_magic == EFS_MAGIC_NEW) - return true; - } - else - { - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + Superblock sb = Marshal.ByteArrayToStructureBigEndian(sector); - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; + AaruConsole.DebugWriteLine("EFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 1, + sb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); - var errno = imagePlugin.ReadSectors(partition.Start + 1, sbSize, out byte[] sector); - if(errno != ErrorNumber.NoError) return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - Superblock sb = Marshal.ByteArrayToStructureBigEndian(sector); - - AaruConsole.DebugWriteLine("EFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 1, - sb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); - - if(sb.sb_magic == EFS_MAGIC || - sb.sb_magic == EFS_MAGIC_NEW) - return true; - } - - return false; + if(sb.sb_magic == EFS_MAGIC || + sb.sb_magic == EFS_MAGIC_NEW) + return true; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + return false; + } - if(imagePlugin.Info.SectorSize < 512) + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + Superblock efsSb; + + // Misaligned + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) + { + uint sbSize = (uint)((Marshal.SizeOf() + 0x400) / imagePlugin.Info.SectorSize); + + if((Marshal.SizeOf() + 0x400) % imagePlugin.Info.SectorSize != 0) + sbSize++; + + var errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + if(errno != ErrorNumber.NoError) return; + + if(sector.Length < Marshal.SizeOf()) return; - Superblock efsSb; + byte[] sbpiece = new byte[Marshal.SizeOf()]; - // Misaligned - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - uint sbSize = (uint)((Marshal.SizeOf() + 0x400) / imagePlugin.Info.SectorSize); + Array.Copy(sector, 0x200, sbpiece, 0, Marshal.SizeOf()); - if((Marshal.SizeOf() + 0x400) % imagePlugin.Info.SectorSize != 0) - sbSize++; + efsSb = Marshal.ByteArrayToStructureBigEndian(sbpiece); - var errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); - if(errno != ErrorNumber.NoError) return; + AaruConsole.DebugWriteLine("EFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", + 0x200, efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); + } + else + { + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - if(sector.Length < Marshal.SizeOf()) - return; + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; - byte[] sbpiece = new byte[Marshal.SizeOf()]; + var errno = imagePlugin.ReadSectors(partition.Start + 1, sbSize, out byte[] sector); + if(errno != ErrorNumber.NoError) return; - Array.Copy(sector, 0x200, sbpiece, 0, Marshal.SizeOf()); - - efsSb = Marshal.ByteArrayToStructureBigEndian(sbpiece); - - AaruConsole.DebugWriteLine("EFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", - 0x200, efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); - } - else - { - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - var errno = imagePlugin.ReadSectors(partition.Start + 1, sbSize, out byte[] sector); - if(errno != ErrorNumber.NoError) return; - - if(sector.Length < Marshal.SizeOf()) - return; - - efsSb = Marshal.ByteArrayToStructureBigEndian(sector); - - AaruConsole.DebugWriteLine("EFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 1, - efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); - } - - if(efsSb.sb_magic != EFS_MAGIC && - efsSb.sb_magic != EFS_MAGIC_NEW) + if(sector.Length < Marshal.SizeOf()) return; - var sb = new StringBuilder(); + efsSb = Marshal.ByteArrayToStructureBigEndian(sector); - sb.AppendLine("SGI extent filesystem"); - - if(efsSb.sb_magic == EFS_MAGIC_NEW) - sb.AppendLine("New version"); - - sb.AppendFormat("Filesystem size: {0} basic blocks", efsSb.sb_size).AppendLine(); - sb.AppendFormat("First cylinder group starts at block {0}", efsSb.sb_firstcg).AppendLine(); - sb.AppendFormat("Cylinder group size: {0} basic blocks", efsSb.sb_cgfsize).AppendLine(); - sb.AppendFormat("{0} inodes per cylinder group", efsSb.sb_cgisize).AppendLine(); - sb.AppendFormat("{0} sectors per track", efsSb.sb_sectors).AppendLine(); - sb.AppendFormat("{0} heads per cylinder", efsSb.sb_heads).AppendLine(); - sb.AppendFormat("{0} cylinder groups", efsSb.sb_ncg).AppendLine(); - sb.AppendFormat("Volume created on {0}", DateHandlers.UnixToDateTime(efsSb.sb_time)).AppendLine(); - sb.AppendFormat("{0} bytes on bitmap", efsSb.sb_bmsize).AppendLine(); - sb.AppendFormat("{0} free blocks", efsSb.sb_tfree).AppendLine(); - sb.AppendFormat("{0} free inodes", efsSb.sb_tinode).AppendLine(); - - if(efsSb.sb_bmblock > 0) - sb.AppendFormat("Bitmap resides at block {0}", efsSb.sb_bmblock).AppendLine(); - - if(efsSb.sb_replsb > 0) - sb.AppendFormat("Replacement superblock resides at block {0}", efsSb.sb_replsb).AppendLine(); - - if(efsSb.sb_lastinode > 0) - sb.AppendFormat("Last inode allocated: {0}", efsSb.sb_lastinode).AppendLine(); - - if(efsSb.sb_dirty > 0) - sb.AppendLine("Volume is dirty"); - - sb.AppendFormat("Checksum: 0x{0:X8}", efsSb.sb_checksum).AppendLine(); - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(efsSb.sb_fname, Encoding)).AppendLine(); - sb.AppendFormat("Volume pack: {0}", StringHandlers.CToString(efsSb.sb_fpack, Encoding)).AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "Extent File System", - ClusterSize = 512, - Clusters = (ulong)efsSb.sb_size, - FreeClusters = (ulong)efsSb.sb_tfree, - FreeClustersSpecified = true, - Dirty = efsSb.sb_dirty > 0, - VolumeName = StringHandlers.CToString(efsSb.sb_fname, Encoding), - VolumeSerial = $"{efsSb.sb_checksum:X8}", - CreationDate = DateHandlers.UnixToDateTime(efsSb.sb_time), - CreationDateSpecified = true - }; + AaruConsole.DebugWriteLine("EFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8} or 0x{3:X8})", 1, + efsSb.sb_magic, EFS_MAGIC, EFS_MAGIC_NEW); } - [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] - readonly struct Superblock + if(efsSb.sb_magic != EFS_MAGIC && + efsSb.sb_magic != EFS_MAGIC_NEW) + return; + + var sb = new StringBuilder(); + + sb.AppendLine("SGI extent filesystem"); + + if(efsSb.sb_magic == EFS_MAGIC_NEW) + sb.AppendLine("New version"); + + sb.AppendFormat("Filesystem size: {0} basic blocks", efsSb.sb_size).AppendLine(); + sb.AppendFormat("First cylinder group starts at block {0}", efsSb.sb_firstcg).AppendLine(); + sb.AppendFormat("Cylinder group size: {0} basic blocks", efsSb.sb_cgfsize).AppendLine(); + sb.AppendFormat("{0} inodes per cylinder group", efsSb.sb_cgisize).AppendLine(); + sb.AppendFormat("{0} sectors per track", efsSb.sb_sectors).AppendLine(); + sb.AppendFormat("{0} heads per cylinder", efsSb.sb_heads).AppendLine(); + sb.AppendFormat("{0} cylinder groups", efsSb.sb_ncg).AppendLine(); + sb.AppendFormat("Volume created on {0}", DateHandlers.UnixToDateTime(efsSb.sb_time)).AppendLine(); + sb.AppendFormat("{0} bytes on bitmap", efsSb.sb_bmsize).AppendLine(); + sb.AppendFormat("{0} free blocks", efsSb.sb_tfree).AppendLine(); + sb.AppendFormat("{0} free inodes", efsSb.sb_tinode).AppendLine(); + + if(efsSb.sb_bmblock > 0) + sb.AppendFormat("Bitmap resides at block {0}", efsSb.sb_bmblock).AppendLine(); + + if(efsSb.sb_replsb > 0) + sb.AppendFormat("Replacement superblock resides at block {0}", efsSb.sb_replsb).AppendLine(); + + if(efsSb.sb_lastinode > 0) + sb.AppendFormat("Last inode allocated: {0}", efsSb.sb_lastinode).AppendLine(); + + if(efsSb.sb_dirty > 0) + sb.AppendLine("Volume is dirty"); + + sb.AppendFormat("Checksum: 0x{0:X8}", efsSb.sb_checksum).AppendLine(); + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(efsSb.sb_fname, Encoding)).AppendLine(); + sb.AppendFormat("Volume pack: {0}", StringHandlers.CToString(efsSb.sb_fpack, Encoding)).AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - /* 0: fs size incl. bb 0 (in bb) */ - public readonly int sb_size; - /* 4: first cg offset (in bb) */ - public readonly int sb_firstcg; - /* 8: cg size (in bb) */ - public readonly int sb_cgfsize; - /* 12: inodes/cg (in bb) */ - public readonly short sb_cgisize; - /* 14: geom: sectors/track */ - public readonly short sb_sectors; - /* 16: geom: heads/cylinder (unused) */ - public readonly short sb_heads; - /* 18: num of cg's in the filesystem */ - public readonly short sb_ncg; - /* 20: non-0 indicates fsck required */ - public readonly short sb_dirty; - /* 22: */ - public readonly short sb_pad0; - /* 24: superblock ctime */ - public readonly int sb_time; - /* 28: magic [0] */ - public readonly uint sb_magic; - /* 32: name of filesystem */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] sb_fname; - /* 38: name of filesystem pack */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] sb_fpack; - /* 44: bitmap size (in bytes) */ - public readonly int sb_bmsize; - /* 48: total free data blocks */ - public readonly int sb_tfree; - /* 52: total free inodes */ - public readonly int sb_tinode; - /* 56: bitmap offset (grown fs) */ - public readonly int sb_bmblock; - /* 62: repl. superblock offset */ - public readonly int sb_replsb; - /* 64: last allocated inode */ - public readonly int sb_lastinode; - /* 68: unused */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly byte[] sb_spare; - /* 88: checksum (all above) */ - public readonly uint sb_checksum; - } + Type = "Extent File System", + ClusterSize = 512, + Clusters = (ulong)efsSb.sb_size, + FreeClusters = (ulong)efsSb.sb_tfree, + FreeClustersSpecified = true, + Dirty = efsSb.sb_dirty > 0, + VolumeName = StringHandlers.CToString(efsSb.sb_fname, Encoding), + VolumeSerial = $"{efsSb.sb_checksum:X8}", + CreationDate = DateHandlers.UnixToDateTime(efsSb.sb_time), + CreationDateSpecified = true + }; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] + readonly struct Superblock + { + /* 0: fs size incl. bb 0 (in bb) */ + public readonly int sb_size; + /* 4: first cg offset (in bb) */ + public readonly int sb_firstcg; + /* 8: cg size (in bb) */ + public readonly int sb_cgfsize; + /* 12: inodes/cg (in bb) */ + public readonly short sb_cgisize; + /* 14: geom: sectors/track */ + public readonly short sb_sectors; + /* 16: geom: heads/cylinder (unused) */ + public readonly short sb_heads; + /* 18: num of cg's in the filesystem */ + public readonly short sb_ncg; + /* 20: non-0 indicates fsck required */ + public readonly short sb_dirty; + /* 22: */ + public readonly short sb_pad0; + /* 24: superblock ctime */ + public readonly int sb_time; + /* 28: magic [0] */ + public readonly uint sb_magic; + /* 32: name of filesystem */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] sb_fname; + /* 38: name of filesystem pack */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] sb_fpack; + /* 44: bitmap size (in bytes) */ + public readonly int sb_bmsize; + /* 48: total free data blocks */ + public readonly int sb_tfree; + /* 52: total free inodes */ + public readonly int sb_tinode; + /* 56: bitmap offset (grown fs) */ + public readonly int sb_bmblock; + /* 62: repl. superblock offset */ + public readonly int sb_replsb; + /* 64: last allocated inode */ + public readonly int sb_lastinode; + /* 68: unused */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly byte[] sb_spare; + /* 88: checksum (all above) */ + public readonly uint sb_checksum; } } \ No newline at end of file diff --git a/Aaru.Filesystems/F2FS.cs b/Aaru.Filesystems/F2FS.cs index 16b5d9bcc..037c00859 100644 --- a/Aaru.Filesystems/F2FS.cs +++ b/Aaru.Filesystems/F2FS.cs @@ -41,203 +41,202 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Flash-Friendly File System (F2FS) +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class F2FS : IFilesystem { + // ReSharper disable InconsistentNaming + const uint F2FS_MAGIC = 0xF2F52010; + const uint F2FS_SUPER_OFFSET = 1024; + const uint F2FS_MIN_SECTOR = 512; + const uint F2FS_MAX_SECTOR = 4096; + const uint F2FS_BLOCK_SIZE = 4096; + + // ReSharper restore InconsistentNaming + /// - /// Implements detection of the Flash-Friendly File System (F2FS) - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class F2FS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "F2FS Plugin"; + /// + public Guid Id => new("82B0920F-5F0D-4063-9F57-ADE0AE02ECE5"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - // ReSharper disable InconsistentNaming - const uint F2FS_MAGIC = 0xF2F52010; - const uint F2FS_SUPER_OFFSET = 1024; - const uint F2FS_MIN_SECTOR = 512; - const uint F2FS_MAX_SECTOR = 4096; - const uint F2FS_BLOCK_SIZE = 4096; + if(imagePlugin.Info.SectorSize < F2FS_MIN_SECTOR || + imagePlugin.Info.SectorSize > F2FS_MAX_SECTOR) + return false; - // ReSharper restore InconsistentNaming + uint sbAddr = F2FS_SUPER_OFFSET / imagePlugin.Info.SectorSize; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "F2FS Plugin"; - /// - public Guid Id => new("82B0920F-5F0D-4063-9F57-ADE0AE02ECE5"); - /// - public string Author => "Natalia Portillo"; + if(sbAddr == 0) + sbAddr = 1; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + if(partition.Start + sbAddr + sbSize >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < Marshal.SizeOf()) + return false; + + Superblock sb = Marshal.ByteArrayToStructureLittleEndian(sector); + + return sb.magic == F2FS_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = Encoding.Unicode; + information = ""; + + if(imagePlugin.Info.SectorSize < F2FS_MIN_SECTOR || + imagePlugin.Info.SectorSize > F2FS_MAX_SECTOR) + return; + + uint sbAddr = F2FS_SUPER_OFFSET / imagePlugin.Info.SectorSize; + + if(sbAddr == 0) + sbAddr = 1; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < Marshal.SizeOf()) + return; + + // ReSharper disable once InconsistentNaming + Superblock f2fsSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(f2fsSb.magic != F2FS_MAGIC) + return; + + var sb = new StringBuilder(); + + sb.AppendLine("F2FS filesystem"); + sb.AppendFormat("Version {0}.{1}", f2fsSb.major_ver, f2fsSb.minor_ver).AppendLine(); + sb.AppendFormat("{0} bytes per sector", 1 << (int)f2fsSb.log_sectorsize).AppendLine(); + + sb.AppendFormat("{0} sectors ({1} bytes) per block", 1 << (int)f2fsSb.log_sectors_per_block, + 1 << (int)f2fsSb.log_blocksize).AppendLine(); + + sb.AppendFormat("{0} blocks per segment", f2fsSb.log_blocks_per_seg).AppendLine(); + sb.AppendFormat("{0} blocks in volume", f2fsSb.block_count).AppendLine(); + sb.AppendFormat("{0} segments per section", f2fsSb.segs_per_sec).AppendLine(); + sb.AppendFormat("{0} sections per zone", f2fsSb.secs_per_zone).AppendLine(); + sb.AppendFormat("{0} sections", f2fsSb.section_count).AppendLine(); + sb.AppendFormat("{0} segments", f2fsSb.segment_count).AppendLine(); + sb.AppendFormat("Root directory resides on inode {0}", f2fsSb.root_ino).AppendLine(); + sb.AppendFormat("Volume UUID: {0}", f2fsSb.uuid).AppendLine(); + + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true)). + AppendLine(); + + sb.AppendFormat("Volume last mounted on kernel version: {0}", StringHandlers.CToString(f2fsSb.version)). + AppendLine(); + + sb.AppendFormat("Volume created on kernel version: {0}", StringHandlers.CToString(f2fsSb.init_version)). + AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - if(imagePlugin.Info.SectorSize < F2FS_MIN_SECTOR || - imagePlugin.Info.SectorSize > F2FS_MAX_SECTOR) - return false; + Type = "F2FS filesystem", + SystemIdentifier = Encoding.ASCII.GetString(f2fsSb.version), + Clusters = f2fsSb.block_count, + ClusterSize = (uint)(1 << (int)f2fsSb.log_blocksize), + DataPreparerIdentifier = Encoding.ASCII.GetString(f2fsSb.init_version), + VolumeName = StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true), + VolumeSerial = f2fsSb.uuid.ToString() + }; + } - uint sbAddr = F2FS_SUPER_OFFSET / imagePlugin.Info.SectorSize; - - if(sbAddr == 0) - sbAddr = 1; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - if(partition.Start + sbAddr + sbSize >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - Superblock sb = Marshal.ByteArrayToStructureLittleEndian(sector); - - return sb.magic == F2FS_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = Encoding.Unicode; - information = ""; - - if(imagePlugin.Info.SectorSize < F2FS_MIN_SECTOR || - imagePlugin.Info.SectorSize > F2FS_MAX_SECTOR) - return; - - uint sbAddr = F2FS_SUPER_OFFSET / imagePlugin.Info.SectorSize; - - if(sbAddr == 0) - sbAddr = 1; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < Marshal.SizeOf()) - return; - - // ReSharper disable once InconsistentNaming - Superblock f2fsSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - if(f2fsSb.magic != F2FS_MAGIC) - return; - - var sb = new StringBuilder(); - - sb.AppendLine("F2FS filesystem"); - sb.AppendFormat("Version {0}.{1}", f2fsSb.major_ver, f2fsSb.minor_ver).AppendLine(); - sb.AppendFormat("{0} bytes per sector", 1 << (int)f2fsSb.log_sectorsize).AppendLine(); - - sb.AppendFormat("{0} sectors ({1} bytes) per block", 1 << (int)f2fsSb.log_sectors_per_block, - 1 << (int)f2fsSb.log_blocksize).AppendLine(); - - sb.AppendFormat("{0} blocks per segment", f2fsSb.log_blocks_per_seg).AppendLine(); - sb.AppendFormat("{0} blocks in volume", f2fsSb.block_count).AppendLine(); - sb.AppendFormat("{0} segments per section", f2fsSb.segs_per_sec).AppendLine(); - sb.AppendFormat("{0} sections per zone", f2fsSb.secs_per_zone).AppendLine(); - sb.AppendFormat("{0} sections", f2fsSb.section_count).AppendLine(); - sb.AppendFormat("{0} segments", f2fsSb.segment_count).AppendLine(); - sb.AppendFormat("Root directory resides on inode {0}", f2fsSb.root_ino).AppendLine(); - sb.AppendFormat("Volume UUID: {0}", f2fsSb.uuid).AppendLine(); - - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true)). - AppendLine(); - - sb.AppendFormat("Volume last mounted on kernel version: {0}", StringHandlers.CToString(f2fsSb.version)). - AppendLine(); - - sb.AppendFormat("Volume created on kernel version: {0}", StringHandlers.CToString(f2fsSb.init_version)). - AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "F2FS filesystem", - SystemIdentifier = Encoding.ASCII.GetString(f2fsSb.version), - Clusters = f2fsSb.block_count, - ClusterSize = (uint)(1 << (int)f2fsSb.log_blocksize), - DataPreparerIdentifier = Encoding.ASCII.GetString(f2fsSb.init_version), - VolumeName = StringHandlers.CToString(f2fsSb.volume_name, Encoding.Unicode, true), - VolumeSerial = f2fsSb.uuid.ToString() - }; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] - readonly struct Superblock - { - public readonly uint magic; - public readonly ushort major_ver; - public readonly ushort minor_ver; - public readonly uint log_sectorsize; - public readonly uint log_sectors_per_block; - public readonly uint log_blocksize; - public readonly uint log_blocks_per_seg; - public readonly uint segs_per_sec; - public readonly uint secs_per_zone; - public readonly uint checksum_offset; - public readonly ulong block_count; - public readonly uint section_count; - public readonly uint segment_count; - public readonly uint segment_count_ckpt; - public readonly uint segment_count_sit; - public readonly uint segment_count_nat; - public readonly uint segment_count_ssa; - public readonly uint segment_count_main; - public readonly uint segment0_blkaddr; - public readonly uint cp_blkaddr; - public readonly uint sit_blkaddr; - public readonly uint nat_blkaddr; - public readonly uint ssa_blkaddr; - public readonly uint main_blkaddr; - public readonly uint root_ino; - public readonly uint node_ino; - public readonly uint meta_ino; - public readonly Guid uuid; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] - public readonly byte[] volume_name; - public readonly uint extension_count; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] extension_list1; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] extension_list2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] extension_list3; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] extension_list4; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] extension_list5; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] extension_list6; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] extension_list7; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] extension_list8; - public readonly uint cp_payload; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] - public readonly byte[] version; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] - public readonly byte[] init_version; - public readonly uint feature; - public readonly byte encryption_level; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] encrypt_pw_salt; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 871)] - public readonly byte[] reserved; - } + [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] + readonly struct Superblock + { + public readonly uint magic; + public readonly ushort major_ver; + public readonly ushort minor_ver; + public readonly uint log_sectorsize; + public readonly uint log_sectors_per_block; + public readonly uint log_blocksize; + public readonly uint log_blocks_per_seg; + public readonly uint segs_per_sec; + public readonly uint secs_per_zone; + public readonly uint checksum_offset; + public readonly ulong block_count; + public readonly uint section_count; + public readonly uint segment_count; + public readonly uint segment_count_ckpt; + public readonly uint segment_count_sit; + public readonly uint segment_count_nat; + public readonly uint segment_count_ssa; + public readonly uint segment_count_main; + public readonly uint segment0_blkaddr; + public readonly uint cp_blkaddr; + public readonly uint sit_blkaddr; + public readonly uint nat_blkaddr; + public readonly uint ssa_blkaddr; + public readonly uint main_blkaddr; + public readonly uint root_ino; + public readonly uint node_ino; + public readonly uint meta_ino; + public readonly Guid uuid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1024)] + public readonly byte[] volume_name; + public readonly uint extension_count; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] extension_list1; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] extension_list2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] extension_list3; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] extension_list4; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] extension_list5; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] extension_list6; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] extension_list7; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] extension_list8; + public readonly uint cp_payload; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public readonly byte[] version; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 256)] + public readonly byte[] init_version; + public readonly uint feature; + public readonly byte encryption_level; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] encrypt_pw_salt; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 871)] + public readonly byte[] reserved; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/BPB.cs b/Aaru.Filesystems/FAT/BPB.cs index 12bb31c11..607e573a0 100644 --- a/Aaru.Filesystems/FAT/BPB.cs +++ b/Aaru.Filesystems/FAT/BPB.cs @@ -39,237 +39,223 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Console; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class FAT { - public sealed partial class FAT + static BpbKind DetectBpbKind(byte[] bpbSector, IMediaImage imagePlugin, Partition partition, + out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb, + out AtariParameterBlock atariBpb, out byte minBootNearJump, + out bool andosOemCorrect, out bool bootable) { - static BpbKind DetectBpbKind(byte[] bpbSector, IMediaImage imagePlugin, Partition partition, - out BiosParameterBlockEbpb fakeBpb, out HumanParameterBlock humanBpb, - out AtariParameterBlock atariBpb, out byte minBootNearJump, - out bool andosOemCorrect, out bool bootable) + fakeBpb = new BiosParameterBlockEbpb(); + minBootNearJump = 0; + andosOemCorrect = false; + bootable = false; + ErrorNumber errno; + + humanBpb = Marshal.ByteArrayToStructureBigEndian(bpbSector); + atariBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + + ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; + + // Check clusters for Human68k are correct + bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters + : humanBpb.clusters == expectedClusters; + + // Check OEM for Human68k is correct + bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && + bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && + bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && + bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && + bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && + bpbSector[17] >= 0x20; + + // Check correct branch for Human68k + bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x1C && bpbSector[1] < 0xFE; + + AaruConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); + AaruConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); + AaruConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); + + // If all Human68k checks are correct, it is a Human68k FAT16 + bool useHumanBpb = humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0; + + if(useHumanBpb) { - fakeBpb = new BiosParameterBlockEbpb(); - minBootNearJump = 0; - andosOemCorrect = false; - bootable = false; - ErrorNumber errno; + AaruConsole.DebugWriteLine("FAT plugin", "Using Human68k BPB"); - humanBpb = Marshal.ByteArrayToStructureBigEndian(bpbSector); - atariBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + fakeBpb.jump = humanBpb.jump; + fakeBpb.oem_name = humanBpb.oem_name; + fakeBpb.bps = (ushort)imagePlugin.Info.SectorSize; + fakeBpb.spc = (byte)(humanBpb.bpc / fakeBpb.bps); + fakeBpb.fats_no = 2; + fakeBpb.root_ent = humanBpb.root_ent; + fakeBpb.media = humanBpb.media; + fakeBpb.spfat = (ushort)(humanBpb.cpfat * fakeBpb.spc); + fakeBpb.boot_code = humanBpb.boot_code; + fakeBpb.sectors = humanBpb.clusters; + fakeBpb.big_sectors = humanBpb.big_clusters; + fakeBpb.rsectors = 1; - ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; + return BpbKind.Human; + } - // Check clusters for Human68k are correct - bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters - : humanBpb.clusters == expectedClusters; + var msxBpb = new MsxParameterBlock(); + var dos2Bpb = new BiosParameterBlock2(); + var dos30Bpb = new BiosParameterBlock30(); + var dos32Bpb = new BiosParameterBlock32(); + var dos33Bpb = new BiosParameterBlock33(); + var shortEbpb = new BiosParameterBlockShortEbpb(); + var ebpb = new BiosParameterBlockEbpb(); + var apricotBpb = new ApricotLabel(); - // Check OEM for Human68k is correct - bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && - bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && - bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && - bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && - bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && - bpbSector[17] >= 0x20; + bool useAtariBpb = false; + bool useMsxBpb = false; + bool useDos2Bpb = false; + bool useDos3Bpb = false; + bool useDos32Bpb = false; + bool useDos33Bpb = false; + bool userShortExtendedBpb = false; + bool useExtendedBpb = false; + bool useShortFat32 = false; + bool useLongFat32 = false; + bool useApricotBpb = false; + bool useDecRainbowBpb = false; - // Check correct branch for Human68k - bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x1C && bpbSector[1] < 0xFE; + if(imagePlugin.Info.SectorSize >= 256) + { + msxBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + dos2Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + dos30Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + dos32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + dos33Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + shortEbpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + ebpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - AaruConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); - AaruConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); - AaruConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); + Fat32ParameterBlockShort shortFat32Bpb = + Marshal.ByteArrayToStructureLittleEndian(bpbSector); - // If all Human68k checks are correct, it is a Human68k FAT16 - bool useHumanBpb = humanClustersCorrect && humanOemCorrect && humanBranchCorrect && expectedClusters > 0; + Fat32ParameterBlock fat32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + apricotBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - if(useHumanBpb) + int bitsInBpsMsx = CountBits.Count(msxBpb.bps); + int bitsInBpsDos33 = CountBits.Count(dos33Bpb.bps); + int bitsInBpsDos40 = CountBits.Count(ebpb.bps); + int bitsInBpsFat32Short = CountBits.Count(shortFat32Bpb.bps); + int bitsInBpsFat32 = CountBits.Count(fat32Bpb.bps); + int bitsInBpsApricot = CountBits.Count(apricotBpb.mainBPB.bps); + + bool correctSpcMsx = msxBpb.spc == 1 || msxBpb.spc == 2 || msxBpb.spc == 4 || msxBpb.spc == 8 || + msxBpb.spc == 16 || msxBpb.spc == 32 || msxBpb.spc == 64; + + bool correctSpcDos33 = dos33Bpb.spc == 1 || dos33Bpb.spc == 2 || dos33Bpb.spc == 4 || + dos33Bpb.spc == 8 || dos33Bpb.spc == 16 || dos33Bpb.spc == 32 || + dos33Bpb.spc == 64; + + bool correctSpcDos40 = ebpb.spc == 1 || ebpb.spc == 2 || ebpb.spc == 4 || ebpb.spc == 8 || + ebpb.spc == 16 || ebpb.spc == 32 || ebpb.spc == 64; + + bool correctSpcFat32Short = shortFat32Bpb.spc == 1 || shortFat32Bpb.spc == 2 || + shortFat32Bpb.spc == 4 || shortFat32Bpb.spc == 8 || + shortFat32Bpb.spc == 16 || shortFat32Bpb.spc == 32 || + shortFat32Bpb.spc == 64; + + bool correctSpcFat32 = fat32Bpb.spc == 1 || fat32Bpb.spc == 2 || fat32Bpb.spc == 4 || + fat32Bpb.spc == 8 || fat32Bpb.spc == 16 || fat32Bpb.spc == 32 || + fat32Bpb.spc == 64; + + bool correctSpcApricot = apricotBpb.mainBPB.spc == 1 || apricotBpb.mainBPB.spc == 2 || + apricotBpb.mainBPB.spc == 4 || apricotBpb.mainBPB.spc == 8 || + apricotBpb.mainBPB.spc == 16 || apricotBpb.mainBPB.spc == 32 || + apricotBpb.mainBPB.spc == 64; + + // This is to support FAT partitions on hybrid ISO/USB images + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - AaruConsole.DebugWriteLine("FAT plugin", "Using Human68k BPB"); - - fakeBpb.jump = humanBpb.jump; - fakeBpb.oem_name = humanBpb.oem_name; - fakeBpb.bps = (ushort)imagePlugin.Info.SectorSize; - fakeBpb.spc = (byte)(humanBpb.bpc / fakeBpb.bps); - fakeBpb.fats_no = 2; - fakeBpb.root_ent = humanBpb.root_ent; - fakeBpb.media = humanBpb.media; - fakeBpb.spfat = (ushort)(humanBpb.cpfat * fakeBpb.spc); - fakeBpb.boot_code = humanBpb.boot_code; - fakeBpb.sectors = humanBpb.clusters; - fakeBpb.big_sectors = humanBpb.big_clusters; - fakeBpb.rsectors = 1; - - return BpbKind.Human; + atariBpb.sectors /= 4; + msxBpb.sectors /= 4; + dos2Bpb.sectors /= 4; + dos30Bpb.sectors /= 4; + dos32Bpb.sectors /= 4; + dos33Bpb.sectors /= 4; + dos33Bpb.big_sectors /= 4; + shortEbpb.sectors /= 4; + shortEbpb.big_sectors /= 4; + ebpb.sectors /= 4; + ebpb.big_sectors /= 4; + shortFat32Bpb.sectors /= 4; + shortFat32Bpb.big_sectors /= 4; + shortFat32Bpb.huge_sectors /= 4; + fat32Bpb.sectors /= 4; + fat32Bpb.big_sectors /= 4; + apricotBpb.mainBPB.sectors /= 4; } - var msxBpb = new MsxParameterBlock(); - var dos2Bpb = new BiosParameterBlock2(); - var dos30Bpb = new BiosParameterBlock30(); - var dos32Bpb = new BiosParameterBlock32(); - var dos33Bpb = new BiosParameterBlock33(); - var shortEbpb = new BiosParameterBlockShortEbpb(); - var ebpb = new BiosParameterBlockEbpb(); - var apricotBpb = new ApricotLabel(); + andosOemCorrect = dos33Bpb.oem_name[0] < 0x20 && dos33Bpb.oem_name[1] >= 0x20 && + dos33Bpb.oem_name[2] >= 0x20 && dos33Bpb.oem_name[3] >= 0x20 && + dos33Bpb.oem_name[4] >= 0x20 && dos33Bpb.oem_name[5] >= 0x20 && + dos33Bpb.oem_name[6] >= 0x20 && dos33Bpb.oem_name[7] >= 0x20; - bool useAtariBpb = false; - bool useMsxBpb = false; - bool useDos2Bpb = false; - bool useDos3Bpb = false; - bool useDos32Bpb = false; - bool useDos33Bpb = false; - bool userShortExtendedBpb = false; - bool useExtendedBpb = false; - bool useShortFat32 = false; - bool useLongFat32 = false; - bool useApricotBpb = false; - bool useDecRainbowBpb = false; - - if(imagePlugin.Info.SectorSize >= 256) + if(bitsInBpsFat32 == 1 && + correctSpcFat32 && + fat32Bpb.fats_no <= 2 && + fat32Bpb.spfat == 0 && + fat32Bpb.signature == 0x29 && + Encoding.ASCII.GetString(fat32Bpb.fs_type) == "FAT32 ") { - msxBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - dos2Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - dos30Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - dos32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - dos33Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - shortEbpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - ebpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + AaruConsole.DebugWriteLine("FAT plugin", "Using FAT32 BPB"); + minBootNearJump = 0x58; - Fat32ParameterBlockShort shortFat32Bpb = - Marshal.ByteArrayToStructureLittleEndian(bpbSector); + return BpbKind.LongFat32; + } - Fat32ParameterBlock fat32Bpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); - apricotBpb = Marshal.ByteArrayToStructureLittleEndian(bpbSector); + if(bitsInBpsFat32Short == 1 && + correctSpcFat32Short && + shortFat32Bpb.fats_no <= 2 && + shortFat32Bpb.sectors == 0 && + shortFat32Bpb.spfat == 0 && + shortFat32Bpb.signature == 0x28) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using short FAT32 BPB"); - int bitsInBpsMsx = CountBits.Count(msxBpb.bps); - int bitsInBpsDos33 = CountBits.Count(dos33Bpb.bps); - int bitsInBpsDos40 = CountBits.Count(ebpb.bps); - int bitsInBpsFat32Short = CountBits.Count(shortFat32Bpb.bps); - int bitsInBpsFat32 = CountBits.Count(fat32Bpb.bps); - int bitsInBpsApricot = CountBits.Count(apricotBpb.mainBPB.bps); + minBootNearJump = 0x57; - bool correctSpcMsx = msxBpb.spc == 1 || msxBpb.spc == 2 || msxBpb.spc == 4 || msxBpb.spc == 8 || - msxBpb.spc == 16 || msxBpb.spc == 32 || msxBpb.spc == 64; + return BpbKind.ShortFat32; + } - bool correctSpcDos33 = dos33Bpb.spc == 1 || dos33Bpb.spc == 2 || dos33Bpb.spc == 4 || - dos33Bpb.spc == 8 || dos33Bpb.spc == 16 || dos33Bpb.spc == 32 || - dos33Bpb.spc == 64; - - bool correctSpcDos40 = ebpb.spc == 1 || ebpb.spc == 2 || ebpb.spc == 4 || ebpb.spc == 8 || - ebpb.spc == 16 || ebpb.spc == 32 || ebpb.spc == 64; - - bool correctSpcFat32Short = shortFat32Bpb.spc == 1 || shortFat32Bpb.spc == 2 || - shortFat32Bpb.spc == 4 || shortFat32Bpb.spc == 8 || - shortFat32Bpb.spc == 16 || shortFat32Bpb.spc == 32 || - shortFat32Bpb.spc == 64; - - bool correctSpcFat32 = fat32Bpb.spc == 1 || fat32Bpb.spc == 2 || fat32Bpb.spc == 4 || - fat32Bpb.spc == 8 || fat32Bpb.spc == 16 || fat32Bpb.spc == 32 || - fat32Bpb.spc == 64; - - bool correctSpcApricot = apricotBpb.mainBPB.spc == 1 || apricotBpb.mainBPB.spc == 2 || - apricotBpb.mainBPB.spc == 4 || apricotBpb.mainBPB.spc == 8 || - apricotBpb.mainBPB.spc == 16 || apricotBpb.mainBPB.spc == 32 || - apricotBpb.mainBPB.spc == 64; - - // This is to support FAT partitions on hybrid ISO/USB images - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) + if(bitsInBpsMsx == 1 && + correctSpcMsx && + msxBpb.fats_no <= 2 && + msxBpb.root_ent > 0 && + msxBpb.sectors <= partition.End - partition.Start + 1 && + msxBpb.spfat > 0 && + Encoding.ASCII.GetString(msxBpb.vol_id) == "VOL_ID") + { + AaruConsole.DebugWriteLine("FAT plugin", "Using MSX BPB"); + useMsxBpb = true; + } + else if(bitsInBpsApricot == 1 && + correctSpcApricot && + apricotBpb.mainBPB.fats_no <= 2 && + apricotBpb.mainBPB.root_ent > 0 && + apricotBpb.mainBPB.sectors <= partition.End - partition.Start + 1 && + apricotBpb.mainBPB.spfat > 0 && + apricotBpb.partitionCount == 0) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB"); + useApricotBpb = true; + } + else if(bitsInBpsDos40 == 1 && + correctSpcDos40 && + ebpb.fats_no <= 2 && + ebpb.root_ent > 0 && + ebpb.spfat > 0 && + (ebpb.signature == 0x28 || ebpb.signature == 0x29 || andosOemCorrect)) + { + if(ebpb.sectors == 0) { - atariBpb.sectors /= 4; - msxBpb.sectors /= 4; - dos2Bpb.sectors /= 4; - dos30Bpb.sectors /= 4; - dos32Bpb.sectors /= 4; - dos33Bpb.sectors /= 4; - dos33Bpb.big_sectors /= 4; - shortEbpb.sectors /= 4; - shortEbpb.big_sectors /= 4; - ebpb.sectors /= 4; - ebpb.big_sectors /= 4; - shortFat32Bpb.sectors /= 4; - shortFat32Bpb.big_sectors /= 4; - shortFat32Bpb.huge_sectors /= 4; - fat32Bpb.sectors /= 4; - fat32Bpb.big_sectors /= 4; - apricotBpb.mainBPB.sectors /= 4; - } - - andosOemCorrect = dos33Bpb.oem_name[0] < 0x20 && dos33Bpb.oem_name[1] >= 0x20 && - dos33Bpb.oem_name[2] >= 0x20 && dos33Bpb.oem_name[3] >= 0x20 && - dos33Bpb.oem_name[4] >= 0x20 && dos33Bpb.oem_name[5] >= 0x20 && - dos33Bpb.oem_name[6] >= 0x20 && dos33Bpb.oem_name[7] >= 0x20; - - if(bitsInBpsFat32 == 1 && - correctSpcFat32 && - fat32Bpb.fats_no <= 2 && - fat32Bpb.spfat == 0 && - fat32Bpb.signature == 0x29 && - Encoding.ASCII.GetString(fat32Bpb.fs_type) == "FAT32 ") - { - AaruConsole.DebugWriteLine("FAT plugin", "Using FAT32 BPB"); - minBootNearJump = 0x58; - - return BpbKind.LongFat32; - } - - if(bitsInBpsFat32Short == 1 && - correctSpcFat32Short && - shortFat32Bpb.fats_no <= 2 && - shortFat32Bpb.sectors == 0 && - shortFat32Bpb.spfat == 0 && - shortFat32Bpb.signature == 0x28) - { - AaruConsole.DebugWriteLine("FAT plugin", "Using short FAT32 BPB"); - - minBootNearJump = 0x57; - - return BpbKind.ShortFat32; - } - - if(bitsInBpsMsx == 1 && - correctSpcMsx && - msxBpb.fats_no <= 2 && - msxBpb.root_ent > 0 && - msxBpb.sectors <= partition.End - partition.Start + 1 && - msxBpb.spfat > 0 && - Encoding.ASCII.GetString(msxBpb.vol_id) == "VOL_ID") - { - AaruConsole.DebugWriteLine("FAT plugin", "Using MSX BPB"); - useMsxBpb = true; - } - else if(bitsInBpsApricot == 1 && - correctSpcApricot && - apricotBpb.mainBPB.fats_no <= 2 && - apricotBpb.mainBPB.root_ent > 0 && - apricotBpb.mainBPB.sectors <= partition.End - partition.Start + 1 && - apricotBpb.mainBPB.spfat > 0 && - apricotBpb.partitionCount == 0) - { - AaruConsole.DebugWriteLine("FAT plugin", "Using Apricot BPB"); - useApricotBpb = true; - } - else if(bitsInBpsDos40 == 1 && - correctSpcDos40 && - ebpb.fats_no <= 2 && - ebpb.root_ent > 0 && - ebpb.spfat > 0 && - (ebpb.signature == 0x28 || ebpb.signature == 0x29 || andosOemCorrect)) - { - if(ebpb.sectors == 0) - { - if(ebpb.big_sectors <= partition.End - partition.Start + 1) - if(ebpb.signature == 0x29 || andosOemCorrect) - { - AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); - useExtendedBpb = true; - minBootNearJump = 0x3C; - } - else - { - AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); - userShortExtendedBpb = true; - minBootNearJump = 0x29; - } - } - else if(ebpb.sectors <= partition.End - partition.Start + 1) + if(ebpb.big_sectors <= partition.End - partition.Start + 1) if(ebpb.signature == 0x29 || andosOemCorrect) { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); @@ -283,338 +269,331 @@ namespace Aaru.Filesystems minBootNearJump = 0x29; } } - else if(bitsInBpsDos33 == 1 && - correctSpcDos33 && - dos33Bpb.rsectors < partition.End - partition.Start && - dos33Bpb.fats_no <= 2 && - dos33Bpb.root_ent > 0 && - dos33Bpb.spfat > 0) - if(dos33Bpb.sectors == 0 && - dos33Bpb.hsectors <= partition.Start && - dos33Bpb.big_sectors > 0 && - dos33Bpb.big_sectors <= partition.End - partition.Start + 1) + else if(ebpb.sectors <= partition.End - partition.Start + 1) + if(ebpb.signature == 0x29 || andosOemCorrect) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 4.0 BPB"); + useExtendedBpb = true; + minBootNearJump = 0x3C; + } + else + { + AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.4 BPB"); + userShortExtendedBpb = true; + minBootNearJump = 0x29; + } + } + else if(bitsInBpsDos33 == 1 && + correctSpcDos33 && + dos33Bpb.rsectors < partition.End - partition.Start && + dos33Bpb.fats_no <= 2 && + dos33Bpb.root_ent > 0 && + dos33Bpb.spfat > 0) + if(dos33Bpb.sectors == 0 && + dos33Bpb.hsectors <= partition.Start && + dos33Bpb.big_sectors > 0 && + dos33Bpb.big_sectors <= partition.End - partition.Start + 1) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); + useDos33Bpb = true; + minBootNearJump = 0x22; + } + else if(dos33Bpb.big_sectors == 0 && + dos33Bpb.hsectors <= partition.Start && + dos33Bpb.sectors > 0 && + dos33Bpb.sectors <= partition.End - partition.Start + 1) + if(atariBpb.jump[0] == 0x60 || + (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && + Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") || + partition.Type == "GEM" || + partition.Type == "BGM") + { + AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); + useAtariBpb = true; + } + else { AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); useDos33Bpb = true; minBootNearJump = 0x22; } - else if(dos33Bpb.big_sectors == 0 && - dos33Bpb.hsectors <= partition.Start && - dos33Bpb.sectors > 0 && - dos33Bpb.sectors <= partition.End - partition.Start + 1) + else + { + if(dos32Bpb.hsectors <= partition.Start && + dos32Bpb.hsectors + dos32Bpb.sectors == dos32Bpb.total_sectors) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.2 BPB"); + useDos32Bpb = true; + minBootNearJump = 0x1E; + } + else if(dos30Bpb.sptrk > 0 && + dos30Bpb.sptrk < 64 && + dos30Bpb.heads > 0 && + dos30Bpb.heads < 256) if(atariBpb.jump[0] == 0x60 || (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && - Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ") || - partition.Type == "GEM" || - partition.Type == "BGM") + Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ")) { AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); useAtariBpb = true; } else { - AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.3 BPB"); - useDos33Bpb = true; - minBootNearJump = 0x22; + AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.0 BPB"); + useDos3Bpb = true; + minBootNearJump = 0x1C; } else { - if(dos32Bpb.hsectors <= partition.Start && - dos32Bpb.hsectors + dos32Bpb.sectors == dos32Bpb.total_sectors) + if(atariBpb.jump[0] == 0x60 || + (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && + Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ")) { - AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.2 BPB"); - useDos32Bpb = true; - minBootNearJump = 0x1E; + AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); + useAtariBpb = true; } - else if(dos30Bpb.sptrk > 0 && - dos30Bpb.sptrk < 64 && - dos30Bpb.heads > 0 && - dos30Bpb.heads < 256) - if(atariBpb.jump[0] == 0x60 || - (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && - Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ")) - { - AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); - useAtariBpb = true; - } - else - { - AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 3.0 BPB"); - useDos3Bpb = true; - minBootNearJump = 0x1C; - } else { - if(atariBpb.jump[0] == 0x60 || - (atariBpb.jump[0] == 0xE9 && atariBpb.jump[1] == 0x00 && - Encoding.ASCII.GetString(dos33Bpb.oem_name) != "NEXT ")) - { - AaruConsole.DebugWriteLine("FAT plugin", "Using Atari BPB"); - useAtariBpb = true; - } - else - { - AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 2.0 BPB"); - useDos2Bpb = true; - minBootNearJump = 0x16; - } + AaruConsole.DebugWriteLine("FAT plugin", "Using DOS 2.0 BPB"); + useDos2Bpb = true; + minBootNearJump = 0x16; } } + } + } + + // DEC Rainbow, lacks a BPB but has a very concrete structure... + if(imagePlugin.Info.Sectors == 800 && + imagePlugin.Info.SectorSize == 512 && + !useAtariBpb && + !useMsxBpb && + !useDos2Bpb && + !useDos3Bpb && + !useDos32Bpb && + !useDos33Bpb && + !userShortExtendedBpb && + !useExtendedBpb && + !useShortFat32 && + !useLongFat32 && + !useApricotBpb) + { + // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) + byte z80Di = bpbSector[0]; + + // First FAT1 sector resides at LBA 0x14 + imagePlugin.ReadSector(0x14, out byte[] fat1Sector0); + + // First FAT2 sector resides at LBA 0x1A + imagePlugin.ReadSector(0x1A, out byte[] fat2Sector0); + bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; + + // Volume is software interleaved 2:1 + var rootMs = new MemoryStream(); + + foreach(ulong rootSector in new[] + { + 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 + }) + { + imagePlugin.ReadSector(rootSector, out byte[] tmp); + rootMs.Write(tmp, 0, tmp.Length); } - // DEC Rainbow, lacks a BPB but has a very concrete structure... - if(imagePlugin.Info.Sectors == 800 && - imagePlugin.Info.SectorSize == 512 && - !useAtariBpb && - !useMsxBpb && - !useDos2Bpb && - !useDos3Bpb && - !useDos32Bpb && - !useDos33Bpb && - !userShortExtendedBpb && - !useExtendedBpb && - !useShortFat32 && - !useLongFat32 && - !useApricotBpb) + byte[] rootDir = rootMs.ToArray(); + bool validRootDir = true; + + // Iterate all root directory + for(int e = 0; e < 96 * 32; e += 32) { - // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) - byte z80Di = bpbSector[0]; + for(int c = 0; c < 11; c++) + if((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) || + rootDir[c + e] == 0xFF || + rootDir[c + e] == 0x2E) + { + validRootDir = false; - // First FAT1 sector resides at LBA 0x14 - imagePlugin.ReadSector(0x14, out byte[] fat1Sector0); + break; + } - // First FAT2 sector resides at LBA 0x1A - imagePlugin.ReadSector(0x1A, out byte[] fat2Sector0); - bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; + if(!validRootDir) + break; + } - // Volume is software interleaved 2:1 - var rootMs = new MemoryStream(); + if(z80Di == 0xF3 && + equalFatIds && + (fat1Sector0[0] & 0xF0) == 0xF0 && + fat1Sector0[1] == 0xFF && + validRootDir) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using DEC Rainbow hardcoded BPB."); + fakeBpb.bps = 512; + fakeBpb.spc = 1; + fakeBpb.rsectors = 20; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 96; + fakeBpb.sectors = 800; + fakeBpb.media = 0xFA; + fakeBpb.sptrk = 10; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 3; + bootable = true; + fakeBpb.boot_code = bpbSector; - foreach(ulong rootSector in new[] - { - 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 - }) - { - imagePlugin.ReadSector(rootSector, out byte[] tmp); - rootMs.Write(tmp, 0, tmp.Length); - } + return BpbKind.DecRainbow; + } + } - byte[] rootDir = rootMs.ToArray(); - bool validRootDir = true; + if(!useAtariBpb && + !useMsxBpb && + !useDos2Bpb && + !useDos3Bpb && + !useDos32Bpb && + !useDos33Bpb && + !useHumanBpb && + !userShortExtendedBpb && + !useExtendedBpb && + !useShortFat32 && + !useLongFat32 && + !useApricotBpb && + !useDecRainbowBpb) + { + imagePlugin.ReadSector(1 + partition.Start, out byte[] fatSector); - // Iterate all root directory - for(int e = 0; e < 96 * 32; e += 32) - { - for(int c = 0; c < 11; c++) - if((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) || - rootDir[c + e] == 0xFF || - rootDir[c + e] == 0x2E) - { - validRootDir = false; + switch(fatSector[0]) + { + case 0xE5: + if(imagePlugin.Info.Sectors == 2002 && + imagePlugin.Info.SectorSize == 128) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 128; + fakeBpb.spc = 4; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 64; + fakeBpb.sectors = 2002; + fakeBpb.media = 0xE5; + fakeBpb.sptrk = 26; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 1; + } + + break; + case 0xFD: + if(imagePlugin.Info.Sectors == 4004 && + imagePlugin.Info.SectorSize == 128) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 128; + fakeBpb.spc = 4; + fakeBpb.rsectors = 4; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 68; + fakeBpb.sectors = 4004; + fakeBpb.media = 0xFD; + fakeBpb.sptrk = 26; + fakeBpb.heads = 2; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 6; + } + else if(imagePlugin.Info.Sectors == 2002 && + imagePlugin.Info.SectorSize == 128) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 128; + fakeBpb.spc = 4; + fakeBpb.rsectors = 4; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 68; + fakeBpb.sectors = 2002; + fakeBpb.media = 0xFD; + fakeBpb.sptrk = 26; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 6; + } + + break; + case 0xFE: + switch(imagePlugin.Info.Sectors) + { + case 320 when imagePlugin.Info.SectorSize == 512: + AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" SSDD."); + fakeBpb.bps = 512; + fakeBpb.spc = 1; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 64; + fakeBpb.sectors = 320; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 8; + fakeBpb.heads = 1; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 1; break; - } - - if(!validRootDir) - break; - } - - if(z80Di == 0xF3 && - equalFatIds && - (fat1Sector0[0] & 0xF0) == 0xF0 && - fat1Sector0[1] == 0xFF && - validRootDir) - { - AaruConsole.DebugWriteLine("FAT plugin", "Using DEC Rainbow hardcoded BPB."); - fakeBpb.bps = 512; - fakeBpb.spc = 1; - fakeBpb.rsectors = 20; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 96; - fakeBpb.sectors = 800; - fakeBpb.media = 0xFA; - fakeBpb.sptrk = 10; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 3; - bootable = true; - fakeBpb.boot_code = bpbSector; - - return BpbKind.DecRainbow; - } - } - - if(!useAtariBpb && - !useMsxBpb && - !useDos2Bpb && - !useDos3Bpb && - !useDos32Bpb && - !useDos33Bpb && - !useHumanBpb && - !userShortExtendedBpb && - !useExtendedBpb && - !useShortFat32 && - !useLongFat32 && - !useApricotBpb && - !useDecRainbowBpb) - { - imagePlugin.ReadSector(1 + partition.Start, out byte[] fatSector); - - switch(fatSector[0]) - { - case 0xE5: - if(imagePlugin.Info.Sectors == 2002 && - imagePlugin.Info.SectorSize == 128) - { + case 2002 when imagePlugin.Info.SectorSize == 128: AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; fakeBpb.spc = 4; fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; - fakeBpb.root_ent = 64; + fakeBpb.root_ent = 68; fakeBpb.sectors = 2002; - fakeBpb.media = 0xE5; + fakeBpb.media = 0xFE; fakeBpb.sptrk = 26; fakeBpb.heads = 1; fakeBpb.hsectors = 0; - fakeBpb.spfat = 1; - } + fakeBpb.spfat = 6; - break; - case 0xFD: - if(imagePlugin.Info.Sectors == 4004 && - imagePlugin.Info.SectorSize == 128) - { + break; + case 1232 when imagePlugin.Info.SectorSize == 1024: AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 128; - fakeBpb.spc = 4; - fakeBpb.rsectors = 4; + fakeBpb.bps = 1024; + fakeBpb.spc = 1; + fakeBpb.rsectors = 1; fakeBpb.fats_no = 2; - fakeBpb.root_ent = 68; - fakeBpb.sectors = 4004; - fakeBpb.media = 0xFD; - fakeBpb.sptrk = 26; + fakeBpb.root_ent = 192; + fakeBpb.sectors = 1232; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 8; fakeBpb.heads = 2; fakeBpb.hsectors = 0; - fakeBpb.spfat = 6; - } - else if(imagePlugin.Info.Sectors == 2002 && - imagePlugin.Info.SectorSize == 128) - { + fakeBpb.spfat = 2; + + break; + case 616 when imagePlugin.Info.SectorSize == 1024: + AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); + fakeBpb.bps = 1024; + fakeBpb.spc = 1; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 6192; + fakeBpb.sectors = 616; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 8; + fakeBpb.heads = 2; + fakeBpb.hsectors = 0; + + break; + case 720 when imagePlugin.Info.SectorSize == 128: AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); fakeBpb.bps = 128; - fakeBpb.spc = 4; - fakeBpb.rsectors = 4; + fakeBpb.spc = 2; + fakeBpb.rsectors = 54; fakeBpb.fats_no = 2; - fakeBpb.root_ent = 68; - fakeBpb.sectors = 2002; - fakeBpb.media = 0xFD; - fakeBpb.sptrk = 26; + fakeBpb.root_ent = 64; + fakeBpb.sectors = 720; + fakeBpb.media = 0xFE; + fakeBpb.sptrk = 18; fakeBpb.heads = 1; fakeBpb.hsectors = 0; - fakeBpb.spfat = 6; - } + fakeBpb.spfat = 4; - break; - case 0xFE: - switch(imagePlugin.Info.Sectors) - { - case 320 when imagePlugin.Info.SectorSize == 512: - AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" SSDD."); - fakeBpb.bps = 512; - fakeBpb.spc = 1; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 64; - fakeBpb.sectors = 320; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 8; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 1; - - break; - case 2002 when imagePlugin.Info.SectorSize == 128: - AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 128; - fakeBpb.spc = 4; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 68; - fakeBpb.sectors = 2002; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 26; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 6; - - break; - case 1232 when imagePlugin.Info.SectorSize == 1024: - AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 1024; - fakeBpb.spc = 1; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 192; - fakeBpb.sectors = 1232; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 8; - fakeBpb.heads = 2; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 2; - - break; - case 616 when imagePlugin.Info.SectorSize == 1024: - AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 1024; - fakeBpb.spc = 1; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 6192; - fakeBpb.sectors = 616; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 8; - fakeBpb.heads = 2; - fakeBpb.hsectors = 0; - - break; - case 720 when imagePlugin.Info.SectorSize == 128: - AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB."); - fakeBpb.bps = 128; - fakeBpb.spc = 2; - fakeBpb.rsectors = 54; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 64; - fakeBpb.sectors = 720; - fakeBpb.media = 0xFE; - fakeBpb.sptrk = 18; - fakeBpb.heads = 1; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 4; - - break; - case 640 when imagePlugin.Info.SectorSize == 512: - AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); - fakeBpb.bps = 512; - fakeBpb.spc = 2; - fakeBpb.rsectors = 1; - fakeBpb.fats_no = 2; - fakeBpb.root_ent = 112; - fakeBpb.sectors = 640; - fakeBpb.media = 0xFF; - fakeBpb.sptrk = 8; - fakeBpb.heads = 2; - fakeBpb.hsectors = 0; - fakeBpb.spfat = 1; - - break; - } - - break; - case 0xFF: - if(imagePlugin.Info.Sectors == 640 && - imagePlugin.Info.SectorSize == 512) - { + break; + case 640 when imagePlugin.Info.SectorSize == 512: AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); fakeBpb.bps = 512; fakeBpb.spc = 2; @@ -627,202 +606,222 @@ namespace Aaru.Filesystems fakeBpb.heads = 2; fakeBpb.hsectors = 0; fakeBpb.spfat = 1; - } - break; - } + break; + } - // This assumes a bootable sector will jump somewhere or disable interrupts in x86 code - bootable |= bpbSector[0] == 0xFA || (bpbSector[0] == 0xEB && bpbSector[1] <= 0x7F) || - (bpbSector[0] == 0xE9 && BitConverter.ToUInt16(bpbSector, 1) <= 0x1FC); + break; + case 0xFF: + if(imagePlugin.Info.Sectors == 640 && + imagePlugin.Info.SectorSize == 512) + { + AaruConsole.DebugWriteLine("FAT plugin", "Using hardcoded BPB for 5.25\" DSDD."); + fakeBpb.bps = 512; + fakeBpb.spc = 2; + fakeBpb.rsectors = 1; + fakeBpb.fats_no = 2; + fakeBpb.root_ent = 112; + fakeBpb.sectors = 640; + fakeBpb.media = 0xFF; + fakeBpb.sptrk = 8; + fakeBpb.heads = 2; + fakeBpb.hsectors = 0; + fakeBpb.spfat = 1; + } - fakeBpb.boot_code = bpbSector; - - return BpbKind.Hardcoded; + break; } - if(useExtendedBpb) - { - fakeBpb = ebpb; + // This assumes a bootable sector will jump somewhere or disable interrupts in x86 code + bootable |= bpbSector[0] == 0xFA || (bpbSector[0] == 0xEB && bpbSector[1] <= 0x7F) || + (bpbSector[0] == 0xE9 && BitConverter.ToUInt16(bpbSector, 1) <= 0x1FC); - return BpbKind.Extended; - } + fakeBpb.boot_code = bpbSector; - if(userShortExtendedBpb) - { - fakeBpb.jump = shortEbpb.jump; - fakeBpb.oem_name = shortEbpb.oem_name; - fakeBpb.bps = shortEbpb.bps; - fakeBpb.spc = shortEbpb.spc; - fakeBpb.rsectors = shortEbpb.rsectors; - fakeBpb.fats_no = shortEbpb.fats_no; - fakeBpb.root_ent = shortEbpb.root_ent; - fakeBpb.sectors = shortEbpb.sectors; - fakeBpb.media = shortEbpb.media; - fakeBpb.spfat = shortEbpb.spfat; - fakeBpb.sptrk = shortEbpb.sptrk; - fakeBpb.heads = shortEbpb.heads; - fakeBpb.hsectors = shortEbpb.hsectors; - fakeBpb.big_sectors = shortEbpb.big_sectors; - fakeBpb.drive_no = shortEbpb.drive_no; - fakeBpb.flags = shortEbpb.flags; - fakeBpb.signature = shortEbpb.signature; - fakeBpb.serial_no = shortEbpb.serial_no; - fakeBpb.boot_code = shortEbpb.boot_code; - fakeBpb.boot_signature = shortEbpb.boot_signature; - - return BpbKind.ShortExtended; - } - - if(useDos33Bpb) - { - fakeBpb.jump = dos33Bpb.jump; - fakeBpb.oem_name = dos33Bpb.oem_name; - fakeBpb.bps = dos33Bpb.bps; - fakeBpb.spc = dos33Bpb.spc; - fakeBpb.rsectors = dos33Bpb.rsectors; - fakeBpb.fats_no = dos33Bpb.fats_no; - fakeBpb.root_ent = dos33Bpb.root_ent; - fakeBpb.sectors = dos33Bpb.sectors; - fakeBpb.media = dos33Bpb.media; - fakeBpb.spfat = dos33Bpb.spfat; - fakeBpb.sptrk = dos33Bpb.sptrk; - fakeBpb.heads = dos33Bpb.heads; - fakeBpb.hsectors = dos33Bpb.hsectors; - fakeBpb.big_sectors = dos33Bpb.big_sectors; - fakeBpb.boot_code = dos33Bpb.boot_code; - fakeBpb.boot_signature = dos33Bpb.boot_signature; - - return BpbKind.Dos33; - } - - if(useDos32Bpb) - { - fakeBpb.jump = dos32Bpb.jump; - fakeBpb.oem_name = dos32Bpb.oem_name; - fakeBpb.bps = dos32Bpb.bps; - fakeBpb.spc = dos32Bpb.spc; - fakeBpb.rsectors = dos32Bpb.rsectors; - fakeBpb.fats_no = dos32Bpb.fats_no; - fakeBpb.root_ent = dos32Bpb.root_ent; - fakeBpb.sectors = dos32Bpb.sectors; - fakeBpb.media = dos32Bpb.media; - fakeBpb.spfat = dos32Bpb.spfat; - fakeBpb.sptrk = dos32Bpb.sptrk; - fakeBpb.heads = dos32Bpb.heads; - fakeBpb.hsectors = dos32Bpb.hsectors; - fakeBpb.boot_code = dos32Bpb.boot_code; - fakeBpb.boot_signature = dos32Bpb.boot_signature; - - return BpbKind.Dos32; - } - - if(useDos3Bpb) - { - fakeBpb.jump = dos30Bpb.jump; - fakeBpb.oem_name = dos30Bpb.oem_name; - fakeBpb.bps = dos30Bpb.bps; - fakeBpb.spc = dos30Bpb.spc; - fakeBpb.rsectors = dos30Bpb.rsectors; - fakeBpb.fats_no = dos30Bpb.fats_no; - fakeBpb.root_ent = dos30Bpb.root_ent; - fakeBpb.sectors = dos30Bpb.sectors; - fakeBpb.media = dos30Bpb.media; - fakeBpb.spfat = dos30Bpb.spfat; - fakeBpb.sptrk = dos30Bpb.sptrk; - fakeBpb.heads = dos30Bpb.heads; - fakeBpb.hsectors = dos30Bpb.hsectors; - fakeBpb.boot_code = dos30Bpb.boot_code; - fakeBpb.boot_signature = dos30Bpb.boot_signature; - - return BpbKind.Dos3; - } - - if(useDos2Bpb) - { - fakeBpb.jump = dos2Bpb.jump; - fakeBpb.oem_name = dos2Bpb.oem_name; - fakeBpb.bps = dos2Bpb.bps; - fakeBpb.spc = dos2Bpb.spc; - fakeBpb.rsectors = dos2Bpb.rsectors; - fakeBpb.fats_no = dos2Bpb.fats_no; - fakeBpb.root_ent = dos2Bpb.root_ent; - fakeBpb.sectors = dos2Bpb.sectors; - fakeBpb.media = dos2Bpb.media; - fakeBpb.spfat = dos2Bpb.spfat; - fakeBpb.boot_code = dos2Bpb.boot_code; - fakeBpb.boot_signature = dos2Bpb.boot_signature; - - return BpbKind.Dos2; - } - - if(useMsxBpb) - { - fakeBpb.jump = msxBpb.jump; - fakeBpb.oem_name = msxBpb.oem_name; - fakeBpb.bps = msxBpb.bps; - fakeBpb.spc = msxBpb.spc; - fakeBpb.rsectors = msxBpb.rsectors; - fakeBpb.fats_no = msxBpb.fats_no; - fakeBpb.root_ent = msxBpb.root_ent; - fakeBpb.sectors = msxBpb.sectors; - fakeBpb.media = msxBpb.media; - fakeBpb.spfat = msxBpb.spfat; - fakeBpb.sptrk = msxBpb.sptrk; - fakeBpb.heads = msxBpb.heads; - fakeBpb.hsectors = msxBpb.hsectors; - fakeBpb.boot_code = msxBpb.boot_code; - fakeBpb.boot_signature = msxBpb.boot_signature; - fakeBpb.serial_no = msxBpb.serial_no; - - // TODO: Is there any way to check this? - bootable = true; - - return BpbKind.Msx; - } - - if(useAtariBpb) - { - fakeBpb.jump = atariBpb.jump; - fakeBpb.oem_name = atariBpb.oem_name; - fakeBpb.bps = atariBpb.bps; - fakeBpb.spc = atariBpb.spc; - fakeBpb.rsectors = atariBpb.rsectors; - fakeBpb.fats_no = atariBpb.fats_no; - fakeBpb.root_ent = atariBpb.root_ent; - fakeBpb.sectors = atariBpb.sectors; - fakeBpb.media = atariBpb.media; - fakeBpb.spfat = atariBpb.spfat; - fakeBpb.sptrk = atariBpb.sptrk; - fakeBpb.heads = atariBpb.heads; - fakeBpb.boot_code = atariBpb.boot_code; - - return BpbKind.Atari; - } - - if(useApricotBpb) - { - fakeBpb.bps = apricotBpb.mainBPB.bps; - fakeBpb.spc = apricotBpb.mainBPB.spc; - fakeBpb.rsectors = apricotBpb.mainBPB.rsectors; - fakeBpb.fats_no = apricotBpb.mainBPB.fats_no; - fakeBpb.root_ent = apricotBpb.mainBPB.root_ent; - fakeBpb.sectors = apricotBpb.mainBPB.sectors; - fakeBpb.media = apricotBpb.mainBPB.media; - fakeBpb.spfat = apricotBpb.mainBPB.spfat; - fakeBpb.sptrk = apricotBpb.spt; - bootable = apricotBpb.bootType > 0; - - if(apricotBpb.bootLocation > 0 && - apricotBpb.bootLocation + apricotBpb.bootSize < imagePlugin.Info.Sectors) - imagePlugin.ReadSectors(apricotBpb.bootLocation, - (uint)(apricotBpb.sectorSize * apricotBpb.bootSize) / - imagePlugin.Info.SectorSize, out fakeBpb.boot_code); - - return BpbKind.Apricot; - } - - return BpbKind.None; + return BpbKind.Hardcoded; } + + if(useExtendedBpb) + { + fakeBpb = ebpb; + + return BpbKind.Extended; + } + + if(userShortExtendedBpb) + { + fakeBpb.jump = shortEbpb.jump; + fakeBpb.oem_name = shortEbpb.oem_name; + fakeBpb.bps = shortEbpb.bps; + fakeBpb.spc = shortEbpb.spc; + fakeBpb.rsectors = shortEbpb.rsectors; + fakeBpb.fats_no = shortEbpb.fats_no; + fakeBpb.root_ent = shortEbpb.root_ent; + fakeBpb.sectors = shortEbpb.sectors; + fakeBpb.media = shortEbpb.media; + fakeBpb.spfat = shortEbpb.spfat; + fakeBpb.sptrk = shortEbpb.sptrk; + fakeBpb.heads = shortEbpb.heads; + fakeBpb.hsectors = shortEbpb.hsectors; + fakeBpb.big_sectors = shortEbpb.big_sectors; + fakeBpb.drive_no = shortEbpb.drive_no; + fakeBpb.flags = shortEbpb.flags; + fakeBpb.signature = shortEbpb.signature; + fakeBpb.serial_no = shortEbpb.serial_no; + fakeBpb.boot_code = shortEbpb.boot_code; + fakeBpb.boot_signature = shortEbpb.boot_signature; + + return BpbKind.ShortExtended; + } + + if(useDos33Bpb) + { + fakeBpb.jump = dos33Bpb.jump; + fakeBpb.oem_name = dos33Bpb.oem_name; + fakeBpb.bps = dos33Bpb.bps; + fakeBpb.spc = dos33Bpb.spc; + fakeBpb.rsectors = dos33Bpb.rsectors; + fakeBpb.fats_no = dos33Bpb.fats_no; + fakeBpb.root_ent = dos33Bpb.root_ent; + fakeBpb.sectors = dos33Bpb.sectors; + fakeBpb.media = dos33Bpb.media; + fakeBpb.spfat = dos33Bpb.spfat; + fakeBpb.sptrk = dos33Bpb.sptrk; + fakeBpb.heads = dos33Bpb.heads; + fakeBpb.hsectors = dos33Bpb.hsectors; + fakeBpb.big_sectors = dos33Bpb.big_sectors; + fakeBpb.boot_code = dos33Bpb.boot_code; + fakeBpb.boot_signature = dos33Bpb.boot_signature; + + return BpbKind.Dos33; + } + + if(useDos32Bpb) + { + fakeBpb.jump = dos32Bpb.jump; + fakeBpb.oem_name = dos32Bpb.oem_name; + fakeBpb.bps = dos32Bpb.bps; + fakeBpb.spc = dos32Bpb.spc; + fakeBpb.rsectors = dos32Bpb.rsectors; + fakeBpb.fats_no = dos32Bpb.fats_no; + fakeBpb.root_ent = dos32Bpb.root_ent; + fakeBpb.sectors = dos32Bpb.sectors; + fakeBpb.media = dos32Bpb.media; + fakeBpb.spfat = dos32Bpb.spfat; + fakeBpb.sptrk = dos32Bpb.sptrk; + fakeBpb.heads = dos32Bpb.heads; + fakeBpb.hsectors = dos32Bpb.hsectors; + fakeBpb.boot_code = dos32Bpb.boot_code; + fakeBpb.boot_signature = dos32Bpb.boot_signature; + + return BpbKind.Dos32; + } + + if(useDos3Bpb) + { + fakeBpb.jump = dos30Bpb.jump; + fakeBpb.oem_name = dos30Bpb.oem_name; + fakeBpb.bps = dos30Bpb.bps; + fakeBpb.spc = dos30Bpb.spc; + fakeBpb.rsectors = dos30Bpb.rsectors; + fakeBpb.fats_no = dos30Bpb.fats_no; + fakeBpb.root_ent = dos30Bpb.root_ent; + fakeBpb.sectors = dos30Bpb.sectors; + fakeBpb.media = dos30Bpb.media; + fakeBpb.spfat = dos30Bpb.spfat; + fakeBpb.sptrk = dos30Bpb.sptrk; + fakeBpb.heads = dos30Bpb.heads; + fakeBpb.hsectors = dos30Bpb.hsectors; + fakeBpb.boot_code = dos30Bpb.boot_code; + fakeBpb.boot_signature = dos30Bpb.boot_signature; + + return BpbKind.Dos3; + } + + if(useDos2Bpb) + { + fakeBpb.jump = dos2Bpb.jump; + fakeBpb.oem_name = dos2Bpb.oem_name; + fakeBpb.bps = dos2Bpb.bps; + fakeBpb.spc = dos2Bpb.spc; + fakeBpb.rsectors = dos2Bpb.rsectors; + fakeBpb.fats_no = dos2Bpb.fats_no; + fakeBpb.root_ent = dos2Bpb.root_ent; + fakeBpb.sectors = dos2Bpb.sectors; + fakeBpb.media = dos2Bpb.media; + fakeBpb.spfat = dos2Bpb.spfat; + fakeBpb.boot_code = dos2Bpb.boot_code; + fakeBpb.boot_signature = dos2Bpb.boot_signature; + + return BpbKind.Dos2; + } + + if(useMsxBpb) + { + fakeBpb.jump = msxBpb.jump; + fakeBpb.oem_name = msxBpb.oem_name; + fakeBpb.bps = msxBpb.bps; + fakeBpb.spc = msxBpb.spc; + fakeBpb.rsectors = msxBpb.rsectors; + fakeBpb.fats_no = msxBpb.fats_no; + fakeBpb.root_ent = msxBpb.root_ent; + fakeBpb.sectors = msxBpb.sectors; + fakeBpb.media = msxBpb.media; + fakeBpb.spfat = msxBpb.spfat; + fakeBpb.sptrk = msxBpb.sptrk; + fakeBpb.heads = msxBpb.heads; + fakeBpb.hsectors = msxBpb.hsectors; + fakeBpb.boot_code = msxBpb.boot_code; + fakeBpb.boot_signature = msxBpb.boot_signature; + fakeBpb.serial_no = msxBpb.serial_no; + + // TODO: Is there any way to check this? + bootable = true; + + return BpbKind.Msx; + } + + if(useAtariBpb) + { + fakeBpb.jump = atariBpb.jump; + fakeBpb.oem_name = atariBpb.oem_name; + fakeBpb.bps = atariBpb.bps; + fakeBpb.spc = atariBpb.spc; + fakeBpb.rsectors = atariBpb.rsectors; + fakeBpb.fats_no = atariBpb.fats_no; + fakeBpb.root_ent = atariBpb.root_ent; + fakeBpb.sectors = atariBpb.sectors; + fakeBpb.media = atariBpb.media; + fakeBpb.spfat = atariBpb.spfat; + fakeBpb.sptrk = atariBpb.sptrk; + fakeBpb.heads = atariBpb.heads; + fakeBpb.boot_code = atariBpb.boot_code; + + return BpbKind.Atari; + } + + if(useApricotBpb) + { + fakeBpb.bps = apricotBpb.mainBPB.bps; + fakeBpb.spc = apricotBpb.mainBPB.spc; + fakeBpb.rsectors = apricotBpb.mainBPB.rsectors; + fakeBpb.fats_no = apricotBpb.mainBPB.fats_no; + fakeBpb.root_ent = apricotBpb.mainBPB.root_ent; + fakeBpb.sectors = apricotBpb.mainBPB.sectors; + fakeBpb.media = apricotBpb.mainBPB.media; + fakeBpb.spfat = apricotBpb.mainBPB.spfat; + fakeBpb.sptrk = apricotBpb.spt; + bootable = apricotBpb.bootType > 0; + + if(apricotBpb.bootLocation > 0 && + apricotBpb.bootLocation + apricotBpb.bootSize < imagePlugin.Info.Sectors) + imagePlugin.ReadSectors(apricotBpb.bootLocation, + (uint)(apricotBpb.sectorSize * apricotBpb.bootSize) / + imagePlugin.Info.SectorSize, out fakeBpb.boot_code); + + return BpbKind.Apricot; + } + + return BpbKind.None; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/Consts.cs b/Aaru.Filesystems/FAT/Consts.cs index d974d5ade..30afff3fb 100644 --- a/Aaru.Filesystems/FAT/Consts.cs +++ b/Aaru.Filesystems/FAT/Consts.cs @@ -34,193 +34,192 @@ using System; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class FAT { - public sealed partial class FAT + const uint FSINFO_SIGNATURE1 = 0x41615252; + const uint FSINFO_SIGNATURE2 = 0x61417272; + const uint FSINFO_SIGNATURE3 = 0xAA550000; + /// Directory finishes + const byte DIRENT_FINISHED = 0x00; + /// Deleted directory entry + const byte DIRENT_DELETED = 0xE5; + /// Minimum allowed value in name/extension + const byte DIRENT_MIN = 0x20; + /// Value used instead of for first name character + const byte DIRENT_E5 = 0x05; + /// Entry points to self or parent directory + const byte DIRENT_SUBDIR = 0x2E; + const uint FAT32_MASK = 0x0FFFFFFF; + const uint FAT32_END_MASK = 0xFFFFFF8; + const uint FAT32_FORMATTED = 0xFFFFFF6; + const uint FAT32_BAD = 0xFFFFFF7; + const uint FAT32_RESERVED = 0xFFFFFF0; + const ushort FAT16_END_MASK = 0xFFF8; + const ushort FAT16_FORMATTED = 0xFFF6; + const ushort FAT16_BAD = 0xFFF7; + const ushort FAT16_RESERVED = 0xFFF0; + const ushort FAT12_END_MASK = 0xFF8; + const ushort FAT12_FORMATTED = 0xFF6; + const ushort FAT12_BAD = 0xFF7; + const ushort FAT12_RESERVED = 0xFF0; + const byte LFN_ERASED = 0x80; + const byte LFN_LAST = 0x40; + const byte LFN_MASK = 0x1F; + const ushort EADATA_MAGIC = 0x4445; + const ushort EASCTR_MAGIC = 0x4145; + const ushort EA_UNUSED = 0xFFFF; + const ushort EAT_BINARY = 0xFFFE; + const ushort EAT_ASCII = 0xFFFD; + const ushort EAT_BITMAP = 0xFFFB; + const ushort EAT_METAFILE = 0xFFFA; + const ushort EAT_ICON = 0xFFF9; + const ushort EAT_EA = 0xFFEE; + const ushort EAT_MVMT = 0xFFDF; + const ushort EAT_MVST = 0xFFDE; + const ushort EAT_ASN1 = 0xFFDD; + const string FAT32_EA_TAIL = " EA. SF"; + + readonly (string hash, string name)[] _knownBootHashes = { - const uint FSINFO_SIGNATURE1 = 0x41615252; - const uint FSINFO_SIGNATURE2 = 0x61417272; - const uint FSINFO_SIGNATURE3 = 0xAA550000; - /// Directory finishes - const byte DIRENT_FINISHED = 0x00; - /// Deleted directory entry - const byte DIRENT_DELETED = 0xE5; - /// Minimum allowed value in name/extension - const byte DIRENT_MIN = 0x20; - /// Value used instead of for first name character - const byte DIRENT_E5 = 0x05; - /// Entry points to self or parent directory - const byte DIRENT_SUBDIR = 0x2E; - const uint FAT32_MASK = 0x0FFFFFFF; - const uint FAT32_END_MASK = 0xFFFFFF8; - const uint FAT32_FORMATTED = 0xFFFFFF6; - const uint FAT32_BAD = 0xFFFFFF7; - const uint FAT32_RESERVED = 0xFFFFFF0; - const ushort FAT16_END_MASK = 0xFFF8; - const ushort FAT16_FORMATTED = 0xFFF6; - const ushort FAT16_BAD = 0xFFF7; - const ushort FAT16_RESERVED = 0xFFF0; - const ushort FAT12_END_MASK = 0xFF8; - const ushort FAT12_FORMATTED = 0xFF6; - const ushort FAT12_BAD = 0xFF7; - const ushort FAT12_RESERVED = 0xFF0; - const byte LFN_ERASED = 0x80; - const byte LFN_LAST = 0x40; - const byte LFN_MASK = 0x1F; - const ushort EADATA_MAGIC = 0x4445; - const ushort EASCTR_MAGIC = 0x4145; - const ushort EA_UNUSED = 0xFFFF; - const ushort EAT_BINARY = 0xFFFE; - const ushort EAT_ASCII = 0xFFFD; - const ushort EAT_BITMAP = 0xFFFB; - const ushort EAT_METAFILE = 0xFFFA; - const ushort EAT_ICON = 0xFFF9; - const ushort EAT_EA = 0xFFEE; - const ushort EAT_MVMT = 0xFFDF; - const ushort EAT_MVST = 0xFFDE; - const ushort EAT_ASN1 = 0xFFDD; - const string FAT32_EA_TAIL = " EA. SF"; + ("b639b4d5b25f63560e3b34a3a0feb732aa65486f", "Amstrad MS-DOS 3.20 (8-sector floppy)"), + ("9311151f13f7611b1431593da05ddd3153370574", "Amstrad MS-DOS 3.20 (Spanish)"), + ("55eda6a9b955f5199020e6b56a6954fa6fcb7dc6", "AT&T MS-DOS 2.11"), + ("d5e10822977efa96e4fbaec2b268ca008d74fe6f", "Atari TOS"), + ("17f11a12b96899d2a4976d889cef160502167f2d", "BeOS"), + ("d0e31673028fcfcea38dff71a7be13669aa20b8d", "Compaq MS-DOS 3.30"), + ("3aa4ce2fa6f9a297b5b15aaef930401af369fcbc", "Compaq MS-DOS 3.30 (8-sector floppy)"), + ("8f1d33520343f35034aa3ce47e4180b10e960b43", "Compaq MS-DOS 3.30 (8-sector floppy)"), + ("2f4011ae0670ff3aff2bdd412a4651f255b600a9", "Compaq MS-DOS 3.31"), + ("afc3fb751089a52c9f0bd37098d3137d32ab4982", "Concurrent DOS 6.0"), + ("c25e2d93d3b8bf9870043bf9d12580ef07f5375e", "CrossDOS"), + ("43fb2afa1ab3102b3f8d0fe901b652a5b0a973e1", "DR-DOS >=7.02"), + ("92d83b7e9e3bd4b73c6b98f8c6434206eac7212f", "DR-DOS 3.40"), + ("6a7aba05f91c5a7108edc5d5ccc9dac0ebf5dd28", "DR-DOS 3.40"), + ("715e78bb1e38b56452dd1c15db8c096dc506eea3", "DR-DOS 3.41"), + ("01e46cb2bc6d65ddd89ba28d254baf563d8fc609", "DR-DOS 5.00"), + ("dd297f159eef8a61f5eec638ce7318a098fa26f4", "DR-DOS 5.00"), + ("f590e53ae7f9caec5dba93241e557710be61b6fe", "DR-DOS 6.00"), + ("630e4aaf230f15eb2d09985f294815b6bc50384c", "DR-DOS 6.00"), + ("8851459816d714f53b9c469472e51460ebd44b98", "DR-DOS 7.03"), + ("cf24388b61eb1b137f2bb8a4c319e7461d460b72", "DR-DOS 7.03"), + ("26bf8efe8368e598397b1f79d635faccf5ca4f87", "DR-DOS 8.00"), + ("36ddd6bf8686801f5a2a3cbd4656afa175cabdfc", "DR-DOS 8.00"), + ("9ac09781e4090d9ba5e1a31816b4ebfa6b54f39e", "DR-DOS 8.00"), + ("f5d68f26abec8392ac23716581c4ab1d6e8456a2", "eComStation"), + ("e2a852db8c3eb3d86ca86a706186a9dd54cdc815", "Epson MS-DOS 3.10"), + ("74675d158dd0f6b5983bd30dda7018a814bd34dd", "Epson MS-DOS 3.20"), + ("683a04f34714555df5e862f709f9abc7de51488e", "Epson MS-DOS 5.00 (PC-98)"), + ("1b062df94fc576af069e40603bf2558000a2ca10", "FreeBSD, NetBSD, Mac OS X"), + ("33c8e306b3a51e09668fd60f099450019a1238ea", "FreeBSD, NetBSD, Mac OS X"), + ("ca386b1cefaf964a49192c2cd08077aff09b82af", "FreeDOS"), + ("26033e0db1ee4f439f07077b790e189d1b77688c", "HP MS-DOS 3.20"), + ("66867cd665e0e87c32de0bcb721ecfe91a551bc5", "mkfs.vfat"), + ("48d4811dbea5d724803017d6d45a49d604982b7b", "mkfs.vfat"), + ("02d615c5fb68bc49766bf89777dd36accb1428b7", "MS-DOS >=4.01, PC-DOS >=5.00 (8-sector floppy)"), + ("cffb1dc01bf9f533ba63d949c359c9ae97944c9b", "MS-DOS >=5.00"), + ("bd099151f2f9f8b3815eef7d9fed90abead52d97", "MS-DOS 3.21"), + ("f338eeff68f4e03ce489eb165e18fd94f8a0c63e", "MS-DOS 3.30A"), + ("0eb0c789e141d59d91f075ef14d63fd4255aeac2", "MS-DOS 3.30A, 5.00, 6.xx (8-sector floppy)"), + ("7aa06595490f92e542965b43805eaa0d6da9c86c", "MS-DOS 3.31"), + ("00401ca66900d7defcbc3d794654d1ba2376e83d", "MS-DOS 3.31"), + ("00e39a27d9b36e88f2b0caaa1959a8e17223bf31", "MS-DOS 3.31 (8-sector floppy)"), + ("1e74fbad5948582247280b116e7175b5a16bcede", "MS-DOS 3.31 (8-sector floppy)"), + ("1cfb2cc3a34c8164b8f5051c118643b23a60f4d0", "MS-DOS 4.01"), + ("d370551c8aca9cfcd964e2a1235071329f93cc03", "Multiuser DOS 7.22r4"), + ("7106ea11dd0b9a39649cbb4f6314a0fa1241da38", "NEC >=MS-DOS 5.00 (PC-98)"), + ("30d4f5f54215af0fc69236594e52b94989b35c21", "NEC MS-DOS 3.30 (PC-98)"), + ("eae42777f562eb81ed624f0c0479347ba11158c9", "Novell DOS 7.00"), + ("c67a7d0bab94a960cca8720d476f5317c960b2fb", "Novell DOS 7.00"), + ("0ed508c71bcf1418a1701b9267ddf166e7993b6b", "Olivetti MS-DOS 3.10"), + ("3e1a3f22973d9f2d15f9a053204c2f7b72de00a9", "OS/2 1.00"), + ("4acb13943f21a266f9eb110969980481783c41c4", "OS/2 1.10"), + ("cc7b32236c76d34edefdac3ca6a7be8e26163cea", "OS/2 1.20, 1.30"), + ("05c6706189fa0532ea83a7484b88d1dcba63e167", "OS/2 2.00"), + ("eefb4383b3e2b05f7f7e0051d58f4617a4f39e42", "OS/2 2.1x, Warp 3"), + ("b1d9666ae8781958242da27d319e73aa2dda6805", "OS/2 Warp 4"), + ("661d92970ab0f81cec453dc7584e0debdd1b4928", "PC-DOS >=5.00"), + ("78522b303fe5752f3cb37eabb6cb54e6cb0cd276", "PC-DOS 2.xx"), + ("277edfefdc3f05a219f9378076227c4126b6c8ef", "PC-DOS 3.00"), + ("f72ef6ff4c90a170bc8cdd1b01e8d98235c16a3b", "PC-DOS 3.10"), + ("cb6b6c1bc024e025710288da652d0d93527a71db", "PC-DOS 3.30"), + ("3b844e2a411182958c2d9e6ee17c6d4fff18bd73", "PC-DOS 4.00"), + ("45e82fcff4c6f8a9c31418323bc063011f5730e5", "PCExchange 2.0"), + ("707849fd75b6a52fd219c3cebe060ecb23c40fc7", "SCO OpenServer"), + ("9f7f146b513b00ff9e8b5b927aee24025c3f3fb8", "Toshiba MS-DOS 3.30"), + ("30eafc45a4606a7b840dcd5899dfb977a837c835", "Toshiba MS-DOS 4.01"), + ("5695a9a69637bd4d4eaa531b976d6f3b71e8d4ad", "Toshiba MS-DOS 4.01"), + ("036a59138d620c16e8d0dba45af4dec4ae1376f7", "Windows 10"), + ("3dd2941b79f0f6644b3a973c7d81e64e434c0b70", "Windows 2000"), + ("cd1e2fced2e49825df23c08a3d1e280c1cf468a7", "Windows 2000"), + ("c20c6e706be97091768da8654fffc2f3c0431318", "Windows 95"), + ("cf750cc0d2d52251a1a0fb9f2568fed3ff81717a", "Windows 95 >=OSR2"), + ("48865a298d4bbe73f89c4de10153e16250c1a9ae", "Windows 95 >=OSR2"), + ("1d2df8f3b1b336fc4aa1c6e49b21956194884b41", "Windows 98 (Spanish)"), + ("6e6fb4a3ea034415d716b1f81217ffecf78813c3", "Windows 98 (Spanish)"), + ("9b5e6be09200145a6ed7c22d14b1e6b4de2f362b", "Windows 98 (Spanish)"), + ("3cea1921d29fcd3343d36c090cb3e3dba926781d", "Windows 98, Me"), + ("037f9c8caed602d93c88f7e9d8f13a732b3ada76", "Windows NT"), + ("a63806bfe11140c873082318dd4da834068be327", "Windows Vista"), + ("8f024b3d501c39ee6e3f8ca28173ad6a780d3eb0", "Windows Vista, 8, 10"), + ("d3e93f8b82ef250db216037d827a4896dc97d2be", "TracerST"), // OEM ID: "TracerST" + //("b741f85ef40288ccc8887de1f6e849009097e1c9", "Norton Utilities"), // OEM ID: "IBM PNCI", need to confirm + ("c49b275537ac7237cac64d83f34d2024ae0ca96a", + "Windows NT (Spanish)"), // Need to check Windows >= 2000 (Spanish) + //("a48b0e4b696317eed829e960d1aa576562a4f185", "TracerST"), // Unknown OEM ID, apparently Tracer, unconfirmed + ("fe477972602ba76658ff7143859045b3c4036ca5", + "iomega"), // OEM ID: "SHIPDISK", contains timedate on boot code may not be unique + ("ef79a1f33e5237827eb812dda548f0e4e916d815", "GEOS"), // OEM ID: "GEOWORKS" + ("8524587ee91494cc51cc2c9d07453e84be0cdc33", "Hero Soft v1.10"), + ("681a0d9d662ba368e6acb0d0bf602e1f56411144", "Human68k 2.00"), + ("91e2b47c3cb46611249e4daa283a68ba21ba596a", "Human68k 2.00") + }; - readonly (string hash, string name)[] _knownBootHashes = - { - ("b639b4d5b25f63560e3b34a3a0feb732aa65486f", "Amstrad MS-DOS 3.20 (8-sector floppy)"), - ("9311151f13f7611b1431593da05ddd3153370574", "Amstrad MS-DOS 3.20 (Spanish)"), - ("55eda6a9b955f5199020e6b56a6954fa6fcb7dc6", "AT&T MS-DOS 2.11"), - ("d5e10822977efa96e4fbaec2b268ca008d74fe6f", "Atari TOS"), - ("17f11a12b96899d2a4976d889cef160502167f2d", "BeOS"), - ("d0e31673028fcfcea38dff71a7be13669aa20b8d", "Compaq MS-DOS 3.30"), - ("3aa4ce2fa6f9a297b5b15aaef930401af369fcbc", "Compaq MS-DOS 3.30 (8-sector floppy)"), - ("8f1d33520343f35034aa3ce47e4180b10e960b43", "Compaq MS-DOS 3.30 (8-sector floppy)"), - ("2f4011ae0670ff3aff2bdd412a4651f255b600a9", "Compaq MS-DOS 3.31"), - ("afc3fb751089a52c9f0bd37098d3137d32ab4982", "Concurrent DOS 6.0"), - ("c25e2d93d3b8bf9870043bf9d12580ef07f5375e", "CrossDOS"), - ("43fb2afa1ab3102b3f8d0fe901b652a5b0a973e1", "DR-DOS >=7.02"), - ("92d83b7e9e3bd4b73c6b98f8c6434206eac7212f", "DR-DOS 3.40"), - ("6a7aba05f91c5a7108edc5d5ccc9dac0ebf5dd28", "DR-DOS 3.40"), - ("715e78bb1e38b56452dd1c15db8c096dc506eea3", "DR-DOS 3.41"), - ("01e46cb2bc6d65ddd89ba28d254baf563d8fc609", "DR-DOS 5.00"), - ("dd297f159eef8a61f5eec638ce7318a098fa26f4", "DR-DOS 5.00"), - ("f590e53ae7f9caec5dba93241e557710be61b6fe", "DR-DOS 6.00"), - ("630e4aaf230f15eb2d09985f294815b6bc50384c", "DR-DOS 6.00"), - ("8851459816d714f53b9c469472e51460ebd44b98", "DR-DOS 7.03"), - ("cf24388b61eb1b137f2bb8a4c319e7461d460b72", "DR-DOS 7.03"), - ("26bf8efe8368e598397b1f79d635faccf5ca4f87", "DR-DOS 8.00"), - ("36ddd6bf8686801f5a2a3cbd4656afa175cabdfc", "DR-DOS 8.00"), - ("9ac09781e4090d9ba5e1a31816b4ebfa6b54f39e", "DR-DOS 8.00"), - ("f5d68f26abec8392ac23716581c4ab1d6e8456a2", "eComStation"), - ("e2a852db8c3eb3d86ca86a706186a9dd54cdc815", "Epson MS-DOS 3.10"), - ("74675d158dd0f6b5983bd30dda7018a814bd34dd", "Epson MS-DOS 3.20"), - ("683a04f34714555df5e862f709f9abc7de51488e", "Epson MS-DOS 5.00 (PC-98)"), - ("1b062df94fc576af069e40603bf2558000a2ca10", "FreeBSD, NetBSD, Mac OS X"), - ("33c8e306b3a51e09668fd60f099450019a1238ea", "FreeBSD, NetBSD, Mac OS X"), - ("ca386b1cefaf964a49192c2cd08077aff09b82af", "FreeDOS"), - ("26033e0db1ee4f439f07077b790e189d1b77688c", "HP MS-DOS 3.20"), - ("66867cd665e0e87c32de0bcb721ecfe91a551bc5", "mkfs.vfat"), - ("48d4811dbea5d724803017d6d45a49d604982b7b", "mkfs.vfat"), - ("02d615c5fb68bc49766bf89777dd36accb1428b7", "MS-DOS >=4.01, PC-DOS >=5.00 (8-sector floppy)"), - ("cffb1dc01bf9f533ba63d949c359c9ae97944c9b", "MS-DOS >=5.00"), - ("bd099151f2f9f8b3815eef7d9fed90abead52d97", "MS-DOS 3.21"), - ("f338eeff68f4e03ce489eb165e18fd94f8a0c63e", "MS-DOS 3.30A"), - ("0eb0c789e141d59d91f075ef14d63fd4255aeac2", "MS-DOS 3.30A, 5.00, 6.xx (8-sector floppy)"), - ("7aa06595490f92e542965b43805eaa0d6da9c86c", "MS-DOS 3.31"), - ("00401ca66900d7defcbc3d794654d1ba2376e83d", "MS-DOS 3.31"), - ("00e39a27d9b36e88f2b0caaa1959a8e17223bf31", "MS-DOS 3.31 (8-sector floppy)"), - ("1e74fbad5948582247280b116e7175b5a16bcede", "MS-DOS 3.31 (8-sector floppy)"), - ("1cfb2cc3a34c8164b8f5051c118643b23a60f4d0", "MS-DOS 4.01"), - ("d370551c8aca9cfcd964e2a1235071329f93cc03", "Multiuser DOS 7.22r4"), - ("7106ea11dd0b9a39649cbb4f6314a0fa1241da38", "NEC >=MS-DOS 5.00 (PC-98)"), - ("30d4f5f54215af0fc69236594e52b94989b35c21", "NEC MS-DOS 3.30 (PC-98)"), - ("eae42777f562eb81ed624f0c0479347ba11158c9", "Novell DOS 7.00"), - ("c67a7d0bab94a960cca8720d476f5317c960b2fb", "Novell DOS 7.00"), - ("0ed508c71bcf1418a1701b9267ddf166e7993b6b", "Olivetti MS-DOS 3.10"), - ("3e1a3f22973d9f2d15f9a053204c2f7b72de00a9", "OS/2 1.00"), - ("4acb13943f21a266f9eb110969980481783c41c4", "OS/2 1.10"), - ("cc7b32236c76d34edefdac3ca6a7be8e26163cea", "OS/2 1.20, 1.30"), - ("05c6706189fa0532ea83a7484b88d1dcba63e167", "OS/2 2.00"), - ("eefb4383b3e2b05f7f7e0051d58f4617a4f39e42", "OS/2 2.1x, Warp 3"), - ("b1d9666ae8781958242da27d319e73aa2dda6805", "OS/2 Warp 4"), - ("661d92970ab0f81cec453dc7584e0debdd1b4928", "PC-DOS >=5.00"), - ("78522b303fe5752f3cb37eabb6cb54e6cb0cd276", "PC-DOS 2.xx"), - ("277edfefdc3f05a219f9378076227c4126b6c8ef", "PC-DOS 3.00"), - ("f72ef6ff4c90a170bc8cdd1b01e8d98235c16a3b", "PC-DOS 3.10"), - ("cb6b6c1bc024e025710288da652d0d93527a71db", "PC-DOS 3.30"), - ("3b844e2a411182958c2d9e6ee17c6d4fff18bd73", "PC-DOS 4.00"), - ("45e82fcff4c6f8a9c31418323bc063011f5730e5", "PCExchange 2.0"), - ("707849fd75b6a52fd219c3cebe060ecb23c40fc7", "SCO OpenServer"), - ("9f7f146b513b00ff9e8b5b927aee24025c3f3fb8", "Toshiba MS-DOS 3.30"), - ("30eafc45a4606a7b840dcd5899dfb977a837c835", "Toshiba MS-DOS 4.01"), - ("5695a9a69637bd4d4eaa531b976d6f3b71e8d4ad", "Toshiba MS-DOS 4.01"), - ("036a59138d620c16e8d0dba45af4dec4ae1376f7", "Windows 10"), - ("3dd2941b79f0f6644b3a973c7d81e64e434c0b70", "Windows 2000"), - ("cd1e2fced2e49825df23c08a3d1e280c1cf468a7", "Windows 2000"), - ("c20c6e706be97091768da8654fffc2f3c0431318", "Windows 95"), - ("cf750cc0d2d52251a1a0fb9f2568fed3ff81717a", "Windows 95 >=OSR2"), - ("48865a298d4bbe73f89c4de10153e16250c1a9ae", "Windows 95 >=OSR2"), - ("1d2df8f3b1b336fc4aa1c6e49b21956194884b41", "Windows 98 (Spanish)"), - ("6e6fb4a3ea034415d716b1f81217ffecf78813c3", "Windows 98 (Spanish)"), - ("9b5e6be09200145a6ed7c22d14b1e6b4de2f362b", "Windows 98 (Spanish)"), - ("3cea1921d29fcd3343d36c090cb3e3dba926781d", "Windows 98, Me"), - ("037f9c8caed602d93c88f7e9d8f13a732b3ada76", "Windows NT"), - ("a63806bfe11140c873082318dd4da834068be327", "Windows Vista"), - ("8f024b3d501c39ee6e3f8ca28173ad6a780d3eb0", "Windows Vista, 8, 10"), - ("d3e93f8b82ef250db216037d827a4896dc97d2be", "TracerST"), // OEM ID: "TracerST" - //("b741f85ef40288ccc8887de1f6e849009097e1c9", "Norton Utilities"), // OEM ID: "IBM PNCI", need to confirm - ("c49b275537ac7237cac64d83f34d2024ae0ca96a", - "Windows NT (Spanish)"), // Need to check Windows >= 2000 (Spanish) - //("a48b0e4b696317eed829e960d1aa576562a4f185", "TracerST"), // Unknown OEM ID, apparently Tracer, unconfirmed - ("fe477972602ba76658ff7143859045b3c4036ca5", - "iomega"), // OEM ID: "SHIPDISK", contains timedate on boot code may not be unique - ("ef79a1f33e5237827eb812dda548f0e4e916d815", "GEOS"), // OEM ID: "GEOWORKS" - ("8524587ee91494cc51cc2c9d07453e84be0cdc33", "Hero Soft v1.10"), - ("681a0d9d662ba368e6acb0d0bf602e1f56411144", "Human68k 2.00"), - ("91e2b47c3cb46611249e4daa283a68ba21ba596a", "Human68k 2.00") - }; + [Flags] + enum FatAttributes : byte + { + ReadOnly = 0x01, Hidden = 0x02, System = 0x04, + VolumeLabel = 0x08, Subdirectory = 0x10, Archive = 0x20, + Device = 0x40, Reserved = 0x80, LFN = 0x0F + } - [Flags] - enum FatAttributes : byte - { - ReadOnly = 0x01, Hidden = 0x02, System = 0x04, - VolumeLabel = 0x08, Subdirectory = 0x10, Archive = 0x20, - Device = 0x40, Reserved = 0x80, LFN = 0x0F - } + enum BpbKind + { + None, Hardcoded, Atari, + Msx, Dos2, Dos3, + Dos32, Dos33, ShortExtended, + Extended, ShortFat32, LongFat32, + Andos, Apricot, DecRainbow, + Human + } - enum BpbKind - { - None, Hardcoded, Atari, - Msx, Dos2, Dos3, - Dos32, Dos33, ShortExtended, - Extended, ShortFat32, LongFat32, - Andos, Apricot, DecRainbow, - Human - } + enum Namespace + { + Dos, Nt, Lfn, + Os2, Ecs, Human + } - enum Namespace - { - Dos, Nt, Lfn, - Os2, Ecs, Human - } + [Flags] + enum EaFlags : uint + { + Normal = 0, Critical = 1 + } - [Flags] - enum EaFlags : uint - { - Normal = 0, Critical = 1 - } - - [Flags] - enum CaseInfo : byte - { - /// FASTFAT.SYS indicator that basename is lowercase - LowerCaseBasename = 0x08, - /// FASTFAT.SYS indicator that extension is lowercase - LowerCaseExtension = 0x10, AllLowerCase = 0x18, - /// FAT32.IFS < 0.97 indicator for normal EAs present - NormalEaOld = 0xEA, - /// FAT32.IFS < 0.97 indicator for critical EAs present - CriticalEaOld = 0xEC, - /// FAT32.IFS >= 0.97 indicator for normal EAs present - NormalEa = 0x40, - /// FAT32.IFS >= 0.97 indicator for critical EAs present - CriticalEa = 0x80 - } + [Flags] + enum CaseInfo : byte + { + /// FASTFAT.SYS indicator that basename is lowercase + LowerCaseBasename = 0x08, + /// FASTFAT.SYS indicator that extension is lowercase + LowerCaseExtension = 0x10, AllLowerCase = 0x18, + /// FAT32.IFS < 0.97 indicator for normal EAs present + NormalEaOld = 0xEA, + /// FAT32.IFS < 0.97 indicator for critical EAs present + CriticalEaOld = 0xEC, + /// FAT32.IFS >= 0.97 indicator for normal EAs present + NormalEa = 0x40, + /// FAT32.IFS >= 0.97 indicator for critical EAs present + CriticalEa = 0x80 } } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/Dir.cs b/Aaru.Filesystems/FAT/Dir.cs index 0d29d59b0..7d6c2605f 100644 --- a/Aaru.Filesystems/FAT/Dir.cs +++ b/Aaru.Filesystems/FAT/Dir.cs @@ -39,58 +39,72 @@ using Aaru.CommonTypes.Enums; using Aaru.Console; using Aaru.Helpers; -namespace Aaru.Filesystems -{ - public sealed partial class FAT - { - /// - /// Solves a symbolic link. - /// Link path. - /// Link destination. - public ErrorNumber ReadLink(string path, out string dest) - { - dest = null; +namespace Aaru.Filesystems; - return ErrorNumber.NotSupported; +public sealed partial class FAT +{ + /// + /// Solves a symbolic link. + /// Link path. + /// Link destination. + public ErrorNumber ReadLink(string path, out string dest) + { + dest = null; + + return ErrorNumber.NotSupported; + } + + /// + /// Lists contents from a directory. + /// Directory path. + /// Directory contents. + public ErrorNumber ReadDir(string path, out List contents) + { + contents = null; + ErrorNumber errno = ErrorNumber.NoError; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(string.IsNullOrWhiteSpace(path) || + path == "/") + { + contents = _rootDirectoryCache.Keys.ToList(); + + return ErrorNumber.NoError; } - /// - /// Lists contents from a directory. - /// Directory path. - /// Directory contents. - public ErrorNumber ReadDir(string path, out List contents) + string cutPath = path.StartsWith("/", StringComparison.Ordinal) ? path.Substring(1).ToLower(_cultureInfo) + : path.ToLower(_cultureInfo); + + if(_directoryCache.TryGetValue(cutPath, out Dictionary currentDirectory)) { - contents = null; - ErrorNumber errno = ErrorNumber.NoError; + contents = currentDirectory.Keys.ToList(); - if(!_mounted) - return ErrorNumber.AccessDenied; + return ErrorNumber.NoError; + } - if(string.IsNullOrWhiteSpace(path) || - path == "/") - { - contents = _rootDirectoryCache.Keys.ToList(); + string[] pieces = cutPath.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); - return ErrorNumber.NoError; - } + KeyValuePair entry = + _rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[0]); - string cutPath = path.StartsWith("/", StringComparison.Ordinal) ? path.Substring(1).ToLower(_cultureInfo) - : path.ToLower(_cultureInfo); + if(string.IsNullOrEmpty(entry.Key)) + return ErrorNumber.NoSuchFile; - if(_directoryCache.TryGetValue(cutPath, out Dictionary currentDirectory)) - { - contents = currentDirectory.Keys.ToList(); + if(!entry.Value.Dirent.attributes.HasFlag(FatAttributes.Subdirectory)) + return ErrorNumber.NotDirectory; - return ErrorNumber.NoError; - } + string currentPath = pieces[0]; - string[] pieces = cutPath.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + currentDirectory = _rootDirectoryCache; - KeyValuePair entry = - _rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[0]); + for(int p = 0; p < pieces.Length; p++) + { + entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[p]); if(string.IsNullOrEmpty(entry.Key)) return ErrorNumber.NoSuchFile; @@ -98,302 +112,287 @@ namespace Aaru.Filesystems if(!entry.Value.Dirent.attributes.HasFlag(FatAttributes.Subdirectory)) return ErrorNumber.NotDirectory; - string currentPath = pieces[0]; + currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; + uint currentCluster = entry.Value.Dirent.start_cluster; - currentDirectory = _rootDirectoryCache; + if(_fat32) + currentCluster += (uint)(entry.Value.Dirent.ea_handle << 16); - for(int p = 0; p < pieces.Length; p++) + if(_directoryCache.TryGetValue(currentPath, out currentDirectory)) + continue; + + // Reserved unallocated directory, seen in Atari ST + if(currentCluster == 0) { - entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[p]); + _directoryCache[currentPath] = new Dictionary(); + contents = new List(); - if(string.IsNullOrEmpty(entry.Key)) - return ErrorNumber.NoSuchFile; - - if(!entry.Value.Dirent.attributes.HasFlag(FatAttributes.Subdirectory)) - return ErrorNumber.NotDirectory; - - currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; - uint currentCluster = entry.Value.Dirent.start_cluster; - - if(_fat32) - currentCluster += (uint)(entry.Value.Dirent.ea_handle << 16); - - if(_directoryCache.TryGetValue(currentPath, out currentDirectory)) - continue; - - // Reserved unallocated directory, seen in Atari ST - if(currentCluster == 0) - { - _directoryCache[currentPath] = new Dictionary(); - contents = new List(); - - return ErrorNumber.NoError; - } - - uint[] clusters = GetClusters(currentCluster); - - if(clusters is null) - return ErrorNumber.InvalidArgument; - - byte[] directoryBuffer = new byte[_bytesPerCluster * clusters.Length]; - - for(int i = 0; i < clusters.Length; i++) - { - errno = _image.ReadSectors(_firstClusterSector + (clusters[i] * _sectorsPerCluster), - _sectorsPerCluster, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - Array.Copy(buffer, 0, directoryBuffer, i * _bytesPerCluster, _bytesPerCluster); - } - - currentDirectory = new Dictionary(); - byte[] lastLfnName = null; - byte lastLfnChecksum = 0; - - for(int pos = 0; pos < directoryBuffer.Length; pos += Marshal.SizeOf()) - { - DirectoryEntry dirent = - Marshal.ByteArrayToStructureLittleEndian(directoryBuffer, pos, - Marshal.SizeOf()); - - if(dirent.filename[0] == DIRENT_FINISHED) - break; - - if(dirent.attributes.HasFlag(FatAttributes.LFN)) - { - if(_namespace != Namespace.Lfn && - _namespace != Namespace.Ecs) - continue; - - LfnEntry lfnEntry = - Marshal.ByteArrayToStructureLittleEndian(directoryBuffer, pos, - Marshal.SizeOf()); - - int lfnSequence = lfnEntry.sequence & LFN_MASK; - - if((lfnEntry.sequence & LFN_ERASED) > 0) - continue; - - if((lfnEntry.sequence & LFN_LAST) > 0) - { - lastLfnName = new byte[lfnSequence * 26]; - lastLfnChecksum = lfnEntry.checksum; - } - - if(lastLfnName is null) - continue; - - if(lfnEntry.checksum != lastLfnChecksum) - continue; - - lfnSequence--; - - Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10); - Array.Copy(lfnEntry.name2, 0, lastLfnName, (lfnSequence * 26) + 10, 12); - Array.Copy(lfnEntry.name3, 0, lastLfnName, (lfnSequence * 26) + 22, 4); - - continue; - } - - // Not a correct entry - if(dirent.filename[0] < DIRENT_MIN && - dirent.filename[0] != DIRENT_E5) - continue; - - // Self - if(Encoding.GetString(dirent.filename).TrimEnd() == ".") - continue; - - // Parent - if(Encoding.GetString(dirent.filename).TrimEnd() == "..") - continue; - - // Deleted - if(dirent.filename[0] == DIRENT_DELETED) - continue; - - string filename; - - if(dirent.attributes.HasFlag(FatAttributes.VolumeLabel)) - continue; - - var completeEntry = new CompleteDirectoryEntry - { - Dirent = dirent - }; - - if((_namespace == Namespace.Lfn || _namespace == Namespace.Ecs) && - lastLfnName != null) - { - byte calculatedLfnChecksum = LfnChecksum(dirent.filename, dirent.extension); - - if(calculatedLfnChecksum == lastLfnChecksum) - { - filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true); - - completeEntry.Lfn = filename; - lastLfnName = null; - lastLfnChecksum = 0; - } - } - - if(dirent.filename[0] == DIRENT_E5) - dirent.filename[0] = DIRENT_DELETED; - - string name = Encoding.GetString(dirent.filename).TrimEnd(); - string extension = Encoding.GetString(dirent.extension).TrimEnd(); - - if(name == "" && - extension == "") - { - AaruConsole.DebugWriteLine("FAT filesystem", "Found empty filename in {0}", path); - - if(!_debug || - (dirent.size > 0 && dirent.start_cluster == 0)) - continue; // Skip invalid name - - // If debug, add it - name = ":{EMPTYNAME}:"; - - // Try to create a unique filename with an extension from 000 to 999 - for(int uniq = 0; uniq < 1000; uniq++) - { - extension = $"{uniq:D03}"; - - if(!currentDirectory.ContainsKey($"{name}.{extension}")) - break; - } - - // If we couldn't find it, just skip over - if(currentDirectory.ContainsKey($"{name}.{extension}")) - continue; - } - - if(_namespace == Namespace.Nt) - { - if(dirent.caseinfo.HasFlag(CaseInfo.LowerCaseExtension)) - extension = extension.ToLower(CultureInfo.CurrentCulture); - - if(dirent.caseinfo.HasFlag(CaseInfo.LowerCaseBasename)) - name = name.ToLower(CultureInfo.CurrentCulture); - } - - if(extension != "") - filename = name + "." + extension; - else - filename = name; - - if(_namespace == Namespace.Human) - { - HumanDirectoryEntry humanEntry = - Marshal.ByteArrayToStructureLittleEndian(directoryBuffer, pos, - Marshal.SizeOf()); - - completeEntry.HumanDirent = humanEntry; - - name = StringHandlers.CToString(humanEntry.name1, Encoding).TrimEnd(); - extension = StringHandlers.CToString(humanEntry.extension, Encoding).TrimEnd(); - string name2 = StringHandlers.CToString(humanEntry.name2, Encoding).TrimEnd(); - - if(extension != "") - filename = name + name2 + "." + extension; - else - filename = name + name2; - - completeEntry.HumanName = filename; - } - - // Atari ST allows slash AND colon so cannot simply substitute one for the other like in Mac filesystems - filename = filename.Replace('/', '\u2215'); - - // Using array accessor ensures that repeated entries just get substituted. - // Repeated entries are not allowed but some bad implementations (e.g. FAT32.IFS)allow to create them - // when using spaces - completeEntry.Shortname = filename; - currentDirectory[completeEntry.ToString()] = completeEntry; - } - - // Check OS/2 .LONGNAME - if(_eaCache != null && - (_namespace == Namespace.Os2 || _namespace == Namespace.Ecs) && - !_fat32) - { - List> filesWithEas = - currentDirectory.Where(t => t.Value.Dirent.ea_handle != 0).ToList(); - - foreach(KeyValuePair fileWithEa in filesWithEas) - { - Dictionary eas = GetEas(fileWithEa.Value.Dirent.ea_handle); - - if(eas is null) - continue; - - if(!eas.TryGetValue("com.microsoft.os2.longname", out byte[] longnameEa)) - continue; - - if(BitConverter.ToUInt16(longnameEa, 0) != EAT_ASCII) - continue; - - ushort longnameSize = BitConverter.ToUInt16(longnameEa, 2); - - if(longnameSize + 4 > longnameEa.Length) - continue; - - byte[] longnameBytes = new byte[longnameSize]; - - Array.Copy(longnameEa, 4, longnameBytes, 0, longnameSize); - - string longname = StringHandlers.CToString(longnameBytes, Encoding); - - if(string.IsNullOrWhiteSpace(longname)) - continue; - - // Forward slash is allowed in .LONGNAME, so change it to visually similar division slash - longname = longname.Replace('/', '\u2215'); - - fileWithEa.Value.Longname = longname; - currentDirectory.Remove(fileWithEa.Key); - currentDirectory[fileWithEa.Value.ToString()] = fileWithEa.Value; - } - } - - // Check FAT32.IFS EAs - if(_fat32 || _debug) - { - List> fat32EaSidecars = currentDirectory. - Where(t => t.Key.EndsWith(FAT32_EA_TAIL, true, _cultureInfo)).ToList(); - - foreach(KeyValuePair sidecar in fat32EaSidecars) - { - // No real file this sidecar accompanies - if(!currentDirectory. - TryGetValue(sidecar.Key.Substring(0, sidecar.Key.Length - FAT32_EA_TAIL.Length), - out CompleteDirectoryEntry fileWithEa)) - continue; - - // If not in debug mode we will consider the lack of EA bitflags to mean the EAs are corrupted or not real - if(!_debug) - if(!fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEaOld) && - !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa) && - !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEa) && - !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa)) - continue; - - fileWithEa.Fat32Ea = sidecar.Value.Dirent; - - if(!_debug) - currentDirectory.Remove(sidecar.Key); - } - } - - _directoryCache.Add(currentPath, currentDirectory); + return ErrorNumber.NoError; } - contents = currentDirectory?.Keys.ToList(); + uint[] clusters = GetClusters(currentCluster); - return ErrorNumber.NoError; + if(clusters is null) + return ErrorNumber.InvalidArgument; + + byte[] directoryBuffer = new byte[_bytesPerCluster * clusters.Length]; + + for(int i = 0; i < clusters.Length; i++) + { + errno = _image.ReadSectors(_firstClusterSector + (clusters[i] * _sectorsPerCluster), + _sectorsPerCluster, out byte[] buffer); + + if(errno != ErrorNumber.NoError) + return errno; + + Array.Copy(buffer, 0, directoryBuffer, i * _bytesPerCluster, _bytesPerCluster); + } + + currentDirectory = new Dictionary(); + byte[] lastLfnName = null; + byte lastLfnChecksum = 0; + + for(int pos = 0; pos < directoryBuffer.Length; pos += Marshal.SizeOf()) + { + DirectoryEntry dirent = + Marshal.ByteArrayToStructureLittleEndian(directoryBuffer, pos, + Marshal.SizeOf()); + + if(dirent.filename[0] == DIRENT_FINISHED) + break; + + if(dirent.attributes.HasFlag(FatAttributes.LFN)) + { + if(_namespace != Namespace.Lfn && + _namespace != Namespace.Ecs) + continue; + + LfnEntry lfnEntry = + Marshal.ByteArrayToStructureLittleEndian(directoryBuffer, pos, + Marshal.SizeOf()); + + int lfnSequence = lfnEntry.sequence & LFN_MASK; + + if((lfnEntry.sequence & LFN_ERASED) > 0) + continue; + + if((lfnEntry.sequence & LFN_LAST) > 0) + { + lastLfnName = new byte[lfnSequence * 26]; + lastLfnChecksum = lfnEntry.checksum; + } + + if(lastLfnName is null) + continue; + + if(lfnEntry.checksum != lastLfnChecksum) + continue; + + lfnSequence--; + + Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10); + Array.Copy(lfnEntry.name2, 0, lastLfnName, (lfnSequence * 26) + 10, 12); + Array.Copy(lfnEntry.name3, 0, lastLfnName, (lfnSequence * 26) + 22, 4); + + continue; + } + + // Not a correct entry + if(dirent.filename[0] < DIRENT_MIN && + dirent.filename[0] != DIRENT_E5) + continue; + + // Self + if(Encoding.GetString(dirent.filename).TrimEnd() == ".") + continue; + + // Parent + if(Encoding.GetString(dirent.filename).TrimEnd() == "..") + continue; + + // Deleted + if(dirent.filename[0] == DIRENT_DELETED) + continue; + + string filename; + + if(dirent.attributes.HasFlag(FatAttributes.VolumeLabel)) + continue; + + var completeEntry = new CompleteDirectoryEntry + { + Dirent = dirent + }; + + if((_namespace == Namespace.Lfn || _namespace == Namespace.Ecs) && + lastLfnName != null) + { + byte calculatedLfnChecksum = LfnChecksum(dirent.filename, dirent.extension); + + if(calculatedLfnChecksum == lastLfnChecksum) + { + filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true); + + completeEntry.Lfn = filename; + lastLfnName = null; + lastLfnChecksum = 0; + } + } + + if(dirent.filename[0] == DIRENT_E5) + dirent.filename[0] = DIRENT_DELETED; + + string name = Encoding.GetString(dirent.filename).TrimEnd(); + string extension = Encoding.GetString(dirent.extension).TrimEnd(); + + if(name == "" && + extension == "") + { + AaruConsole.DebugWriteLine("FAT filesystem", "Found empty filename in {0}", path); + + if(!_debug || + (dirent.size > 0 && dirent.start_cluster == 0)) + continue; // Skip invalid name + + // If debug, add it + name = ":{EMPTYNAME}:"; + + // Try to create a unique filename with an extension from 000 to 999 + for(int uniq = 0; uniq < 1000; uniq++) + { + extension = $"{uniq:D03}"; + + if(!currentDirectory.ContainsKey($"{name}.{extension}")) + break; + } + + // If we couldn't find it, just skip over + if(currentDirectory.ContainsKey($"{name}.{extension}")) + continue; + } + + if(_namespace == Namespace.Nt) + { + if(dirent.caseinfo.HasFlag(CaseInfo.LowerCaseExtension)) + extension = extension.ToLower(CultureInfo.CurrentCulture); + + if(dirent.caseinfo.HasFlag(CaseInfo.LowerCaseBasename)) + name = name.ToLower(CultureInfo.CurrentCulture); + } + + if(extension != "") + filename = name + "." + extension; + else + filename = name; + + if(_namespace == Namespace.Human) + { + HumanDirectoryEntry humanEntry = + Marshal.ByteArrayToStructureLittleEndian(directoryBuffer, pos, + Marshal.SizeOf()); + + completeEntry.HumanDirent = humanEntry; + + name = StringHandlers.CToString(humanEntry.name1, Encoding).TrimEnd(); + extension = StringHandlers.CToString(humanEntry.extension, Encoding).TrimEnd(); + string name2 = StringHandlers.CToString(humanEntry.name2, Encoding).TrimEnd(); + + if(extension != "") + filename = name + name2 + "." + extension; + else + filename = name + name2; + + completeEntry.HumanName = filename; + } + + // Atari ST allows slash AND colon so cannot simply substitute one for the other like in Mac filesystems + filename = filename.Replace('/', '\u2215'); + + // Using array accessor ensures that repeated entries just get substituted. + // Repeated entries are not allowed but some bad implementations (e.g. FAT32.IFS)allow to create them + // when using spaces + completeEntry.Shortname = filename; + currentDirectory[completeEntry.ToString()] = completeEntry; + } + + // Check OS/2 .LONGNAME + if(_eaCache != null && + (_namespace == Namespace.Os2 || _namespace == Namespace.Ecs) && + !_fat32) + { + List> filesWithEas = + currentDirectory.Where(t => t.Value.Dirent.ea_handle != 0).ToList(); + + foreach(KeyValuePair fileWithEa in filesWithEas) + { + Dictionary eas = GetEas(fileWithEa.Value.Dirent.ea_handle); + + if(eas is null) + continue; + + if(!eas.TryGetValue("com.microsoft.os2.longname", out byte[] longnameEa)) + continue; + + if(BitConverter.ToUInt16(longnameEa, 0) != EAT_ASCII) + continue; + + ushort longnameSize = BitConverter.ToUInt16(longnameEa, 2); + + if(longnameSize + 4 > longnameEa.Length) + continue; + + byte[] longnameBytes = new byte[longnameSize]; + + Array.Copy(longnameEa, 4, longnameBytes, 0, longnameSize); + + string longname = StringHandlers.CToString(longnameBytes, Encoding); + + if(string.IsNullOrWhiteSpace(longname)) + continue; + + // Forward slash is allowed in .LONGNAME, so change it to visually similar division slash + longname = longname.Replace('/', '\u2215'); + + fileWithEa.Value.Longname = longname; + currentDirectory.Remove(fileWithEa.Key); + currentDirectory[fileWithEa.Value.ToString()] = fileWithEa.Value; + } + } + + // Check FAT32.IFS EAs + if(_fat32 || _debug) + { + List> fat32EaSidecars = currentDirectory. + Where(t => t.Key.EndsWith(FAT32_EA_TAIL, true, _cultureInfo)).ToList(); + + foreach(KeyValuePair sidecar in fat32EaSidecars) + { + // No real file this sidecar accompanies + if(!currentDirectory. + TryGetValue(sidecar.Key.Substring(0, sidecar.Key.Length - FAT32_EA_TAIL.Length), + out CompleteDirectoryEntry fileWithEa)) + continue; + + // If not in debug mode we will consider the lack of EA bitflags to mean the EAs are corrupted or not real + if(!_debug) + if(!fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEaOld) && + !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa) && + !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEa) && + !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa)) + continue; + + fileWithEa.Fat32Ea = sidecar.Value.Dirent; + + if(!_debug) + currentDirectory.Remove(sidecar.Key); + } + } + + _directoryCache.Add(currentPath, currentDirectory); } + + contents = currentDirectory?.Keys.ToList(); + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/FAT.cs b/Aaru.Filesystems/FAT/FAT.cs index 146d03408..935fe9bec 100644 --- a/Aaru.Filesystems/FAT/FAT.cs +++ b/Aaru.Filesystems/FAT/FAT.cs @@ -38,76 +38,75 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// TODO: Differentiate between Atari and X68k FAT, as this one uses a standard BPB. +// X68K uses cdate/adate from direntry for extending filename +/// +/// Implements the File Allocation Table, aka FAT, filesystem (FAT12, FAT16 and FAT32 variants). +public sealed partial class FAT : IReadOnlyFilesystem { - // TODO: Differentiate between Atari and X68k FAT, as this one uses a standard BPB. - // X68K uses cdate/adate from direntry for extending filename + uint _bytesPerCluster; + byte[] _cachedEaData; + CultureInfo _cultureInfo; + bool _debug; + Dictionary> _directoryCache; + DirectoryEntry _eaDirEntry; + bool _fat12; + bool _fat16; + bool _fat32; + ushort[] _fatEntries; + ulong _fatFirstSector; + ulong _firstClusterSector; + bool _mounted; + Namespace _namespace; + uint _reservedSectors; + Dictionary _rootDirectoryCache; + uint _sectorsPerCluster; + uint _sectorsPerFat; + FileSystemInfo _statfs; + bool _useFirstFat; + /// - /// Implements the File Allocation Table, aka FAT, filesystem (FAT12, FAT16 and FAT32 variants). - public sealed partial class FAT : IReadOnlyFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Microsoft File Allocation Table"; + /// + public Guid Id => new("33513B2C-0D26-0D2D-32C3-79D8611158E0"); + /// + public string Author => "Natalia Portillo"; + + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + {}; + + /// + public Dictionary Namespaces => new() { - uint _bytesPerCluster; - byte[] _cachedEaData; - CultureInfo _cultureInfo; - bool _debug; - Dictionary> _directoryCache; - DirectoryEntry _eaDirEntry; - bool _fat12; - bool _fat16; - bool _fat32; - ushort[] _fatEntries; - ulong _fatFirstSector; - ulong _firstClusterSector; - bool _mounted; - Namespace _namespace; - uint _reservedSectors; - Dictionary _rootDirectoryCache; - uint _sectorsPerCluster; - uint _sectorsPerFat; - FileSystemInfo _statfs; - bool _useFirstFat; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Microsoft File Allocation Table"; - /// - public Guid Id => new("33513B2C-0D26-0D2D-32C3-79D8611158E0"); - /// - public string Author => "Natalia Portillo"; - - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - {}; - - /// - public Dictionary Namespaces => new() { - { - "dos", "DOS (8.3 all uppercase)" - }, - { - "nt", "Windows NT (8.3 mixed case)" - }, - { - "os2", "OS/2 .LONGNAME extended attribute" - }, - { - "ecs", "Use LFN when available with fallback to .LONGNAME (default)" - }, - { - "lfn", "Long file names" - } - }; - - static Dictionary GetDefaultOptions() => new() + "dos", "DOS (8.3 all uppercase)" + }, { - { - "debug", false.ToString() - } - }; - } + "nt", "Windows NT (8.3 mixed case)" + }, + { + "os2", "OS/2 .LONGNAME extended attribute" + }, + { + "ecs", "Use LFN when available with fallback to .LONGNAME (default)" + }, + { + "lfn", "Long file names" + } + }; + + static Dictionary GetDefaultOptions() => new() + { + { + "debug", false.ToString() + } + }; } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/File.cs b/Aaru.Filesystems/FAT/File.cs index 90136444b..289e35b56 100644 --- a/Aaru.Filesystems/FAT/File.cs +++ b/Aaru.Filesystems/FAT/File.cs @@ -39,317 +39,316 @@ using Aaru.CommonTypes.Structs; using Aaru.Helpers; using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class FAT { - public sealed partial class FAT + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) { - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + deviceBlock = 0; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = Stat(path, out FileEntryInfo stat); + + if(err != ErrorNumber.NoError) + return err; + + if(stat.Attributes.HasFlag(FileAttributes.Directory) && + !_debug) + return ErrorNumber.IsDirectory; + + uint[] clusters = GetClusters((uint)stat.Inode); + + if(fileBlock >= clusters.Length) + return ErrorNumber.InvalidArgument; + + deviceBlock = (long)(_firstClusterSector + (clusters[fileBlock] * _sectorsPerCluster)); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + { + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = Stat(path, out FileEntryInfo stat); + + if(err != ErrorNumber.NoError) + return err; + + attributes = stat.Attributes; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + ErrorNumber errno; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = Stat(path, out FileEntryInfo stat); + + if(err != ErrorNumber.NoError) + return err; + + if(stat.Attributes.HasFlag(FileAttributes.Directory) && + !_debug) + return ErrorNumber.IsDirectory; + + if(size == 0) { - deviceBlock = 0; + buf = Array.Empty(); - if(!_mounted) - return ErrorNumber.AccessDenied; + return ErrorNumber.NoError; + } - ErrorNumber err = Stat(path, out FileEntryInfo stat); + if(offset >= stat.Length) + return ErrorNumber.InvalidArgument; - if(err != ErrorNumber.NoError) - return err; + if(size + offset >= stat.Length) + size = stat.Length - offset; - if(stat.Attributes.HasFlag(FileAttributes.Directory) && - !_debug) - return ErrorNumber.IsDirectory; + uint[] clusters = GetClusters((uint)stat.Inode); - uint[] clusters = GetClusters((uint)stat.Inode); + if(clusters is null) + return ErrorNumber.InvalidArgument; - if(fileBlock >= clusters.Length) + long firstCluster = offset / _bytesPerCluster; + long offsetInCluster = offset % _bytesPerCluster; + long sizeInClusters = (size + offsetInCluster) / _bytesPerCluster; + + if((size + offsetInCluster) % _bytesPerCluster > 0) + sizeInClusters++; + + var ms = new MemoryStream(); + + for(int i = 0; i < sizeInClusters; i++) + { + if(i + firstCluster >= clusters.Length) return ErrorNumber.InvalidArgument; - deviceBlock = (long)(_firstClusterSector + (clusters[fileBlock] * _sectorsPerCluster)); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) - { - attributes = new FileAttributes(); - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = Stat(path, out FileEntryInfo stat); - - if(err != ErrorNumber.NoError) - return err; - - attributes = stat.Attributes; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) - { - ErrorNumber errno; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = Stat(path, out FileEntryInfo stat); - - if(err != ErrorNumber.NoError) - return err; - - if(stat.Attributes.HasFlag(FileAttributes.Directory) && - !_debug) - return ErrorNumber.IsDirectory; - - if(size == 0) - { - buf = Array.Empty(); - - return ErrorNumber.NoError; - } - - if(offset >= stat.Length) - return ErrorNumber.InvalidArgument; - - if(size + offset >= stat.Length) - size = stat.Length - offset; - - uint[] clusters = GetClusters((uint)stat.Inode); - - if(clusters is null) - return ErrorNumber.InvalidArgument; - - long firstCluster = offset / _bytesPerCluster; - long offsetInCluster = offset % _bytesPerCluster; - long sizeInClusters = (size + offsetInCluster) / _bytesPerCluster; - - if((size + offsetInCluster) % _bytesPerCluster > 0) - sizeInClusters++; - - var ms = new MemoryStream(); - - for(int i = 0; i < sizeInClusters; i++) - { - if(i + firstCluster >= clusters.Length) - return ErrorNumber.InvalidArgument; - - errno = _image.ReadSectors(_firstClusterSector + (clusters[i + firstCluster] * _sectorsPerCluster), - _sectorsPerCluster, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - ms.Write(buffer, 0, buffer.Length); - } - - ms.Position = offsetInCluster; - buf = new byte[size]; - ms.Read(buf, 0, (int)size); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) - { - stat = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = GetFileEntry(path, out CompleteDirectoryEntry completeEntry); - - if(err != ErrorNumber.NoError) - return err; - - DirectoryEntry entry = completeEntry.Dirent; - - stat = new FileEntryInfo - { - Attributes = new FileAttributes(), - Blocks = entry.size / _bytesPerCluster, - BlockSize = _bytesPerCluster, - Length = entry.size, - Inode = (ulong)(_fat32 ? (entry.ea_handle << 16) + entry.start_cluster : entry.start_cluster), - Links = 1 - }; - - if(entry.cdate > 0 || - entry.ctime > 0) - stat.CreationTime = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); - - if(_namespace != Namespace.Human) - { - if(entry.mdate > 0 || - entry.mtime > 0) - stat.LastWriteTime = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); - - if(entry.ctime_ms > 0) - stat.CreationTime = stat.CreationTime?.AddMilliseconds(entry.ctime_ms * 10); - } - - if(entry.size % _bytesPerCluster > 0) - stat.Blocks++; - - if(entry.attributes.HasFlag(FatAttributes.Subdirectory)) - { - stat.Attributes |= FileAttributes.Directory; - - if((_fat32 && entry.ea_handle << 16 > 0) || - entry.start_cluster > 0) - stat.Blocks = - _fat32 ? GetClusters((uint)((entry.ea_handle << 16) + entry.start_cluster))?.Length ?? 0 - : GetClusters(entry.start_cluster)?.Length ?? 0; - - stat.Length = stat.Blocks * stat.BlockSize; - } - - if(entry.attributes.HasFlag(FatAttributes.ReadOnly)) - stat.Attributes |= FileAttributes.ReadOnly; - - if(entry.attributes.HasFlag(FatAttributes.Hidden)) - stat.Attributes |= FileAttributes.Hidden; - - if(entry.attributes.HasFlag(FatAttributes.System)) - stat.Attributes |= FileAttributes.System; - - if(entry.attributes.HasFlag(FatAttributes.Archive)) - stat.Attributes |= FileAttributes.Archive; - - if(entry.attributes.HasFlag(FatAttributes.Device)) - stat.Attributes |= FileAttributes.Device; - - return ErrorNumber.NoError; - } - - uint[] GetClusters(uint startCluster) - { - if(startCluster == 0) - return Array.Empty(); - - if(startCluster >= XmlFsType.Clusters) - return null; - - List clusters = new(); - - uint nextCluster = startCluster; - - ulong nextSector = (nextCluster / _fatEntriesPerSector) + _fatFirstSector + - (_useFirstFat ? 0 : _sectorsPerFat); - - int nextEntry = (int)(nextCluster % _fatEntriesPerSector); - - ulong currentSector = nextSector; - ErrorNumber errno = _image.ReadSector(currentSector, out byte[] fatData); + errno = _image.ReadSectors(_firstClusterSector + (clusters[i + firstCluster] * _sectorsPerCluster), + _sectorsPerCluster, out byte[] buffer); if(errno != ErrorNumber.NoError) - return null; + return errno; - if(_fat32) - while((nextCluster & FAT32_MASK) > 0 && - (nextCluster & FAT32_MASK) <= FAT32_RESERVED) - { - clusters.Add(nextCluster); - - if(currentSector != nextSector) - { - errno = _image.ReadSector(nextSector, out fatData); - - if(errno != ErrorNumber.NoError) - return null; - - currentSector = nextSector; - } - - nextCluster = BitConverter.ToUInt32(fatData, nextEntry * 4); - - nextSector = (nextCluster / _fatEntriesPerSector) + _fatFirstSector + - (_useFirstFat ? 0 : _sectorsPerFat); - - nextEntry = (int)(nextCluster % _fatEntriesPerSector); - } - else if(_fat16) - while(nextCluster > 0 && - nextCluster <= FAT16_RESERVED) - { - if(nextCluster > _fatEntries.Length) - return null; - - clusters.Add(nextCluster); - nextCluster = _fatEntries[nextCluster]; - } - else - while(nextCluster > 0 && - nextCluster <= FAT12_RESERVED) - { - if(nextCluster > _fatEntries.Length) - return null; - - clusters.Add(nextCluster); - nextCluster = _fatEntries[nextCluster]; - } - - return clusters.ToArray(); + ms.Write(buffer, 0, buffer.Length); } - ErrorNumber GetFileEntry(string path, out CompleteDirectoryEntry entry) + ms.Position = offsetInCluster; + buf = new byte[size]; + ms.Read(buf, 0, (int)size); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = GetFileEntry(path, out CompleteDirectoryEntry completeEntry); + + if(err != ErrorNumber.NoError) + return err; + + DirectoryEntry entry = completeEntry.Dirent; + + stat = new FileEntryInfo { - entry = null; + Attributes = new FileAttributes(), + Blocks = entry.size / _bytesPerCluster, + BlockSize = _bytesPerCluster, + Length = entry.size, + Inode = (ulong)(_fat32 ? (entry.ea_handle << 16) + entry.start_cluster : entry.start_cluster), + Links = 1 + }; - string cutPath = path.StartsWith('/') ? path.Substring(1).ToLower(_cultureInfo) - : path.ToLower(_cultureInfo); + if(entry.cdate > 0 || + entry.ctime > 0) + stat.CreationTime = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); - string[] pieces = cutPath.Split(new[] + if(_namespace != Namespace.Human) + { + if(entry.mdate > 0 || + entry.mtime > 0) + stat.LastWriteTime = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); + + if(entry.ctime_ms > 0) + stat.CreationTime = stat.CreationTime?.AddMilliseconds(entry.ctime_ms * 10); + } + + if(entry.size % _bytesPerCluster > 0) + stat.Blocks++; + + if(entry.attributes.HasFlag(FatAttributes.Subdirectory)) + { + stat.Attributes |= FileAttributes.Directory; + + if((_fat32 && entry.ea_handle << 16 > 0) || + entry.start_cluster > 0) + stat.Blocks = + _fat32 ? GetClusters((uint)((entry.ea_handle << 16) + entry.start_cluster))?.Length ?? 0 + : GetClusters(entry.start_cluster)?.Length ?? 0; + + stat.Length = stat.Blocks * stat.BlockSize; + } + + if(entry.attributes.HasFlag(FatAttributes.ReadOnly)) + stat.Attributes |= FileAttributes.ReadOnly; + + if(entry.attributes.HasFlag(FatAttributes.Hidden)) + stat.Attributes |= FileAttributes.Hidden; + + if(entry.attributes.HasFlag(FatAttributes.System)) + stat.Attributes |= FileAttributes.System; + + if(entry.attributes.HasFlag(FatAttributes.Archive)) + stat.Attributes |= FileAttributes.Archive; + + if(entry.attributes.HasFlag(FatAttributes.Device)) + stat.Attributes |= FileAttributes.Device; + + return ErrorNumber.NoError; + } + + uint[] GetClusters(uint startCluster) + { + if(startCluster == 0) + return Array.Empty(); + + if(startCluster >= XmlFsType.Clusters) + return null; + + List clusters = new(); + + uint nextCluster = startCluster; + + ulong nextSector = (nextCluster / _fatEntriesPerSector) + _fatFirstSector + + (_useFirstFat ? 0 : _sectorsPerFat); + + int nextEntry = (int)(nextCluster % _fatEntriesPerSector); + + ulong currentSector = nextSector; + ErrorNumber errno = _image.ReadSector(currentSector, out byte[] fatData); + + if(errno != ErrorNumber.NoError) + return null; + + if(_fat32) + while((nextCluster & FAT32_MASK) > 0 && + (nextCluster & FAT32_MASK) <= FAT32_RESERVED) { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + clusters.Add(nextCluster); - if(pieces.Length == 0) - return ErrorNumber.InvalidArgument; + if(currentSector != nextSector) + { + errno = _image.ReadSector(nextSector, out fatData); - string parentPath = string.Join("/", pieces, 0, pieces.Length - 1); + if(errno != ErrorNumber.NoError) + return null; - if(!_directoryCache.TryGetValue(parentPath, out _)) + currentSector = nextSector; + } + + nextCluster = BitConverter.ToUInt32(fatData, nextEntry * 4); + + nextSector = (nextCluster / _fatEntriesPerSector) + _fatFirstSector + + (_useFirstFat ? 0 : _sectorsPerFat); + + nextEntry = (int)(nextCluster % _fatEntriesPerSector); + } + else if(_fat16) + while(nextCluster > 0 && + nextCluster <= FAT16_RESERVED) { - ErrorNumber err = ReadDir(parentPath, out _); + if(nextCluster > _fatEntries.Length) + return null; - if(err != ErrorNumber.NoError) - return err; + clusters.Add(nextCluster); + nextCluster = _fatEntries[nextCluster]; + } + else + while(nextCluster > 0 && + nextCluster <= FAT12_RESERVED) + { + if(nextCluster > _fatEntries.Length) + return null; + + clusters.Add(nextCluster); + nextCluster = _fatEntries[nextCluster]; } - Dictionary parent; + return clusters.ToArray(); + } - if(pieces.Length == 1) - parent = _rootDirectoryCache; - else if(!_directoryCache.TryGetValue(parentPath, out parent)) - return ErrorNumber.InvalidArgument; + ErrorNumber GetFileEntry(string path, out CompleteDirectoryEntry entry) + { + entry = null; - KeyValuePair dirent = - parent.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[^1]); + string cutPath = path.StartsWith('/') ? path.Substring(1).ToLower(_cultureInfo) + : path.ToLower(_cultureInfo); - if(string.IsNullOrEmpty(dirent.Key)) - return ErrorNumber.NoSuchFile; - - entry = dirent.Value; - - return ErrorNumber.NoError; - } - - byte LfnChecksum(byte[] name, byte[] extension) + string[] pieces = cutPath.Split(new[] { - byte sum = 0; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - for(int i = 0; i < 8; i++) - sum = (byte)(((sum & 1) << 7) + (sum >> 1) + name[i]); + if(pieces.Length == 0) + return ErrorNumber.InvalidArgument; - for(int i = 0; i < 3; i++) - sum = (byte)(((sum & 1) << 7) + (sum >> 1) + extension[i]); + string parentPath = string.Join("/", pieces, 0, pieces.Length - 1); - return sum; + if(!_directoryCache.TryGetValue(parentPath, out _)) + { + ErrorNumber err = ReadDir(parentPath, out _); + + if(err != ErrorNumber.NoError) + return err; } + + Dictionary parent; + + if(pieces.Length == 1) + parent = _rootDirectoryCache; + else if(!_directoryCache.TryGetValue(parentPath, out parent)) + return ErrorNumber.InvalidArgument; + + KeyValuePair dirent = + parent.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[^1]); + + if(string.IsNullOrEmpty(dirent.Key)) + return ErrorNumber.NoSuchFile; + + entry = dirent.Value; + + return ErrorNumber.NoError; + } + + byte LfnChecksum(byte[] name, byte[] extension) + { + byte sum = 0; + + for(int i = 0; i < 8; i++) + sum = (byte)(((sum & 1) << 7) + (sum >> 1) + name[i]); + + for(int i = 0; i < 3; i++) + sum = (byte)(((sum & 1) << 7) + (sum >> 1) + extension[i]); + + return sum; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/Info.cs b/Aaru.Filesystems/FAT/Info.cs index dbd4ea3e5..62cadc19b 100644 --- a/Aaru.Filesystems/FAT/Info.cs +++ b/Aaru.Filesystems/FAT/Info.cs @@ -45,1102 +45,1101 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class FAT { - public sealed partial class FAT + /// + [SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")] + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - [SuppressMessage("ReSharper", "JoinDeclarationAndInitializer")] - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(2 + partition.Start >= partition.End) + return false; + + ushort bps; + byte spc; + byte numberOfFats; + ushort reservedSecs; + ushort rootEntries; + ushort sectors; + byte mediaDescriptor; + ushort fatSectors; + uint bigSectors; + byte bpbSignature; + byte fat32Signature; + ulong hugeSectors; + byte[] fat32Id = new byte[8]; + byte[] msxId = new byte[6]; + byte fatId; + byte[] dosOem = new byte[8]; + byte[] atariOem = new byte[6]; + ushort bootable = 0; + + uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; + + ErrorNumber errno = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb, out byte[] bpbSector); + + if(errno != ErrorNumber.NoError) + return false; + + errno = imagePlugin.ReadSector(sectorsPerBpb + partition.Start, out byte[] fatSector); + + if(errno != ErrorNumber.NoError) + return false; + + HumanParameterBlock humanBpb = Marshal.ByteArrayToStructureBigEndian(bpbSector); + + ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; + + AaruConsole.DebugWriteLine("FAT plugin", "Human bpc = {0}", humanBpb.bpc); + AaruConsole.DebugWriteLine("FAT plugin", "Human clusters = {0}", humanBpb.clusters); + AaruConsole.DebugWriteLine("FAT plugin", "Human big_clusters = {0}", humanBpb.big_clusters); + AaruConsole.DebugWriteLine("FAT plugin", "Human expected clusters = {0}", expectedClusters); + + // Check clusters for Human68k are correct + bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters + : humanBpb.clusters == expectedClusters; + + // Check OEM for Human68k is correct + bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && + bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && + bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && + bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && + bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && + bpbSector[17] >= 0x20; + + // Check correct branch for Human68k + bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x20 && bpbSector[1] < 0xFE; + + AaruConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); + AaruConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); + AaruConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); + + // If all Human68k checks are correct, it is a Human68k FAT16 + if(humanClustersCorrect && + humanOemCorrect && + humanBranchCorrect && + expectedClusters > 0) + return true; + + Array.Copy(bpbSector, 0x02, atariOem, 0, 6); + Array.Copy(bpbSector, 0x03, dosOem, 0, 8); + bps = BitConverter.ToUInt16(bpbSector, 0x00B); + spc = bpbSector[0x00D]; + reservedSecs = BitConverter.ToUInt16(bpbSector, 0x00E); + numberOfFats = bpbSector[0x010]; + rootEntries = BitConverter.ToUInt16(bpbSector, 0x011); + sectors = BitConverter.ToUInt16(bpbSector, 0x013); + mediaDescriptor = bpbSector[0x015]; + fatSectors = BitConverter.ToUInt16(bpbSector, 0x016); + Array.Copy(bpbSector, 0x052, msxId, 0, 6); + bigSectors = BitConverter.ToUInt32(bpbSector, 0x020); + bpbSignature = bpbSector[0x026]; + fat32Signature = bpbSector[0x042]; + Array.Copy(bpbSector, 0x052, fat32Id, 0, 8); + hugeSectors = BitConverter.ToUInt64(bpbSector, 0x052); + fatId = fatSector[0]; + int bitsInBps = CountBits.Count(bps); + + if(imagePlugin.Info.SectorSize >= 512) + bootable = BitConverter.ToUInt16(bpbSector, 0x1FE); + + bool correctSpc = spc == 1 || spc == 2 || spc == 4 || spc == 8 || spc == 16 || spc == 32 || spc == 64; + string msxString = Encoding.ASCII.GetString(msxId); + string fat32String = Encoding.ASCII.GetString(fat32Id); + + bool atariOemCorrect = atariOem[0] >= 0x20 && atariOem[1] >= 0x20 && atariOem[2] >= 0x20 && + atariOem[3] >= 0x20 && atariOem[4] >= 0x20 && atariOem[5] >= 0x20; + + bool dosOemCorrect = dosOem[0] >= 0x20 && dosOem[1] >= 0x20 && dosOem[2] >= 0x20 && dosOem[3] >= 0x20 && + dosOem[4] >= 0x20 && dosOem[5] >= 0x20 && dosOem[6] >= 0x20 && dosOem[7] >= 0x20; + + string oemString = Encoding.ASCII.GetString(dosOem); + + AaruConsole.DebugWriteLine("FAT plugin", "atari_oem_correct = {0}", atariOemCorrect); + AaruConsole.DebugWriteLine("FAT plugin", "dos_oem_correct = {0}", dosOemCorrect); + AaruConsole.DebugWriteLine("FAT plugin", "bps = {0}", bps); + AaruConsole.DebugWriteLine("FAT plugin", "bits in bps = {0}", bitsInBps); + AaruConsole.DebugWriteLine("FAT plugin", "spc = {0}", spc); + AaruConsole.DebugWriteLine("FAT plugin", "correct_spc = {0}", correctSpc); + AaruConsole.DebugWriteLine("FAT plugin", "reserved_secs = {0}", reservedSecs); + AaruConsole.DebugWriteLine("FAT plugin", "fats_no = {0}", numberOfFats); + AaruConsole.DebugWriteLine("FAT plugin", "root_entries = {0}", rootEntries); + AaruConsole.DebugWriteLine("FAT plugin", "sectors = {0}", sectors); + AaruConsole.DebugWriteLine("FAT plugin", "media_descriptor = 0x{0:X2}", mediaDescriptor); + AaruConsole.DebugWriteLine("FAT plugin", "fat_sectors = {0}", fatSectors); + AaruConsole.DebugWriteLine("FAT plugin", "msx_id = \"{0}\"", msxString); + AaruConsole.DebugWriteLine("FAT plugin", "big_sectors = {0}", bigSectors); + AaruConsole.DebugWriteLine("FAT plugin", "bpb_signature = 0x{0:X2}", bpbSignature); + AaruConsole.DebugWriteLine("FAT plugin", "fat32_signature = 0x{0:X2}", fat32Signature); + AaruConsole.DebugWriteLine("FAT plugin", "fat32_id = \"{0}\"", fat32String); + AaruConsole.DebugWriteLine("FAT plugin", "huge_sectors = {0}", hugeSectors); + AaruConsole.DebugWriteLine("FAT plugin", "fat_id = 0x{0:X2}", fatId); + + ushort apricotBps = BitConverter.ToUInt16(bpbSector, 0x50); + byte apricotSpc = bpbSector[0x52]; + ushort apricotReservedSecs = BitConverter.ToUInt16(bpbSector, 0x53); + byte apricotFatsNo = bpbSector[0x55]; + ushort apricotRootEntries = BitConverter.ToUInt16(bpbSector, 0x56); + ushort apricotSectors = BitConverter.ToUInt16(bpbSector, 0x58); + byte apricotMediaDescriptor = bpbSector[0x5A]; + ushort apricotFatSectors = BitConverter.ToUInt16(bpbSector, 0x5B); + + bool apricotCorrectSpc = apricotSpc == 1 || apricotSpc == 2 || apricotSpc == 4 || apricotSpc == 8 || + apricotSpc == 16 || apricotSpc == 32 || apricotSpc == 64; + + int bitsInApricotBps = CountBits.Count(apricotBps); + byte apricotPartitions = bpbSector[0x0C]; + + AaruConsole.DebugWriteLine("FAT plugin", "apricot_bps = {0}", apricotBps); + AaruConsole.DebugWriteLine("FAT plugin", "apricot_spc = {0}", apricotSpc); + AaruConsole.DebugWriteLine("FAT plugin", "apricot_correct_spc = {0}", apricotCorrectSpc); + AaruConsole.DebugWriteLine("FAT plugin", "apricot_reserved_secs = {0}", apricotReservedSecs); + AaruConsole.DebugWriteLine("FAT plugin", "apricot_fats_no = {0}", apricotFatsNo); + AaruConsole.DebugWriteLine("FAT plugin", "apricot_root_entries = {0}", apricotRootEntries); + AaruConsole.DebugWriteLine("FAT plugin", "apricot_sectors = {0}", apricotSectors); + AaruConsole.DebugWriteLine("FAT plugin", "apricot_media_descriptor = 0x{0:X2}", apricotMediaDescriptor); + AaruConsole.DebugWriteLine("FAT plugin", "apricot_fat_sectors = {0}", apricotFatSectors); + + // This is to support FAT partitions on hybrid ISO/USB images + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - if(2 + partition.Start >= partition.End) - return false; + sectors /= 4; + bigSectors /= 4; + hugeSectors /= 4; + } - ushort bps; - byte spc; - byte numberOfFats; - ushort reservedSecs; - ushort rootEntries; - ushort sectors; - byte mediaDescriptor; - ushort fatSectors; - uint bigSectors; - byte bpbSignature; - byte fat32Signature; - ulong hugeSectors; - byte[] fat32Id = new byte[8]; - byte[] msxId = new byte[6]; - byte fatId; - byte[] dosOem = new byte[8]; - byte[] atariOem = new byte[6]; - ushort bootable = 0; + switch(oemString) + { + // exFAT + case "EXFAT ": return false; - uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; + // NTFS + case "NTFS " when bootable == 0xAA55 && numberOfFats == 0 && fatSectors == 0: return false; - ErrorNumber errno = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb, out byte[] bpbSector); + // QNX4 + case "FQNX4FS ": return false; + } + + // HPFS + if(16 + partition.Start <= partition.End) + { + errno = imagePlugin.ReadSector(16 + partition.Start, + out byte[] hpfsSbSector); // Seek to superblock, on logical sector 16 if(errno != ErrorNumber.NoError) return false; - errno = imagePlugin.ReadSector(sectorsPerBpb + partition.Start, out byte[] fatSector); + uint hpfsMagic1 = BitConverter.ToUInt32(hpfsSbSector, 0x000); + uint hpfsMagic2 = BitConverter.ToUInt32(hpfsSbSector, 0x004); + + if(hpfsMagic1 == 0xF995E849 && + hpfsMagic2 == 0xFA53E9C5) + return false; + } + + switch(bitsInBps) + { + // FAT32 for sure + case 1 when correctSpc && numberOfFats <= 2 && fatSectors == 0 && fat32Signature == 0x29 && + fat32String == "FAT32 ": return true; + + // short FAT32 + case 1 when correctSpc && numberOfFats <= 2 && fatSectors == 0 && fat32Signature == 0x28: + return sectors == 0 ? bigSectors == 0 + ? hugeSectors <= partition.End - partition.Start + 1 + : bigSectors <= partition.End - partition.Start + 1 + : sectors <= partition.End - partition.Start + 1; + + // MSX-DOS FAT12 + case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && + sectors <= partition.End - partition.Start + 1 && fatSectors > 0 && + msxString == "VOL_ID": return true; + + // EBPB + case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && fatSectors > 0 && + (bpbSignature == 0x28 || bpbSignature == 0x29): + return sectors == 0 ? bigSectors <= partition.End - partition.Start + 1 + : sectors <= partition.End - partition.Start + 1; + + // BPB + case 1 when correctSpc && reservedSecs < partition.End - partition.Start && numberOfFats <= 2 && + rootEntries > 0 && fatSectors > 0: + return sectors == 0 ? bigSectors <= partition.End - partition.Start + 1 + : sectors <= partition.End - partition.Start + 1; + } + + // Apricot BPB + if(bitsInApricotBps == 1 && + apricotCorrectSpc && + apricotReservedSecs < partition.End - partition.Start && + apricotFatsNo <= 2 && + apricotRootEntries > 0 && + apricotFatSectors > 0 && + apricotSectors <= partition.End - partition.Start + 1 && + apricotPartitions == 0) + return true; + + // All FAT12 without BPB can only be used on floppies, without partitions. + if(partition.Start != 0) + return false; + + // DEC Rainbow, lacks a BPB but has a very concrete structure... + if(imagePlugin.Info.Sectors == 800 && + imagePlugin.Info.SectorSize == 512) + { + // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) + byte z80Di = bpbSector[0]; + + // First FAT1 sector resides at LBA 0x14 + errno = imagePlugin.ReadSector(0x14, out byte[] fat1Sector0); if(errno != ErrorNumber.NoError) return false; - HumanParameterBlock humanBpb = Marshal.ByteArrayToStructureBigEndian(bpbSector); + // First FAT2 sector resides at LBA 0x1A + errno = imagePlugin.ReadSector(0x1A, out byte[] fat2Sector0); - ulong expectedClusters = humanBpb.bpc > 0 ? partition.Size / humanBpb.bpc : 0; + if(errno != ErrorNumber.NoError) + return false; - AaruConsole.DebugWriteLine("FAT plugin", "Human bpc = {0}", humanBpb.bpc); - AaruConsole.DebugWriteLine("FAT plugin", "Human clusters = {0}", humanBpb.clusters); - AaruConsole.DebugWriteLine("FAT plugin", "Human big_clusters = {0}", humanBpb.big_clusters); - AaruConsole.DebugWriteLine("FAT plugin", "Human expected clusters = {0}", expectedClusters); + bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; - // Check clusters for Human68k are correct - bool humanClustersCorrect = humanBpb.clusters == 0 ? humanBpb.big_clusters == expectedClusters - : humanBpb.clusters == expectedClusters; + // Volume is software interleaved 2:1 + var rootMs = new MemoryStream(); - // Check OEM for Human68k is correct - bool humanOemCorrect = bpbSector[2] >= 0x20 && bpbSector[3] >= 0x20 && bpbSector[4] >= 0x20 && - bpbSector[5] >= 0x20 && bpbSector[6] >= 0x20 && bpbSector[7] >= 0x20 && - bpbSector[8] >= 0x20 && bpbSector[9] >= 0x20 && bpbSector[10] >= 0x20 && - bpbSector[11] >= 0x20 && bpbSector[12] >= 0x20 && bpbSector[13] >= 0x20 && - bpbSector[14] >= 0x20 && bpbSector[15] >= 0x20 && bpbSector[16] >= 0x20 && - bpbSector[17] >= 0x20; + foreach(ulong rootSector in new ulong[] + { + 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 + }) + { + errno = imagePlugin.ReadSector(rootSector, out byte[] tmp); - // Check correct branch for Human68k - bool humanBranchCorrect = bpbSector[0] == 0x60 && bpbSector[1] >= 0x20 && bpbSector[1] < 0xFE; + if(errno != ErrorNumber.NoError) + return false; - AaruConsole.DebugWriteLine("FAT plugin", "humanClustersCorrect = {0}", humanClustersCorrect); - AaruConsole.DebugWriteLine("FAT plugin", "humanOemCorrect = {0}", humanOemCorrect); - AaruConsole.DebugWriteLine("FAT plugin", "humanBranchCorrect = {0}", humanBranchCorrect); + rootMs.Write(tmp, 0, tmp.Length); + } - // If all Human68k checks are correct, it is a Human68k FAT16 - if(humanClustersCorrect && - humanOemCorrect && - humanBranchCorrect && - expectedClusters > 0) + byte[] rootDir = rootMs.ToArray(); + bool validRootDir = true; + + // Iterate all root directory + for(int e = 0; e < 96 * 32; e += 32) + { + for(int c = 0; c < 11; c++) + if((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) || + rootDir[c + e] == 0xFF || + rootDir[c + e] == 0x2E) + { + validRootDir = false; + + break; + } + + if(!validRootDir) + break; + } + + if(z80Di == 0xF3 && + equalFatIds && + (fat1Sector0[0] & 0xF0) == 0xF0 && + fat1Sector0[1] == 0xFF && + validRootDir) return true; + } - Array.Copy(bpbSector, 0x02, atariOem, 0, 6); - Array.Copy(bpbSector, 0x03, dosOem, 0, 8); - bps = BitConverter.ToUInt16(bpbSector, 0x00B); - spc = bpbSector[0x00D]; - reservedSecs = BitConverter.ToUInt16(bpbSector, 0x00E); - numberOfFats = bpbSector[0x010]; - rootEntries = BitConverter.ToUInt16(bpbSector, 0x011); - sectors = BitConverter.ToUInt16(bpbSector, 0x013); - mediaDescriptor = bpbSector[0x015]; - fatSectors = BitConverter.ToUInt16(bpbSector, 0x016); - Array.Copy(bpbSector, 0x052, msxId, 0, 6); - bigSectors = BitConverter.ToUInt32(bpbSector, 0x020); - bpbSignature = bpbSector[0x026]; - fat32Signature = bpbSector[0x042]; - Array.Copy(bpbSector, 0x052, fat32Id, 0, 8); - hugeSectors = BitConverter.ToUInt64(bpbSector, 0x052); - fatId = fatSector[0]; - int bitsInBps = CountBits.Count(bps); + byte fat2 = fatSector[1]; + byte fat3 = fatSector[2]; + ushort fatCluster2 = (ushort)(((fat2 << 8) + fat3) & 0xFFF); - if(imagePlugin.Info.SectorSize >= 512) - bootable = BitConverter.ToUInt16(bpbSector, 0x1FE); + AaruConsole.DebugWriteLine("FAT plugin", "1st fat cluster 1 = {0:X3}", fatCluster2); - bool correctSpc = spc == 1 || spc == 2 || spc == 4 || spc == 8 || spc == 16 || spc == 32 || spc == 64; - string msxString = Encoding.ASCII.GetString(msxId); - string fat32String = Encoding.ASCII.GetString(fat32Id); + if(fatCluster2 < 0xFF0) + return false; - bool atariOemCorrect = atariOem[0] >= 0x20 && atariOem[1] >= 0x20 && atariOem[2] >= 0x20 && - atariOem[3] >= 0x20 && atariOem[4] >= 0x20 && atariOem[5] >= 0x20; + ulong fat2SectorNo = 0; - bool dosOemCorrect = dosOem[0] >= 0x20 && dosOem[1] >= 0x20 && dosOem[2] >= 0x20 && dosOem[3] >= 0x20 && - dosOem[4] >= 0x20 && dosOem[5] >= 0x20 && dosOem[6] >= 0x20 && dosOem[7] >= 0x20; + switch(fatId) + { + case 0xE5: + if(imagePlugin.Info.Sectors == 2002 && + imagePlugin.Info.SectorSize == 128) + fat2SectorNo = 2; - string oemString = Encoding.ASCII.GetString(dosOem); + break; + case 0xFD: + if(imagePlugin.Info.Sectors == 4004 && + imagePlugin.Info.SectorSize == 128) + fat2SectorNo = 7; + else if(imagePlugin.Info.Sectors == 2002 && + imagePlugin.Info.SectorSize == 128) + fat2SectorNo = 7; - AaruConsole.DebugWriteLine("FAT plugin", "atari_oem_correct = {0}", atariOemCorrect); - AaruConsole.DebugWriteLine("FAT plugin", "dos_oem_correct = {0}", dosOemCorrect); - AaruConsole.DebugWriteLine("FAT plugin", "bps = {0}", bps); - AaruConsole.DebugWriteLine("FAT plugin", "bits in bps = {0}", bitsInBps); - AaruConsole.DebugWriteLine("FAT plugin", "spc = {0}", spc); - AaruConsole.DebugWriteLine("FAT plugin", "correct_spc = {0}", correctSpc); - AaruConsole.DebugWriteLine("FAT plugin", "reserved_secs = {0}", reservedSecs); - AaruConsole.DebugWriteLine("FAT plugin", "fats_no = {0}", numberOfFats); - AaruConsole.DebugWriteLine("FAT plugin", "root_entries = {0}", rootEntries); - AaruConsole.DebugWriteLine("FAT plugin", "sectors = {0}", sectors); - AaruConsole.DebugWriteLine("FAT plugin", "media_descriptor = 0x{0:X2}", mediaDescriptor); - AaruConsole.DebugWriteLine("FAT plugin", "fat_sectors = {0}", fatSectors); - AaruConsole.DebugWriteLine("FAT plugin", "msx_id = \"{0}\"", msxString); - AaruConsole.DebugWriteLine("FAT plugin", "big_sectors = {0}", bigSectors); - AaruConsole.DebugWriteLine("FAT plugin", "bpb_signature = 0x{0:X2}", bpbSignature); - AaruConsole.DebugWriteLine("FAT plugin", "fat32_signature = 0x{0:X2}", fat32Signature); - AaruConsole.DebugWriteLine("FAT plugin", "fat32_id = \"{0}\"", fat32String); - AaruConsole.DebugWriteLine("FAT plugin", "huge_sectors = {0}", hugeSectors); - AaruConsole.DebugWriteLine("FAT plugin", "fat_id = 0x{0:X2}", fatId); + break; + case 0xFE: + if(imagePlugin.Info.Sectors == 320 && + imagePlugin.Info.SectorSize == 512) + fat2SectorNo = 2; + else if(imagePlugin.Info.Sectors == 2002 && + imagePlugin.Info.SectorSize == 128) + fat2SectorNo = 7; + else if(imagePlugin.Info.Sectors == 1232 && + imagePlugin.Info.SectorSize == 1024) + fat2SectorNo = 3; + else if(imagePlugin.Info.Sectors == 616 && + imagePlugin.Info.SectorSize == 1024) + fat2SectorNo = 2; + else if(imagePlugin.Info.Sectors == 720 && + imagePlugin.Info.SectorSize == 128) + fat2SectorNo = 5; + else if(imagePlugin.Info.Sectors == 640 && + imagePlugin.Info.SectorSize == 512) + fat2SectorNo = 2; - ushort apricotBps = BitConverter.ToUInt16(bpbSector, 0x50); - byte apricotSpc = bpbSector[0x52]; - ushort apricotReservedSecs = BitConverter.ToUInt16(bpbSector, 0x53); - byte apricotFatsNo = bpbSector[0x55]; - ushort apricotRootEntries = BitConverter.ToUInt16(bpbSector, 0x56); - ushort apricotSectors = BitConverter.ToUInt16(bpbSector, 0x58); - byte apricotMediaDescriptor = bpbSector[0x5A]; - ushort apricotFatSectors = BitConverter.ToUInt16(bpbSector, 0x5B); + break; + case 0xFF: + if(imagePlugin.Info.Sectors == 640 && + imagePlugin.Info.SectorSize == 512) + fat2SectorNo = 2; - bool apricotCorrectSpc = apricotSpc == 1 || apricotSpc == 2 || apricotSpc == 4 || apricotSpc == 8 || - apricotSpc == 16 || apricotSpc == 32 || apricotSpc == 64; + break; + default: + if(fatId < 0xE8) + return false; - int bitsInApricotBps = CountBits.Count(apricotBps); - byte apricotPartitions = bpbSector[0x0C]; + fat2SectorNo = 2; - AaruConsole.DebugWriteLine("FAT plugin", "apricot_bps = {0}", apricotBps); - AaruConsole.DebugWriteLine("FAT plugin", "apricot_spc = {0}", apricotSpc); - AaruConsole.DebugWriteLine("FAT plugin", "apricot_correct_spc = {0}", apricotCorrectSpc); - AaruConsole.DebugWriteLine("FAT plugin", "apricot_reserved_secs = {0}", apricotReservedSecs); - AaruConsole.DebugWriteLine("FAT plugin", "apricot_fats_no = {0}", apricotFatsNo); - AaruConsole.DebugWriteLine("FAT plugin", "apricot_root_entries = {0}", apricotRootEntries); - AaruConsole.DebugWriteLine("FAT plugin", "apricot_sectors = {0}", apricotSectors); - AaruConsole.DebugWriteLine("FAT plugin", "apricot_media_descriptor = 0x{0:X2}", apricotMediaDescriptor); - AaruConsole.DebugWriteLine("FAT plugin", "apricot_fat_sectors = {0}", apricotFatSectors); + break; + } + if(fat2SectorNo > partition.End || + fat2SectorNo == 0) + return false; + + AaruConsole.DebugWriteLine("FAT plugin", "2nd fat starts at = {0}", fat2SectorNo); + + errno = imagePlugin.ReadSector(fat2SectorNo, out byte[] fat2Sector); + + if(errno != ErrorNumber.NoError) + return false; + + fat2 = fat2Sector[1]; + fat3 = fat2Sector[2]; + fatCluster2 = (ushort)(((fat2 << 8) + fat3) & 0xFFF); + + if(fatCluster2 < 0xFF0) + return false; + + return fatId == fat2Sector[0]; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("IBM437"); + information = ""; + ErrorNumber errno; + + var sb = new StringBuilder(); + XmlFsType = new FileSystemType(); + + uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; + + errno = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb, out byte[] bpbSector); + + if(errno != ErrorNumber.NoError) + return; + + BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, out BiosParameterBlockEbpb fakeBpb, + out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, + out byte minBootNearJump, out bool andosOemCorrect, out bool bootable); + + bool isFat12 = false; + bool isFat16 = false; + bool isFat32 = false; + ulong rootDirectorySector = 0; + string extraInfo = null; + string bootChk = null; + XmlFsType.Bootable = bootable; + + // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field. + uint sectorsPerRealSector; + + // This is needed because some OSes don't put volume label as first entry in the root directory + uint sectorsForRootDirectory = 0; + + switch(bpbKind) + { + case BpbKind.DecRainbow: + case BpbKind.Hardcoded: + case BpbKind.Msx: + case BpbKind.Apricot: + isFat12 = true; + + break; + case BpbKind.ShortFat32: + case BpbKind.LongFat32: + { + isFat32 = true; + + Fat32ParameterBlock fat32Bpb = + Marshal.ByteArrayToStructureLittleEndian(bpbSector); + + Fat32ParameterBlockShort shortFat32Bpb = + Marshal.ByteArrayToStructureLittleEndian(bpbSector); + + // This is to support FAT partitions on hybrid ISO/USB images + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) + { + fat32Bpb.bps *= 4; + fat32Bpb.spc /= 4; + fat32Bpb.big_spfat /= 4; + fat32Bpb.hsectors /= 4; + fat32Bpb.sptrk /= 4; + } + + if(fat32Bpb.version != 0) + { + sb.AppendLine("FAT+"); + XmlFsType.Type = "FAT+"; + } + else + { + sb.AppendLine("Microsoft FAT32"); + XmlFsType.Type = "FAT32"; + } + + if(fat32Bpb.oem_name != null) + if(fat32Bpb.oem_name[5] == 0x49 && + fat32Bpb.oem_name[6] == 0x48 && + fat32Bpb.oem_name[7] == 0x43) + sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); + else + XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); + + if(!string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) + sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); + + sb.AppendFormat("{0} bytes per sector.", fat32Bpb.bps).AppendLine(); + sb.AppendFormat("{0} sectors per cluster.", fat32Bpb.spc).AppendLine(); + XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); + sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fat32Bpb.rsectors).AppendLine(); + + if(fat32Bpb.big_sectors == 0 && + fat32Bpb.signature == 0x28) + { + sb.AppendFormat("{0} sectors on volume ({1} bytes).", shortFat32Bpb.huge_sectors, + shortFat32Bpb.huge_sectors * shortFat32Bpb.bps).AppendLine(); + + XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; + } + else if(fat32Bpb.sectors == 0) + { + sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.big_sectors, + fat32Bpb.big_sectors * fat32Bpb.bps).AppendLine(); + + XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; + } + else + { + sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.sectors, + fat32Bpb.sectors * fat32Bpb.bps).AppendLine(); + + XmlFsType.Clusters = (ulong)(fat32Bpb.sectors / fat32Bpb.spc); + } + + sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); + sb.AppendFormat("Media descriptor: 0x{0:X2}", fat32Bpb.media).AppendLine(); + sb.AppendFormat("{0} sectors per FAT.", fat32Bpb.big_spfat).AppendLine(); + sb.AppendFormat("{0} sectors per track.", fat32Bpb.sptrk).AppendLine(); + sb.AppendFormat("{0} heads.", fat32Bpb.heads).AppendLine(); + sb.AppendFormat("{0} hidden sectors before BPB.", fat32Bpb.hsectors).AppendLine(); + sb.AppendFormat("Cluster of root directory: {0}", fat32Bpb.root_cluster).AppendLine(); + sb.AppendFormat("Sector of FSINFO structure: {0}", fat32Bpb.fsinfo_sector).AppendLine(); + sb.AppendFormat("Sector of backup FAT32 parameter block: {0}", fat32Bpb.backup_sector).AppendLine(); + sb.AppendFormat("Drive number: 0x{0:X2}", fat32Bpb.drive_no).AppendLine(); + sb.AppendFormat("Volume Serial Number: 0x{0:X8}", fat32Bpb.serial_no).AppendLine(); + XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; + + if((fat32Bpb.flags & 0xF8) == 0x00) + { + if((fat32Bpb.flags & 0x01) == 0x01) + { + sb.AppendLine("Volume should be checked on next mount."); + XmlFsType.Dirty = true; + } + + if((fat32Bpb.flags & 0x02) == 0x02) + sb.AppendLine("Disk surface should be on next mount."); + } + + if((fat32Bpb.mirror_flags & 0x80) == 0x80) + sb.AppendFormat("FATs are out of sync. FAT #{0} is in use.", fat32Bpb.mirror_flags & 0xF). + AppendLine(); + else + sb.AppendLine("All copies of FAT are the same."); + + if((fat32Bpb.mirror_flags & 0x6F20) == 0x6F20) + sb.AppendLine("DR-DOS will boot this FAT32 using CHS."); + else if((fat32Bpb.mirror_flags & 0x4F20) == 0x4F20) + sb.AppendLine("DR-DOS will boot this FAT32 using LBA."); + + if(fat32Bpb.signature == 0x29) + { + XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fat32Bpb.volume_label, Encoding); + XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); + + sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fat32Bpb.fs_type)). + AppendLine(); + + bootChk = Sha1Context.Data(fat32Bpb.boot_code, out _); + } + else + bootChk = Sha1Context.Data(shortFat32Bpb.boot_code, out _); + + // Check that jumps to a correct boot code position and has boot signature set. + // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... + XmlFsType.Bootable = + (fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80) || + (fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && + BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && + BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC); + + sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; + + // First root directory sector + rootDirectorySector = + (ulong)(((fat32Bpb.root_cluster - 2) * fat32Bpb.spc) + (fat32Bpb.big_spfat * fat32Bpb.fats_no) + + fat32Bpb.rsectors) * sectorsPerRealSector; + + sectorsForRootDirectory = 1; + + if(fat32Bpb.fsinfo_sector + partition.Start <= partition.End) + { + errno = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start, + out byte[] fsinfoSector); + + if(errno != ErrorNumber.NoError) + return; + + FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian(fsinfoSector); + + if(fsInfo.signature1 == FSINFO_SIGNATURE1 && + fsInfo.signature2 == FSINFO_SIGNATURE2 && + fsInfo.signature3 == FSINFO_SIGNATURE3) + { + if(fsInfo.free_clusters < 0xFFFFFFFF) + { + sb.AppendFormat("{0} free clusters", fsInfo.free_clusters).AppendLine(); + XmlFsType.FreeClusters = fsInfo.free_clusters; + XmlFsType.FreeClustersSpecified = true; + } + + if(fsInfo.last_cluster > 2 && + fsInfo.last_cluster < 0xFFFFFFFF) + sb.AppendFormat("Last allocated cluster {0}", fsInfo.last_cluster).AppendLine(); + } + } + + break; + } + + // Some fields could overflow fake BPB, those will be handled below + case BpbKind.Atari: + { + ushort sum = 0; + + for(int i = 0; i < bpbSector.Length; i += 2) + sum += BigEndianBitConverter.ToUInt16(bpbSector, i); + + // TODO: Check this + if(sum == 0x1234) + { + XmlFsType.Bootable = true; + var atariSb = new StringBuilder(); + + atariSb.AppendFormat("cmdload will be loaded with value {0:X4}h", + BigEndianBitConverter.ToUInt16(bpbSector, 0x01E)).AppendLine(); + + atariSb.AppendFormat("Boot program will be loaded at address {0:X4}h", atariBpb.ldaaddr). + AppendLine(); + + atariSb.AppendFormat("FAT and directory will be cached at address {0:X4}h", atariBpb.fatbuf). + AppendLine(); + + if(atariBpb.ldmode == 0) + { + byte[] tmp = new byte[8]; + Array.Copy(atariBpb.fname, 0, tmp, 0, 8); + string fname = Encoding.ASCII.GetString(tmp).Trim(); + tmp = new byte[3]; + Array.Copy(atariBpb.fname, 8, tmp, 0, 3); + string extension = Encoding.ASCII.GetString(tmp).Trim(); + string filename; + + if(string.IsNullOrEmpty(extension)) + filename = fname; + else + filename = fname + "." + extension; + + atariSb.AppendFormat("Boot program resides in file \"{0}\"", filename).AppendLine(); + } + else + atariSb. + AppendFormat("Boot program starts in sector {0} and is {1} sectors long ({2} bytes)", + atariBpb.ssect, atariBpb.sectcnt, atariBpb.sectcnt * atariBpb.bps). + AppendLine(); + + extraInfo = atariSb.ToString(); + } + + break; + } + + case BpbKind.Human: + XmlFsType.Bootable = true; + + break; + } + + if(!isFat32) + { // This is to support FAT partitions on hybrid ISO/USB images if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - sectors /= 4; - bigSectors /= 4; - hugeSectors /= 4; + fakeBpb.bps *= 4; + fakeBpb.spc /= 4; + fakeBpb.spfat /= 4; + fakeBpb.hsectors /= 4; + fakeBpb.sptrk /= 4; + fakeBpb.rsectors /= 4; + + if(fakeBpb.spc == 0) + fakeBpb.spc = 1; } - switch(oemString) + ulong clusters; + + if(bpbKind != BpbKind.Human) { - // exFAT - case "EXFAT ": return false; + int reservedSectors = fakeBpb.rsectors + (fakeBpb.fats_no * fakeBpb.spfat) + + (fakeBpb.root_ent * 32 / fakeBpb.bps); - // NTFS - case "NTFS " when bootable == 0xAA55 && numberOfFats == 0 && fatSectors == 0: return false; - - // QNX4 - case "FQNX4FS ": return false; + if(fakeBpb.sectors == 0) + clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.big_sectors - reservedSectors + : (fakeBpb.big_sectors - reservedSectors) / fakeBpb.spc); + else + clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors - reservedSectors + : (fakeBpb.sectors - reservedSectors) / fakeBpb.spc); } + else + clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters; - // HPFS - if(16 + partition.Start <= partition.End) + // This will walk all the FAT entries and check if they're valid FAT12 or FAT16 entries. + // If the whole table is valid in both senses, it considers the type entry in the BPB. + // BeOS is known to set the type as FAT16 but treat it as FAT12. + if(!isFat12 && + !isFat16) { - errno = imagePlugin.ReadSector(16 + partition.Start, - out byte[] hpfsSbSector); // Seek to superblock, on logical sector 16 - - if(errno != ErrorNumber.NoError) - return false; - - uint hpfsMagic1 = BitConverter.ToUInt32(hpfsSbSector, 0x000); - uint hpfsMagic2 = BitConverter.ToUInt32(hpfsSbSector, 0x004); - - if(hpfsMagic1 == 0xF995E849 && - hpfsMagic2 == 0xFA53E9C5) - return false; - } - - switch(bitsInBps) - { - // FAT32 for sure - case 1 when correctSpc && numberOfFats <= 2 && fatSectors == 0 && fat32Signature == 0x29 && - fat32String == "FAT32 ": return true; - - // short FAT32 - case 1 when correctSpc && numberOfFats <= 2 && fatSectors == 0 && fat32Signature == 0x28: - return sectors == 0 ? bigSectors == 0 - ? hugeSectors <= partition.End - partition.Start + 1 - : bigSectors <= partition.End - partition.Start + 1 - : sectors <= partition.End - partition.Start + 1; - - // MSX-DOS FAT12 - case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && - sectors <= partition.End - partition.Start + 1 && fatSectors > 0 && - msxString == "VOL_ID": return true; - - // EBPB - case 1 when correctSpc && numberOfFats <= 2 && rootEntries > 0 && fatSectors > 0 && - (bpbSignature == 0x28 || bpbSignature == 0x29): - return sectors == 0 ? bigSectors <= partition.End - partition.Start + 1 - : sectors <= partition.End - partition.Start + 1; - - // BPB - case 1 when correctSpc && reservedSecs < partition.End - partition.Start && numberOfFats <= 2 && - rootEntries > 0 && fatSectors > 0: - return sectors == 0 ? bigSectors <= partition.End - partition.Start + 1 - : sectors <= partition.End - partition.Start + 1; - } - - // Apricot BPB - if(bitsInApricotBps == 1 && - apricotCorrectSpc && - apricotReservedSecs < partition.End - partition.Start && - apricotFatsNo <= 2 && - apricotRootEntries > 0 && - apricotFatSectors > 0 && - apricotSectors <= partition.End - partition.Start + 1 && - apricotPartitions == 0) - return true; - - // All FAT12 without BPB can only be used on floppies, without partitions. - if(partition.Start != 0) - return false; - - // DEC Rainbow, lacks a BPB but has a very concrete structure... - if(imagePlugin.Info.Sectors == 800 && - imagePlugin.Info.SectorSize == 512) - { - // DEC Rainbow boots up with a Z80, first byte should be DI (disable interrupts) - byte z80Di = bpbSector[0]; - - // First FAT1 sector resides at LBA 0x14 - errno = imagePlugin.ReadSector(0x14, out byte[] fat1Sector0); - - if(errno != ErrorNumber.NoError) - return false; - - // First FAT2 sector resides at LBA 0x1A - errno = imagePlugin.ReadSector(0x1A, out byte[] fat2Sector0); - - if(errno != ErrorNumber.NoError) - return false; - - bool equalFatIds = fat1Sector0[0] == fat2Sector0[0] && fat1Sector0[1] == fat2Sector0[1]; - - // Volume is software interleaved 2:1 - var rootMs = new MemoryStream(); - - foreach(ulong rootSector in new ulong[] + if(clusters < 4089) { - 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 - }) - { - errno = imagePlugin.ReadSector(rootSector, out byte[] tmp); + ushort[] fat12 = new ushort[clusters]; + + _reservedSectors = fakeBpb.rsectors; + sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; + _fatFirstSector = partition.Start + (_reservedSectors * sectorsPerRealSector); + + errno = imagePlugin.ReadSectors(_fatFirstSector, fakeBpb.spfat, out byte[] fatBytes); if(errno != ErrorNumber.NoError) - return false; + return; - rootMs.Write(tmp, 0, tmp.Length); - } + int pos = 0; - byte[] rootDir = rootMs.ToArray(); - bool validRootDir = true; - - // Iterate all root directory - for(int e = 0; e < 96 * 32; e += 32) - { - for(int c = 0; c < 11; c++) - if((rootDir[c + e] < 0x20 && rootDir[c + e] != 0x00 && rootDir[c + e] != 0x05) || - rootDir[c + e] == 0xFF || - rootDir[c + e] == 0x2E) - { - validRootDir = false; + for(int i = 0; i + 3 < fatBytes.Length && pos < fat12.Length; i += 3) + { + fat12[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); + if(pos >= fat12.Length) break; - } - if(!validRootDir) + fat12[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); + } + + bool fat12Valid = fat12[0] >= FAT12_RESERVED && fat12[1] >= FAT12_RESERVED; + + foreach(ushort entry in fat12) + { + if(entry >= FAT12_RESERVED || + entry <= clusters) + continue; + + fat12Valid = false; + + break; + } + + ushort[] fat16 = MemoryMarshal.Cast(fatBytes).ToArray(); + + bool fat16Valid = fat16[0] >= FAT16_RESERVED && fat16[1] >= 0x3FF0; + + foreach(ushort entry in fat16) + { + if(entry >= FAT16_RESERVED || + entry <= clusters) + continue; + + fat16Valid = false; + + break; + } + + isFat12 = fat12Valid; + isFat16 = fat16Valid; + + // Check BPB type + if(isFat12 == isFat16) + { + isFat12 = fakeBpb.fs_type != null && + Encoding.ASCII.GetString(fakeBpb.fs_type) == "FAT12 "; + + isFat16 = fakeBpb.fs_type != null && + Encoding.ASCII.GetString(fakeBpb.fs_type) == "FAT16 "; + } + + if(!isFat12 && + !isFat16) + isFat12 = true; + } + else + isFat16 = true; + } + + if(isFat12) + { + switch(bpbKind) + { + case BpbKind.Atari: + sb.AppendLine("Atari FAT12"); + + break; + case BpbKind.Apricot: + sb.AppendLine("Apricot FAT12"); + + break; + case BpbKind.Human: + sb.AppendLine("Human68k FAT12"); + + break; + default: + sb.AppendLine("Microsoft FAT12"); + break; } - if(z80Di == 0xF3 && - equalFatIds && - (fat1Sector0[0] & 0xF0) == 0xF0 && - fat1Sector0[1] == 0xFF && - validRootDir) - return true; + XmlFsType.Type = "FAT12"; } - - byte fat2 = fatSector[1]; - byte fat3 = fatSector[2]; - ushort fatCluster2 = (ushort)(((fat2 << 8) + fat3) & 0xFFF); - - AaruConsole.DebugWriteLine("FAT plugin", "1st fat cluster 1 = {0:X3}", fatCluster2); - - if(fatCluster2 < 0xFF0) - return false; - - ulong fat2SectorNo = 0; - - switch(fatId) + else if(isFat16) { - case 0xE5: - if(imagePlugin.Info.Sectors == 2002 && - imagePlugin.Info.SectorSize == 128) - fat2SectorNo = 2; + sb.AppendLine(bpbKind == BpbKind.Atari + ? "Atari FAT16" + : bpbKind == BpbKind.Human + ? "Human68k FAT16" + : "Microsoft FAT16"); - break; - case 0xFD: - if(imagePlugin.Info.Sectors == 4004 && - imagePlugin.Info.SectorSize == 128) - fat2SectorNo = 7; - else if(imagePlugin.Info.Sectors == 2002 && - imagePlugin.Info.SectorSize == 128) - fat2SectorNo = 7; - - break; - case 0xFE: - if(imagePlugin.Info.Sectors == 320 && - imagePlugin.Info.SectorSize == 512) - fat2SectorNo = 2; - else if(imagePlugin.Info.Sectors == 2002 && - imagePlugin.Info.SectorSize == 128) - fat2SectorNo = 7; - else if(imagePlugin.Info.Sectors == 1232 && - imagePlugin.Info.SectorSize == 1024) - fat2SectorNo = 3; - else if(imagePlugin.Info.Sectors == 616 && - imagePlugin.Info.SectorSize == 1024) - fat2SectorNo = 2; - else if(imagePlugin.Info.Sectors == 720 && - imagePlugin.Info.SectorSize == 128) - fat2SectorNo = 5; - else if(imagePlugin.Info.Sectors == 640 && - imagePlugin.Info.SectorSize == 512) - fat2SectorNo = 2; - - break; - case 0xFF: - if(imagePlugin.Info.Sectors == 640 && - imagePlugin.Info.SectorSize == 512) - fat2SectorNo = 2; - - break; - default: - if(fatId < 0xE8) - return false; - - fat2SectorNo = 2; - - break; + XmlFsType.Type = "FAT16"; } - if(fat2SectorNo > partition.End || - fat2SectorNo == 0) - return false; + if(bpbKind == BpbKind.Atari) + { + if(atariBpb.serial_no[0] == 0x49 && + atariBpb.serial_no[1] == 0x48 && + atariBpb.serial_no[2] == 0x43) + sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); + else + XmlFsType.VolumeSerial = + $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}"; - AaruConsole.DebugWriteLine("FAT plugin", "2nd fat starts at = {0}", fat2SectorNo); + XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name); - errno = imagePlugin.ReadSector(fat2SectorNo, out byte[] fat2Sector); + if(string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) + XmlFsType.SystemIdentifier = null; + } + else if(fakeBpb.oem_name != null) + { + if(fakeBpb.oem_name[5] == 0x49 && + fakeBpb.oem_name[6] == 0x48 && + fakeBpb.oem_name[7] == 0x43) + sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); + else + { + // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies + // OEM ID should be ASCII, otherwise ignore it + if(fakeBpb.oem_name[0] >= 0x20 && + fakeBpb.oem_name[0] <= 0x7F && + fakeBpb.oem_name[1] >= 0x20 && + fakeBpb.oem_name[1] <= 0x7F && + fakeBpb.oem_name[2] >= 0x20 && + fakeBpb.oem_name[2] <= 0x7F && + fakeBpb.oem_name[3] >= 0x20 && + fakeBpb.oem_name[3] <= 0x7F && + fakeBpb.oem_name[4] >= 0x20 && + fakeBpb.oem_name[4] <= 0x7F && + fakeBpb.oem_name[5] >= 0x20 && + fakeBpb.oem_name[5] <= 0x7F && + fakeBpb.oem_name[6] >= 0x20 && + fakeBpb.oem_name[6] <= 0x7F && + fakeBpb.oem_name[7] >= 0x20 && + fakeBpb.oem_name[7] <= 0x7F) + XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name); + else if(fakeBpb.oem_name[0] < 0x20 && + fakeBpb.oem_name[1] >= 0x20 && + fakeBpb.oem_name[1] <= 0x7F && + fakeBpb.oem_name[2] >= 0x20 && + fakeBpb.oem_name[2] <= 0x7F && + fakeBpb.oem_name[3] >= 0x20 && + fakeBpb.oem_name[3] <= 0x7F && + fakeBpb.oem_name[4] >= 0x20 && + fakeBpb.oem_name[4] <= 0x7F && + fakeBpb.oem_name[5] >= 0x20 && + fakeBpb.oem_name[5] <= 0x7F && + fakeBpb.oem_name[6] >= 0x20 && + fakeBpb.oem_name[6] <= 0x7F && + fakeBpb.oem_name[7] >= 0x20 && + fakeBpb.oem_name[7] <= 0x7F) + XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1); + } - if(errno != ErrorNumber.NoError) - return false; + if(fakeBpb.signature == 0x28 || + fakeBpb.signature == 0x29) + XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}"; + } - fat2 = fat2Sector[1]; - fat3 = fat2Sector[2]; - fatCluster2 = (ushort)(((fat2 << 8) + fat3) & 0xFFF); + if(XmlFsType.SystemIdentifier != null) + sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); - if(fatCluster2 < 0xFF0) - return false; + sb.AppendFormat("{0} bytes per sector.", fakeBpb.bps).AppendLine(); - return fatId == fat2Sector[0]; + if(bpbKind != BpbKind.Human) + if(fakeBpb.sectors == 0) + sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.big_sectors, + fakeBpb.big_sectors * fakeBpb.bps).AppendLine(); + else + sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.sectors, + fakeBpb.sectors * fakeBpb.bps).AppendLine(); + else + sb.AppendFormat("{0} sectors on volume ({1} bytes).", + clusters * humanBpb.bpc / imagePlugin.Info.SectorSize, clusters * humanBpb.bpc). + AppendLine(); + + XmlFsType.Clusters = clusters; + sb.AppendFormat("{0} sectors per cluster.", fakeBpb.spc).AppendLine(); + sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); + XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc); + sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fakeBpb.rsectors).AppendLine(); + sb.AppendFormat("{0} FATs.", fakeBpb.fats_no).AppendLine(); + sb.AppendFormat("{0} entries on root directory.", fakeBpb.root_ent).AppendLine(); + + if(fakeBpb.media > 0) + sb.AppendFormat("Media descriptor: 0x{0:X2}", fakeBpb.media).AppendLine(); + + sb.AppendFormat("{0} sectors per FAT.", fakeBpb.spfat).AppendLine(); + + if(fakeBpb.sptrk > 0 && + fakeBpb.sptrk < 64 && + fakeBpb.heads > 0 && + fakeBpb.heads < 256) + { + sb.AppendFormat("{0} sectors per track.", fakeBpb.sptrk).AppendLine(); + sb.AppendFormat("{0} heads.", fakeBpb.heads).AppendLine(); + } + + if(fakeBpb.hsectors <= partition.Start) + sb.AppendFormat("{0} hidden sectors before BPB.", fakeBpb.hsectors).AppendLine(); + + if(fakeBpb.signature == 0x28 || + fakeBpb.signature == 0x29 || + andosOemCorrect) + { + sb.AppendFormat("Drive number: 0x{0:X2}", fakeBpb.drive_no).AppendLine(); + + if(XmlFsType.VolumeSerial != null) + sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); + + if((fakeBpb.flags & 0xF8) == 0x00) + { + if((fakeBpb.flags & 0x01) == 0x01) + { + sb.AppendLine("Volume should be checked on next mount."); + XmlFsType.Dirty = true; + } + + if((fakeBpb.flags & 0x02) == 0x02) + sb.AppendLine("Disk surface should be on next mount."); + } + + if(fakeBpb.signature == 0x29 || andosOemCorrect) + { + XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fakeBpb.volume_label, Encoding); + XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); + sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fakeBpb.fs_type)).AppendLine(); + } + } + else if(bpbKind == BpbKind.Atari && + XmlFsType.VolumeSerial != null) + sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); + + bootChk = Sha1Context.Data(fakeBpb.boot_code, out _); + + // Workaround that PCExchange jumps into "FAT16 "... + if(XmlFsType.SystemIdentifier == "PCX 2.0 ") + fakeBpb.jump[1] += 8; + + // Check that jumps to a correct boot code position and has boot signature set. + // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... + if(XmlFsType.Bootable == false && + fakeBpb.jump != null) + XmlFsType.Bootable |= + (fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80) || + (fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 && + BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump && + BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC); + + sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; + + // First root directory sector + rootDirectorySector = + (ulong)((fakeBpb.spfat * fakeBpb.fats_no) + fakeBpb.rsectors) * sectorsPerRealSector; + + sectorsForRootDirectory = (uint)(fakeBpb.root_ent * 32 / imagePlugin.Info.SectorSize); } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + if(extraInfo != null) + sb.Append(extraInfo); + + if(rootDirectorySector + partition.Start < partition.End && + imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) { - Encoding = encoding ?? Encoding.GetEncoding("IBM437"); - information = ""; - ErrorNumber errno; - - var sb = new StringBuilder(); - XmlFsType = new FileSystemType(); - - uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; - - errno = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb, out byte[] bpbSector); + errno = imagePlugin.ReadSectors(rootDirectorySector + partition.Start, sectorsForRootDirectory, + out byte[] rootDirectory); if(errno != ErrorNumber.NoError) return; - BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, out BiosParameterBlockEbpb fakeBpb, - out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, - out byte minBootNearJump, out bool andosOemCorrect, out bool bootable); - - bool isFat12 = false; - bool isFat16 = false; - bool isFat32 = false; - ulong rootDirectorySector = 0; - string extraInfo = null; - string bootChk = null; - XmlFsType.Bootable = bootable; - - // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field. - uint sectorsPerRealSector; - - // This is needed because some OSes don't put volume label as first entry in the root directory - uint sectorsForRootDirectory = 0; - - switch(bpbKind) + if(bpbKind == BpbKind.DecRainbow) { - case BpbKind.DecRainbow: - case BpbKind.Hardcoded: - case BpbKind.Msx: - case BpbKind.Apricot: - isFat12 = true; + var rootMs = new MemoryStream(); - break; - case BpbKind.ShortFat32: - case BpbKind.LongFat32: + foreach(ulong rootSector in new[] + { + 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 + }) { - isFat32 = true; + errno = imagePlugin.ReadSector(rootSector, out byte[] tmp); - Fat32ParameterBlock fat32Bpb = - Marshal.ByteArrayToStructureLittleEndian(bpbSector); + if(errno != ErrorNumber.NoError) + return; - Fat32ParameterBlockShort shortFat32Bpb = - Marshal.ByteArrayToStructureLittleEndian(bpbSector); - - // This is to support FAT partitions on hybrid ISO/USB images - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - fat32Bpb.bps *= 4; - fat32Bpb.spc /= 4; - fat32Bpb.big_spfat /= 4; - fat32Bpb.hsectors /= 4; - fat32Bpb.sptrk /= 4; - } - - if(fat32Bpb.version != 0) - { - sb.AppendLine("FAT+"); - XmlFsType.Type = "FAT+"; - } - else - { - sb.AppendLine("Microsoft FAT32"); - XmlFsType.Type = "FAT32"; - } - - if(fat32Bpb.oem_name != null) - if(fat32Bpb.oem_name[5] == 0x49 && - fat32Bpb.oem_name[6] == 0x48 && - fat32Bpb.oem_name[7] == 0x43) - sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); - else - XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); - - if(!string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) - sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); - - sb.AppendFormat("{0} bytes per sector.", fat32Bpb.bps).AppendLine(); - sb.AppendFormat("{0} sectors per cluster.", fat32Bpb.spc).AppendLine(); - XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); - sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fat32Bpb.rsectors).AppendLine(); - - if(fat32Bpb.big_sectors == 0 && - fat32Bpb.signature == 0x28) - { - sb.AppendFormat("{0} sectors on volume ({1} bytes).", shortFat32Bpb.huge_sectors, - shortFat32Bpb.huge_sectors * shortFat32Bpb.bps).AppendLine(); - - XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; - } - else if(fat32Bpb.sectors == 0) - { - sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.big_sectors, - fat32Bpb.big_sectors * fat32Bpb.bps).AppendLine(); - - XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; - } - else - { - sb.AppendFormat("{0} sectors on volume ({1} bytes).", fat32Bpb.sectors, - fat32Bpb.sectors * fat32Bpb.bps).AppendLine(); - - XmlFsType.Clusters = (ulong)(fat32Bpb.sectors / fat32Bpb.spc); - } - - sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); - sb.AppendFormat("Media descriptor: 0x{0:X2}", fat32Bpb.media).AppendLine(); - sb.AppendFormat("{0} sectors per FAT.", fat32Bpb.big_spfat).AppendLine(); - sb.AppendFormat("{0} sectors per track.", fat32Bpb.sptrk).AppendLine(); - sb.AppendFormat("{0} heads.", fat32Bpb.heads).AppendLine(); - sb.AppendFormat("{0} hidden sectors before BPB.", fat32Bpb.hsectors).AppendLine(); - sb.AppendFormat("Cluster of root directory: {0}", fat32Bpb.root_cluster).AppendLine(); - sb.AppendFormat("Sector of FSINFO structure: {0}", fat32Bpb.fsinfo_sector).AppendLine(); - sb.AppendFormat("Sector of backup FAT32 parameter block: {0}", fat32Bpb.backup_sector).AppendLine(); - sb.AppendFormat("Drive number: 0x{0:X2}", fat32Bpb.drive_no).AppendLine(); - sb.AppendFormat("Volume Serial Number: 0x{0:X8}", fat32Bpb.serial_no).AppendLine(); - XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; - - if((fat32Bpb.flags & 0xF8) == 0x00) - { - if((fat32Bpb.flags & 0x01) == 0x01) - { - sb.AppendLine("Volume should be checked on next mount."); - XmlFsType.Dirty = true; - } - - if((fat32Bpb.flags & 0x02) == 0x02) - sb.AppendLine("Disk surface should be on next mount."); - } - - if((fat32Bpb.mirror_flags & 0x80) == 0x80) - sb.AppendFormat("FATs are out of sync. FAT #{0} is in use.", fat32Bpb.mirror_flags & 0xF). - AppendLine(); - else - sb.AppendLine("All copies of FAT are the same."); - - if((fat32Bpb.mirror_flags & 0x6F20) == 0x6F20) - sb.AppendLine("DR-DOS will boot this FAT32 using CHS."); - else if((fat32Bpb.mirror_flags & 0x4F20) == 0x4F20) - sb.AppendLine("DR-DOS will boot this FAT32 using LBA."); - - if(fat32Bpb.signature == 0x29) - { - XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fat32Bpb.volume_label, Encoding); - XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); - - sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fat32Bpb.fs_type)). - AppendLine(); - - bootChk = Sha1Context.Data(fat32Bpb.boot_code, out _); - } - else - bootChk = Sha1Context.Data(shortFat32Bpb.boot_code, out _); - - // Check that jumps to a correct boot code position and has boot signature set. - // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... - XmlFsType.Bootable = - (fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80) || - (fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && - BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && - BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC); - - sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; - - // First root directory sector - rootDirectorySector = - (ulong)(((fat32Bpb.root_cluster - 2) * fat32Bpb.spc) + (fat32Bpb.big_spfat * fat32Bpb.fats_no) + - fat32Bpb.rsectors) * sectorsPerRealSector; - - sectorsForRootDirectory = 1; - - if(fat32Bpb.fsinfo_sector + partition.Start <= partition.End) - { - errno = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start, - out byte[] fsinfoSector); - - if(errno != ErrorNumber.NoError) - return; - - FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian(fsinfoSector); - - if(fsInfo.signature1 == FSINFO_SIGNATURE1 && - fsInfo.signature2 == FSINFO_SIGNATURE2 && - fsInfo.signature3 == FSINFO_SIGNATURE3) - { - if(fsInfo.free_clusters < 0xFFFFFFFF) - { - sb.AppendFormat("{0} free clusters", fsInfo.free_clusters).AppendLine(); - XmlFsType.FreeClusters = fsInfo.free_clusters; - XmlFsType.FreeClustersSpecified = true; - } - - if(fsInfo.last_cluster > 2 && - fsInfo.last_cluster < 0xFFFFFFFF) - sb.AppendFormat("Last allocated cluster {0}", fsInfo.last_cluster).AppendLine(); - } - } - - break; + rootMs.Write(tmp, 0, tmp.Length); } - // Some fields could overflow fake BPB, those will be handled below - case BpbKind.Atari: - { - ushort sum = 0; - - for(int i = 0; i < bpbSector.Length; i += 2) - sum += BigEndianBitConverter.ToUInt16(bpbSector, i); - - // TODO: Check this - if(sum == 0x1234) - { - XmlFsType.Bootable = true; - var atariSb = new StringBuilder(); - - atariSb.AppendFormat("cmdload will be loaded with value {0:X4}h", - BigEndianBitConverter.ToUInt16(bpbSector, 0x01E)).AppendLine(); - - atariSb.AppendFormat("Boot program will be loaded at address {0:X4}h", atariBpb.ldaaddr). - AppendLine(); - - atariSb.AppendFormat("FAT and directory will be cached at address {0:X4}h", atariBpb.fatbuf). - AppendLine(); - - if(atariBpb.ldmode == 0) - { - byte[] tmp = new byte[8]; - Array.Copy(atariBpb.fname, 0, tmp, 0, 8); - string fname = Encoding.ASCII.GetString(tmp).Trim(); - tmp = new byte[3]; - Array.Copy(atariBpb.fname, 8, tmp, 0, 3); - string extension = Encoding.ASCII.GetString(tmp).Trim(); - string filename; - - if(string.IsNullOrEmpty(extension)) - filename = fname; - else - filename = fname + "." + extension; - - atariSb.AppendFormat("Boot program resides in file \"{0}\"", filename).AppendLine(); - } - else - atariSb. - AppendFormat("Boot program starts in sector {0} and is {1} sectors long ({2} bytes)", - atariBpb.ssect, atariBpb.sectcnt, atariBpb.sectcnt * atariBpb.bps). - AppendLine(); - - extraInfo = atariSb.ToString(); - } - - break; - } - - case BpbKind.Human: - XmlFsType.Bootable = true; - - break; + rootDirectory = rootMs.ToArray(); } - if(!isFat32) + for(int i = 0; i < rootDirectory.Length; i += 32) { - // This is to support FAT partitions on hybrid ISO/USB images - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - fakeBpb.bps *= 4; - fakeBpb.spc /= 4; - fakeBpb.spfat /= 4; - fakeBpb.hsectors /= 4; - fakeBpb.sptrk /= 4; - fakeBpb.rsectors /= 4; + // Not a correct entry + if(rootDirectory[i] < DIRENT_MIN && + rootDirectory[i] != DIRENT_E5) + continue; - if(fakeBpb.spc == 0) - fakeBpb.spc = 1; + // Deleted or subdirectory entry + if(rootDirectory[i] == DIRENT_SUBDIR || + rootDirectory[i] == DIRENT_DELETED) + continue; + + // Not a volume label + if(rootDirectory[i + 0x0B] != 0x08 && + rootDirectory[i + 0x0B] != 0x28) + continue; + + DirectoryEntry entry = + Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, 32); + + byte[] fullname = new byte[11]; + Array.Copy(entry.filename, 0, fullname, 0, 8); + Array.Copy(entry.extension, 0, fullname, 8, 3); + string volname = Encoding.GetString(fullname).Trim(); + + if(!string.IsNullOrEmpty(volname)) + XmlFsType.VolumeName = + entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) ? volname.ToLower() : volname; + + if(entry.ctime > 0 && + entry.cdate > 0) + { + XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); + + if(entry.ctime_ms > 0) + XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); + + XmlFsType.CreationDateSpecified = true; + sb.AppendFormat("Volume created on {0}", XmlFsType.CreationDate).AppendLine(); } - ulong clusters; - - if(bpbKind != BpbKind.Human) + if(entry.mtime > 0 && + entry.mdate > 0) { - int reservedSectors = fakeBpb.rsectors + (fakeBpb.fats_no * fakeBpb.spfat) + - (fakeBpb.root_ent * 32 / fakeBpb.bps); - - if(fakeBpb.sectors == 0) - clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.big_sectors - reservedSectors - : (fakeBpb.big_sectors - reservedSectors) / fakeBpb.spc); - else - clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors - reservedSectors - : (fakeBpb.sectors - reservedSectors) / fakeBpb.spc); - } - else - clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters; - - // This will walk all the FAT entries and check if they're valid FAT12 or FAT16 entries. - // If the whole table is valid in both senses, it considers the type entry in the BPB. - // BeOS is known to set the type as FAT16 but treat it as FAT12. - if(!isFat12 && - !isFat16) - { - if(clusters < 4089) - { - ushort[] fat12 = new ushort[clusters]; - - _reservedSectors = fakeBpb.rsectors; - sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; - _fatFirstSector = partition.Start + (_reservedSectors * sectorsPerRealSector); - - errno = imagePlugin.ReadSectors(_fatFirstSector, fakeBpb.spfat, out byte[] fatBytes); - - if(errno != ErrorNumber.NoError) - return; - - int pos = 0; - - for(int i = 0; i + 3 < fatBytes.Length && pos < fat12.Length; i += 3) - { - fat12[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); - - if(pos >= fat12.Length) - break; - - fat12[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); - } - - bool fat12Valid = fat12[0] >= FAT12_RESERVED && fat12[1] >= FAT12_RESERVED; - - foreach(ushort entry in fat12) - { - if(entry >= FAT12_RESERVED || - entry <= clusters) - continue; - - fat12Valid = false; - - break; - } - - ushort[] fat16 = MemoryMarshal.Cast(fatBytes).ToArray(); - - bool fat16Valid = fat16[0] >= FAT16_RESERVED && fat16[1] >= 0x3FF0; - - foreach(ushort entry in fat16) - { - if(entry >= FAT16_RESERVED || - entry <= clusters) - continue; - - fat16Valid = false; - - break; - } - - isFat12 = fat12Valid; - isFat16 = fat16Valid; - - // Check BPB type - if(isFat12 == isFat16) - { - isFat12 = fakeBpb.fs_type != null && - Encoding.ASCII.GetString(fakeBpb.fs_type) == "FAT12 "; - - isFat16 = fakeBpb.fs_type != null && - Encoding.ASCII.GetString(fakeBpb.fs_type) == "FAT16 "; - } - - if(!isFat12 && - !isFat16) - isFat12 = true; - } - else - isFat16 = true; + XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); + XmlFsType.ModificationDateSpecified = true; + sb.AppendFormat("Volume last modified on {0}", XmlFsType.ModificationDate).AppendLine(); } - if(isFat12) - { - switch(bpbKind) - { - case BpbKind.Atari: - sb.AppendLine("Atari FAT12"); - - break; - case BpbKind.Apricot: - sb.AppendLine("Apricot FAT12"); - - break; - case BpbKind.Human: - sb.AppendLine("Human68k FAT12"); - - break; - default: - sb.AppendLine("Microsoft FAT12"); - - break; - } - - XmlFsType.Type = "FAT12"; - } - else if(isFat16) - { - sb.AppendLine(bpbKind == BpbKind.Atari - ? "Atari FAT16" - : bpbKind == BpbKind.Human - ? "Human68k FAT16" - : "Microsoft FAT16"); - - XmlFsType.Type = "FAT16"; - } - - if(bpbKind == BpbKind.Atari) - { - if(atariBpb.serial_no[0] == 0x49 && - atariBpb.serial_no[1] == 0x48 && - atariBpb.serial_no[2] == 0x43) - sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); - else - XmlFsType.VolumeSerial = - $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}"; - - XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name); - - if(string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) - XmlFsType.SystemIdentifier = null; - } - else if(fakeBpb.oem_name != null) - { - if(fakeBpb.oem_name[5] == 0x49 && - fakeBpb.oem_name[6] == 0x48 && - fakeBpb.oem_name[7] == 0x43) - sb.AppendLine("Volume has been modified by Windows 9x/Me Volume Tracker."); - else - { - // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies - // OEM ID should be ASCII, otherwise ignore it - if(fakeBpb.oem_name[0] >= 0x20 && - fakeBpb.oem_name[0] <= 0x7F && - fakeBpb.oem_name[1] >= 0x20 && - fakeBpb.oem_name[1] <= 0x7F && - fakeBpb.oem_name[2] >= 0x20 && - fakeBpb.oem_name[2] <= 0x7F && - fakeBpb.oem_name[3] >= 0x20 && - fakeBpb.oem_name[3] <= 0x7F && - fakeBpb.oem_name[4] >= 0x20 && - fakeBpb.oem_name[4] <= 0x7F && - fakeBpb.oem_name[5] >= 0x20 && - fakeBpb.oem_name[5] <= 0x7F && - fakeBpb.oem_name[6] >= 0x20 && - fakeBpb.oem_name[6] <= 0x7F && - fakeBpb.oem_name[7] >= 0x20 && - fakeBpb.oem_name[7] <= 0x7F) - XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name); - else if(fakeBpb.oem_name[0] < 0x20 && - fakeBpb.oem_name[1] >= 0x20 && - fakeBpb.oem_name[1] <= 0x7F && - fakeBpb.oem_name[2] >= 0x20 && - fakeBpb.oem_name[2] <= 0x7F && - fakeBpb.oem_name[3] >= 0x20 && - fakeBpb.oem_name[3] <= 0x7F && - fakeBpb.oem_name[4] >= 0x20 && - fakeBpb.oem_name[4] <= 0x7F && - fakeBpb.oem_name[5] >= 0x20 && - fakeBpb.oem_name[5] <= 0x7F && - fakeBpb.oem_name[6] >= 0x20 && - fakeBpb.oem_name[6] <= 0x7F && - fakeBpb.oem_name[7] >= 0x20 && - fakeBpb.oem_name[7] <= 0x7F) - XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1); - } - - if(fakeBpb.signature == 0x28 || - fakeBpb.signature == 0x29) - XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}"; - } - - if(XmlFsType.SystemIdentifier != null) - sb.AppendFormat("OEM Name: {0}", XmlFsType.SystemIdentifier.Trim()).AppendLine(); - - sb.AppendFormat("{0} bytes per sector.", fakeBpb.bps).AppendLine(); - - if(bpbKind != BpbKind.Human) - if(fakeBpb.sectors == 0) - sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.big_sectors, - fakeBpb.big_sectors * fakeBpb.bps).AppendLine(); - else - sb.AppendFormat("{0} sectors on volume ({1} bytes).", fakeBpb.sectors, - fakeBpb.sectors * fakeBpb.bps).AppendLine(); - else - sb.AppendFormat("{0} sectors on volume ({1} bytes).", - clusters * humanBpb.bpc / imagePlugin.Info.SectorSize, clusters * humanBpb.bpc). + if(entry.adate > 0) + sb.AppendFormat("Volume last accessed on {0:d}", DateHandlers.DosToDateTime(entry.adate, 0)). AppendLine(); - XmlFsType.Clusters = clusters; - sb.AppendFormat("{0} sectors per cluster.", fakeBpb.spc).AppendLine(); - sb.AppendFormat("{0} clusters on volume.", XmlFsType.Clusters).AppendLine(); - XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc); - sb.AppendFormat("{0} sectors reserved between BPB and FAT.", fakeBpb.rsectors).AppendLine(); - sb.AppendFormat("{0} FATs.", fakeBpb.fats_no).AppendLine(); - sb.AppendFormat("{0} entries on root directory.", fakeBpb.root_ent).AppendLine(); - - if(fakeBpb.media > 0) - sb.AppendFormat("Media descriptor: 0x{0:X2}", fakeBpb.media).AppendLine(); - - sb.AppendFormat("{0} sectors per FAT.", fakeBpb.spfat).AppendLine(); - - if(fakeBpb.sptrk > 0 && - fakeBpb.sptrk < 64 && - fakeBpb.heads > 0 && - fakeBpb.heads < 256) - { - sb.AppendFormat("{0} sectors per track.", fakeBpb.sptrk).AppendLine(); - sb.AppendFormat("{0} heads.", fakeBpb.heads).AppendLine(); - } - - if(fakeBpb.hsectors <= partition.Start) - sb.AppendFormat("{0} hidden sectors before BPB.", fakeBpb.hsectors).AppendLine(); - - if(fakeBpb.signature == 0x28 || - fakeBpb.signature == 0x29 || - andosOemCorrect) - { - sb.AppendFormat("Drive number: 0x{0:X2}", fakeBpb.drive_no).AppendLine(); - - if(XmlFsType.VolumeSerial != null) - sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); - - if((fakeBpb.flags & 0xF8) == 0x00) - { - if((fakeBpb.flags & 0x01) == 0x01) - { - sb.AppendLine("Volume should be checked on next mount."); - XmlFsType.Dirty = true; - } - - if((fakeBpb.flags & 0x02) == 0x02) - sb.AppendLine("Disk surface should be on next mount."); - } - - if(fakeBpb.signature == 0x29 || andosOemCorrect) - { - XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fakeBpb.volume_label, Encoding); - XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); - sb.AppendFormat("Filesystem type: {0}", Encoding.ASCII.GetString(fakeBpb.fs_type)).AppendLine(); - } - } - else if(bpbKind == BpbKind.Atari && - XmlFsType.VolumeSerial != null) - sb.AppendFormat("Volume Serial Number: {0}", XmlFsType.VolumeSerial).AppendLine(); - - bootChk = Sha1Context.Data(fakeBpb.boot_code, out _); - - // Workaround that PCExchange jumps into "FAT16 "... - if(XmlFsType.SystemIdentifier == "PCX 2.0 ") - fakeBpb.jump[1] += 8; - - // Check that jumps to a correct boot code position and has boot signature set. - // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... - if(XmlFsType.Bootable == false && - fakeBpb.jump != null) - XmlFsType.Bootable |= - (fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80) || - (fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 && - BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump && - BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC); - - sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; - - // First root directory sector - rootDirectorySector = - (ulong)((fakeBpb.spfat * fakeBpb.fats_no) + fakeBpb.rsectors) * sectorsPerRealSector; - - sectorsForRootDirectory = (uint)(fakeBpb.root_ent * 32 / imagePlugin.Info.SectorSize); + break; } - - if(extraInfo != null) - sb.Append(extraInfo); - - if(rootDirectorySector + partition.Start < partition.End && - imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) - { - errno = imagePlugin.ReadSectors(rootDirectorySector + partition.Start, sectorsForRootDirectory, - out byte[] rootDirectory); - - if(errno != ErrorNumber.NoError) - return; - - if(bpbKind == BpbKind.DecRainbow) - { - var rootMs = new MemoryStream(); - - foreach(ulong rootSector in new[] - { - 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 - }) - { - errno = imagePlugin.ReadSector(rootSector, out byte[] tmp); - - if(errno != ErrorNumber.NoError) - return; - - rootMs.Write(tmp, 0, tmp.Length); - } - - rootDirectory = rootMs.ToArray(); - } - - for(int i = 0; i < rootDirectory.Length; i += 32) - { - // Not a correct entry - if(rootDirectory[i] < DIRENT_MIN && - rootDirectory[i] != DIRENT_E5) - continue; - - // Deleted or subdirectory entry - if(rootDirectory[i] == DIRENT_SUBDIR || - rootDirectory[i] == DIRENT_DELETED) - continue; - - // Not a volume label - if(rootDirectory[i + 0x0B] != 0x08 && - rootDirectory[i + 0x0B] != 0x28) - continue; - - DirectoryEntry entry = - Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, 32); - - byte[] fullname = new byte[11]; - Array.Copy(entry.filename, 0, fullname, 0, 8); - Array.Copy(entry.extension, 0, fullname, 8, 3); - string volname = Encoding.GetString(fullname).Trim(); - - if(!string.IsNullOrEmpty(volname)) - XmlFsType.VolumeName = - entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) ? volname.ToLower() : volname; - - if(entry.ctime > 0 && - entry.cdate > 0) - { - XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); - - if(entry.ctime_ms > 0) - XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); - - XmlFsType.CreationDateSpecified = true; - sb.AppendFormat("Volume created on {0}", XmlFsType.CreationDate).AppendLine(); - } - - if(entry.mtime > 0 && - entry.mdate > 0) - { - XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); - XmlFsType.ModificationDateSpecified = true; - sb.AppendFormat("Volume last modified on {0}", XmlFsType.ModificationDate).AppendLine(); - } - - if(entry.adate > 0) - sb.AppendFormat("Volume last accessed on {0:d}", DateHandlers.DosToDateTime(entry.adate, 0)). - AppendLine(); - - break; - } - } - - if(!string.IsNullOrEmpty(XmlFsType.VolumeName)) - sb.AppendFormat("Volume label: {0}", XmlFsType.VolumeName).AppendLine(); - - if(XmlFsType.Bootable) - { - // Intel short jump - if(bpbSector[0] == 0xEB && - bpbSector[1] < 0x80) - { - int sigSize = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0; - byte[] bootCode = new byte[512 - sigSize - bpbSector[1] - 2]; - Array.Copy(bpbSector, bpbSector[1] + 2, bootCode, 0, bootCode.Length); - Sha1Context.Data(bootCode, out _); - } - - // Intel big jump - else if(bpbSector[0] == 0xE9 && - BitConverter.ToUInt16(bpbSector, 1) < 0x1FC) - { - int sigSize = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0; - byte[] bootCode = new byte[512 - sigSize - BitConverter.ToUInt16(bpbSector, 1) - 3]; - Array.Copy(bpbSector, BitConverter.ToUInt16(bpbSector, 1) + 3, bootCode, 0, bootCode.Length); - Sha1Context.Data(bootCode, out _); - } - - sb.AppendLine("Volume is bootable"); - sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine(); - string bootName = _knownBootHashes.FirstOrDefault(t => t.hash == bootChk).name; - - if(string.IsNullOrWhiteSpace(bootName)) - sb.AppendLine("Unknown boot code."); - else - sb.AppendFormat("Boot code corresponds to {0}", bootName).AppendLine(); - } - - information = sb.ToString(); } + + if(!string.IsNullOrEmpty(XmlFsType.VolumeName)) + sb.AppendFormat("Volume label: {0}", XmlFsType.VolumeName).AppendLine(); + + if(XmlFsType.Bootable) + { + // Intel short jump + if(bpbSector[0] == 0xEB && + bpbSector[1] < 0x80) + { + int sigSize = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0; + byte[] bootCode = new byte[512 - sigSize - bpbSector[1] - 2]; + Array.Copy(bpbSector, bpbSector[1] + 2, bootCode, 0, bootCode.Length); + Sha1Context.Data(bootCode, out _); + } + + // Intel big jump + else if(bpbSector[0] == 0xE9 && + BitConverter.ToUInt16(bpbSector, 1) < 0x1FC) + { + int sigSize = bpbSector[510] == 0x55 && bpbSector[511] == 0xAA ? 2 : 0; + byte[] bootCode = new byte[512 - sigSize - BitConverter.ToUInt16(bpbSector, 1) - 3]; + Array.Copy(bpbSector, BitConverter.ToUInt16(bpbSector, 1) + 3, bootCode, 0, bootCode.Length); + Sha1Context.Data(bootCode, out _); + } + + sb.AppendLine("Volume is bootable"); + sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine(); + string bootName = _knownBootHashes.FirstOrDefault(t => t.hash == bootChk).name; + + if(string.IsNullOrWhiteSpace(bootName)) + sb.AppendLine("Unknown boot code."); + else + sb.AppendFormat("Boot code corresponds to {0}", bootName).AppendLine(); + } + + information = sb.ToString(); } } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/Structs.cs b/Aaru.Filesystems/FAT/Structs.cs index 532c6ab26..729abdfd6 100644 --- a/Aaru.Filesystems/FAT/Structs.cs +++ b/Aaru.Filesystems/FAT/Structs.cs @@ -35,957 +35,956 @@ using System.Runtime.InteropServices; // ReSharper disable NotAccessedField.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class FAT { - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class FAT + const int UMSDOS_MAXNAME = 220; + + /// BIOS Parameter Block as used by Atari ST GEMDOS on FAT12 volumes. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct AtariParameterBlock { - const int UMSDOS_MAXNAME = 220; + /// 68000 BRA.S jump or x86 loop + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] jump; + /// OEM Name, 6 bytes, space-padded, "Loader" for Atari ST boot loader + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] oem_name; + /// Volume serial number + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] serial_no; + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT (inclusive) + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor, unused by GEMDOS + public readonly byte media; + /// Sectors per FAT + public readonly ushort spfat; + /// Sectors per track + public readonly ushort sptrk; + /// Heads + public readonly ushort heads; + /// Hidden sectors before BPB, unused by GEMDOS + public readonly ushort hsectors; + /// Word to be loaded in the cmdload system variable. Big-endian. + public readonly ushort execflag; + /// + /// Word indicating load mode. If zero, file named is located and loaded. It not, sectors + /// specified in and are loaded. Big endian. + /// + public readonly ushort ldmode; + /// Starting sector of boot code. + public readonly ushort ssect; + /// Count of sectors of boot code. + public readonly ushort sectcnt; + /// Address where boot code should be loaded. + public readonly ushort ldaaddr; + /// Padding. + public readonly ushort padding; + /// Address where FAT and root directory sectors must be loaded. + public readonly ushort fatbuf; + /// Unknown. + public readonly ushort unknown; + /// Filename to be loaded for booting. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] fname; + /// Reserved + public readonly ushort reserved; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 455)] + public readonly byte[] boot_code; + /// Big endian word to make big endian sum of all sector words be equal to 0x1234 if disk is bootable. + public readonly ushort checksum; + } - /// BIOS Parameter Block as used by Atari ST GEMDOS on FAT12 volumes. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct AtariParameterBlock + /// BIOS Parameter Block as used by MSX-DOS 2. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct MsxParameterBlock + { + /// x86 loop + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT (inclusive) + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT + public readonly ushort spfat; + /// Sectors per track + public readonly ushort sptrk; + /// Heads + public readonly ushort heads; + /// Hidden sectors before BPB + public readonly ushort hsectors; + /// Jump for MSX-DOS 1 boot code + public readonly ushort msxdos_jmp; + /// Set to "VOL_ID" by MSX-DOS 2 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] vol_id; + /// Bigger than 0 if there are deleted files (MSX-DOS 2) + public readonly byte undelete_flag; + /// Volume serial number (MSX-DOS 2) + public readonly uint serial_no; + /// Reserved (MSX-DOS 2) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] reserved; + /// Jump for MSX-DOS 2 boot code (MSX-DOS 2) + public readonly ushort msxdos2_jmp; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 460)] + public readonly byte[] boot_code; + /// Always 0x55 0xAA. + public readonly ushort boot_signature; + } + + /// DOS 2.0 BIOS Parameter Block. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BiosParameterBlock2 + { + /// x86 jump + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT + public readonly ushort spfat; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 486)] + public readonly byte[] boot_code; + /// 0x55 0xAA if bootable. + public readonly ushort boot_signature; + } + + /// DOS 3.0 BIOS Parameter Block. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BiosParameterBlock30 + { + /// x86 jump + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT + public readonly ushort spfat; + /// Sectors per track + public readonly ushort sptrk; + /// Heads + public readonly ushort heads; + /// Hidden sectors before BPB + public readonly ushort hsectors; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 480)] + public readonly byte[] boot_code; + /// Always 0x55 0xAA. + public readonly ushort boot_signature; + } + + /// DOS 3.2 BIOS Parameter Block. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BiosParameterBlock32 + { + /// x86 jump + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT + public readonly ushort spfat; + /// Sectors per track + public readonly ushort sptrk; + /// Heads + public readonly ushort heads; + /// Hidden sectors before BPB + public readonly ushort hsectors; + /// Total sectors including hidden ones + public readonly ushort total_sectors; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 478)] + public readonly byte[] boot_code; + /// Always 0x55 0xAA. + public readonly ushort boot_signature; + } + + /// DOS 3.31 BIOS Parameter Block. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BiosParameterBlock33 + { + /// x86 jump + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT + public readonly ushort spfat; + /// Sectors per track + public readonly ushort sptrk; + /// Heads + public readonly ushort heads; + /// Hidden sectors before BPB + public readonly uint hsectors; + /// Sectors in volume if > 65535 + public uint big_sectors; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 474)] + public readonly byte[] boot_code; + /// Always 0x55 0xAA. + public readonly ushort boot_signature; + } + + /// DOS 3.4 BIOS Parameter Block. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BiosParameterBlockShortEbpb + { + /// x86 jump + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT + public readonly ushort spfat; + /// Sectors per track + public readonly ushort sptrk; + /// Heads + public readonly ushort heads; + /// Hidden sectors before BPB + public readonly uint hsectors; + /// Sectors in volume if > 65535 + public uint big_sectors; + /// Drive number + public readonly byte drive_no; + /// Volume flags + public readonly byte flags; + /// EPB signature, 0x28 + public readonly byte signature; + /// Volume serial number + public readonly uint serial_no; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 467)] + public readonly byte[] boot_code; + /// Always 0x55 0xAA. + public readonly ushort boot_signature; + } + + /// DOS 4.0 or higher BIOS Parameter Block. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BiosParameterBlockEbpb + { + /// x86 jump + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public byte[] oem_name; + /// Bytes per sector + public ushort bps; + /// Sectors per cluster + public byte spc; + /// Reserved sectors between BPB and FAT + public ushort rsectors; + /// Number of FATs + public byte fats_no; + /// Number of entries on root directory + public ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public byte media; + /// Sectors per FAT + public ushort spfat; + /// Sectors per track + public ushort sptrk; + /// Heads + public ushort heads; + /// Hidden sectors before BPB + public uint hsectors; + /// Sectors in volume if > 65535 + public uint big_sectors; + /// Drive number + public byte drive_no; + /// Volume flags + public byte flags; + /// EPB signature, 0x29 + public byte signature; + /// Volume serial number + public uint serial_no; + /// Volume label, 11 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] volume_label; + /// Filesystem type, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] fs_type; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 448)] + public byte[] boot_code; + /// Always 0x55 0xAA. + public ushort boot_signature; + } + + /// FAT32 Parameter Block + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct Fat32ParameterBlockShort + { + /// x86 jump + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory, set to 0 + public readonly ushort root_ent; + /// Sectors in volume, set to 0 + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT, set to 0 + public readonly ushort spfat; + /// Sectors per track + public readonly ushort sptrk; + /// Heads + public readonly ushort heads; + /// Hidden sectors before BPB + public readonly uint hsectors; + /// Sectors in volume + public uint big_sectors; + /// Sectors per FAT + public readonly uint big_spfat; + /// FAT flags + public readonly ushort mirror_flags; + /// FAT32 version + public readonly ushort version; + /// Cluster of root directory + public readonly uint root_cluster; + /// Sector of FSINFO structure + public readonly ushort fsinfo_sector; + /// Sector of FAT32PB backup + public readonly ushort backup_sector; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] reserved; + /// Drive number + public readonly byte drive_no; + /// Volume flags + public readonly byte flags; + /// Signature, should be 0x28 + public readonly byte signature; + /// Volume serial number + public readonly uint serial_no; + /// Volume label, 11 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] reserved2; + /// Sectors in volume if equals 0 + public ulong huge_sectors; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 420)] + public readonly byte[] boot_code; + /// Always 0x55 0xAA. + public readonly ushort boot_signature; + } + + /// FAT32 Parameter Block + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct Fat32ParameterBlock + { + /// x86 jump + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// Bytes per sector + public ushort bps; + /// Sectors per cluster + public byte spc; + /// Reserved sectors between BPB and FAT + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory, set to 0 + public readonly ushort root_ent; + /// Sectors in volume, set to 0 + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT, set to 0 + public readonly ushort spfat; + /// Sectors per track + public ushort sptrk; + /// Heads + public readonly ushort heads; + /// Hidden sectors before BPB + public uint hsectors; + /// Sectors in volume + public uint big_sectors; + /// Sectors per FAT + public uint big_spfat; + /// FAT flags + public readonly ushort mirror_flags; + /// FAT32 version + public readonly ushort version; + /// Cluster of root directory + public readonly uint root_cluster; + /// Sector of FSINFO structure + public readonly ushort fsinfo_sector; + /// Sector of FAT32PB backup + public readonly ushort backup_sector; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] reserved; + /// Drive number + public readonly byte drive_no; + /// Volume flags + public readonly byte flags; + /// Signature, should be 0x29 + public readonly byte signature; + /// Volume serial number + public readonly uint serial_no; + /// Volume label, 11 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] volume_label; + /// Filesystem type, 8 bytes, space-padded, must be "FAT32 " + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] fs_type; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 419)] + public readonly byte[] boot_code; + /// Always 0x55 0xAA. + public readonly ushort boot_signature; + } + + /// Apricot Label. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ApricotLabel + { + /// Version of format which created disk + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] version; + /// Operating system. + public readonly byte operatingSystem; + /// Software write protection. + [MarshalAs(UnmanagedType.U1)] + public readonly bool writeProtected; + /// Copy protected. + [MarshalAs(UnmanagedType.U1)] + public readonly bool copyProtected; + /// Boot type. + public readonly byte bootType; + /// Partitions. + public readonly byte partitionCount; + /// Is hard disk?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool winchester; + /// Sector size. + public readonly ushort sectorSize; + /// Sectors per track. + public readonly ushort spt; + /// Tracks per side. + public readonly uint cylinders; + /// Sides. + public readonly byte heads; + /// Interleave factor. + public readonly byte interleave; + /// Skew factor. + public readonly ushort skew; + /// Sector where boot code starts. + public readonly uint bootLocation; + /// Size in sectors of boot code. + public readonly ushort bootSize; + /// Address at which to load boot code. + public readonly uint bootAddress; + /// Offset where to jump to boot. + public readonly ushort bootOffset; + /// Segment where to jump to boot. + public readonly ushort bootSegment; + /// First data sector. + public readonly uint firstDataBlock; + /// Generation. + public readonly ushort generation; + /// Copy count. + public readonly ushort copyCount; + /// Maximum number of copies. + public readonly ushort maxCopies; + /// Serial number. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] serialNumber; + /// Part number. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] partNumber; + /// Copyright. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] + public readonly byte[] copyright; + /// BPB for whole disk. + public ApricotParameterBlock mainBPB; + /// Name of FONT file. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] fontName; + /// Name of KEYBOARD file. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] keyboardName; + /// Minor BIOS version. + public readonly byte biosMinorVersion; + /// Major BIOS version. + public readonly byte biosMajorVersion; + /// Diagnostics enabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool diagnosticsFlag; + /// Printer device. + public readonly byte prnDevice; + /// Bell volume. + public readonly byte bellVolume; + /// Cache enabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool enableCache; + /// Graphics enabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool enableGraphics; + /// Length in sectors of DOS. + public readonly byte dosLength; + /// Length in sectors of FONT file. + public readonly byte fontLength; + /// Length in sectors of KEYBOARD file. + public readonly byte keyboardLength; + /// Starting sector of DOS. + public readonly ushort dosStart; + /// Starting sector of FONT file. + public readonly ushort fontStart; + /// Starting sector of KEYBOARD file. + public readonly ushort keyboardStart; + /// Keyboard click volume. + public readonly byte keyboardVolume; + /// Auto-repeat enabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool autorepeat; + /// Auto-repeat lead-in. + public readonly byte autorepeatLeadIn; + /// Auto-repeat interval. + public readonly byte autorepeatInterval; + /// Microscreen mode. + public readonly byte microscreenMode; + /// Spare area for keyboard values expansion. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] spareKeyboard; + /// Screen line mode. + public readonly byte lineMode; + /// Screen line width. + public readonly byte lineWidth; + /// Screen disabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool imageOff; + /// Spare area for screen values expansion. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] + public readonly byte[] spareScreen; + /// TX baud rate. + public readonly byte txBaudRate; + /// RX baud rate. + public readonly byte rxBaudRate; + /// TX bits. + public readonly byte txBits; + /// RX bits. + public readonly byte rxBits; + /// Stop bits. + public readonly byte stopBits; + /// Parity enabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool parityCheck; + /// Parity type. + public readonly byte parityType; + /// Xon/Xoff enabled on TX. + [MarshalAs(UnmanagedType.U1)] + public readonly bool txXonXoff; + /// Xon/Xoff enabled on RX. + [MarshalAs(UnmanagedType.U1)] + public readonly bool rxXonXoff; + /// Xon character. + public readonly byte xonCharacter; + /// Xoff character. + public readonly byte xoffCharacter; + /// Xon/Xoff buffer on RX. + public readonly ushort rxXonXoffBuffer; + /// DTR/DSR enabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool dtrDsr; + /// CTS/RTS enabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool ctsRts; + /// NULLs after CR. + public readonly byte nullsAfterCr; + /// NULLs after 0xFF. + public readonly byte nullsAfterFF; + /// Send LF after CR in serial port. + [MarshalAs(UnmanagedType.U1)] + public readonly bool lfAfterCRSerial; + /// BIOS error report in serial port. + [MarshalAs(UnmanagedType.U1)] + public readonly bool biosErrorReportSerial; + /// Spare area for serial port values expansion. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] + public readonly byte[] spareSerial; + /// Send LF after CR in parallel port. + [MarshalAs(UnmanagedType.U1)] + public readonly bool lfAfterCrParallel; + /// Select line supported?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool selectLine; + /// Paper empty supported?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool paperEmpty; + /// Fault line supported?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool faultLine; + /// BIOS error report in parallel port. + [MarshalAs(UnmanagedType.U1)] + public readonly bool biosErrorReportParallel; + /// Spare area for parallel port values expansion. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] spareParallel; + /// Spare area for Winchester values expansion. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] + public readonly byte[] spareWinchester; + /// Parking enabled?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool parkingEnabled; + /// Format protection?. + [MarshalAs(UnmanagedType.U1)] + public readonly bool formatProtection; + /// Spare area for RAM disk values expansion. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] spareRamDisk; + /// List of bad blocks. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly ushort[] badBlocks; + /// Array of partition BPBs. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly ApricotParameterBlock[] partitions; + /// Spare area. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] + public readonly byte[] spare; + /// CP/M double side indicator?. + public readonly bool cpmDoubleSided; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ApricotParameterBlock + { + /// Bytes per sector + public readonly ushort bps; + /// Sectors per cluster + public readonly byte spc; + /// Reserved sectors between BPB and FAT + public readonly ushort rsectors; + /// Number of FATs + public readonly byte fats_no; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Sectors in volume + public ushort sectors; + /// Media descriptor + public readonly byte media; + /// Sectors per FAT + public readonly ushort spfat; + /// Disk type + public readonly byte diskType; + /// Volume starting sector + public readonly ushort startSector; + } + + /// FAT32 FS Information Sector + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct FsInfoSector + { + /// Signature must be + public readonly uint signature1; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 480)] + public readonly byte[] reserved1; + /// Signature must be + public readonly uint signature2; + /// Free clusters + public readonly uint free_clusters; + /// cated cluster + public readonly uint last_cluster; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] reserved2; + /// Signature must be + public readonly uint signature3; + } + + /// Human68k Parameter Block, big endian, 512 bytes even on 256 bytes/sector. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HumanParameterBlock + { + /// 68k bra.S + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] jump; + /// OEM Name, 16 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] oem_name; + /// Bytes per cluster + public readonly ushort bpc; + /// Unknown, seen 1, 2 and 16 + public readonly byte unknown1; + /// Unknown, always 512? + public readonly ushort unknown2; + /// Unknown, always 1? + public readonly byte unknown3; + /// Number of entries on root directory + public readonly ushort root_ent; + /// Clusters, set to 0 if more than 65536 + public readonly ushort clusters; + /// Media descriptor + public readonly byte media; + /// Clusters per FAT, set to 0 + public readonly byte cpfat; + /// Clustersin volume + public readonly uint big_clusters; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 478)] + public readonly byte[] boot_code; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryEntry + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] filename; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] extension; + public readonly FatAttributes attributes; + public readonly CaseInfo caseinfo; + public readonly byte ctime_ms; + public readonly ushort ctime; + public readonly ushort cdate; + public readonly ushort adate; + public readonly ushort ea_handle; + public readonly ushort mtime; + public readonly ushort mdate; + public readonly ushort start_cluster; + public readonly uint size; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HumanDirectoryEntry + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] name1; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] extension; + public readonly FatAttributes attributes; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly byte[] name2; + public readonly ushort mtime; + public readonly ushort mdate; + public readonly ushort start_cluster; + public readonly uint size; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct LfnEntry + { + public readonly byte sequence; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly byte[] name1; + public readonly FatAttributes attributes; + public readonly byte type; + public readonly byte checksum; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] name2; + public readonly ushort start_cluster; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] name3; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct EaHeader + { + public readonly ushort magic; + public readonly ushort cluster; + public readonly EaFlags flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] filename; + public readonly uint unknown; + public readonly ushort zero; + } + + /// This structure is 256 bytes large, depending on the name, only part of it is written to disk + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct UmsdosDirectoryEntry + { + /// if == 0, then this entry is not used + public readonly byte name_len; + /// UMSDOS_xxxx + public readonly UmsdosFlags flags; + /// How many hard links point to this entry + public readonly ushort nlink; + /// Owner user id + public readonly int uid; + /// Group id + public readonly int gid; + /// Access time + public readonly int atime; + /// Last modification time + public readonly int mtime; + /// Creation time + public readonly int ctime; + /// major and minor number of a device + public readonly uint rdev; + /* */ + /// Standard UNIX permissions bits + type of + public readonly ushort mode; + /// unused bytes for future extensions + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] spare; + /// + /// Not '\0' terminated but '\0' padded, so it will allow for adding news fields in this record by reducing the + /// size of name[] + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = UMSDOS_MAXNAME)] + public readonly byte[] name; + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + enum UmsdosFlags : byte + { + /// Never show this entry in directory search + UMSDOS_HIDDEN = 1, + /// It is a (pseudo) hard link + UMSDOS_HLINK = 2 + } + + class CompleteDirectoryEntry + { + public DirectoryEntry Dirent; + public DirectoryEntry Fat32Ea; + public HumanDirectoryEntry HumanDirent; + public string HumanName; + public string Lfn; + public UmsdosDirectoryEntry LinuxDirent; + public string LinuxName; + public string Longname; + public string Shortname; + + public override string ToString() { - /// 68000 BRA.S jump or x86 loop - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] jump; - /// OEM Name, 6 bytes, space-padded, "Loader" for Atari ST boot loader - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] oem_name; - /// Volume serial number - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] serial_no; - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT (inclusive) - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor, unused by GEMDOS - public readonly byte media; - /// Sectors per FAT - public readonly ushort spfat; - /// Sectors per track - public readonly ushort sptrk; - /// Heads - public readonly ushort heads; - /// Hidden sectors before BPB, unused by GEMDOS - public readonly ushort hsectors; - /// Word to be loaded in the cmdload system variable. Big-endian. - public readonly ushort execflag; - /// - /// Word indicating load mode. If zero, file named is located and loaded. It not, sectors - /// specified in and are loaded. Big endian. - /// - public readonly ushort ldmode; - /// Starting sector of boot code. - public readonly ushort ssect; - /// Count of sectors of boot code. - public readonly ushort sectcnt; - /// Address where boot code should be loaded. - public readonly ushort ldaaddr; - /// Padding. - public readonly ushort padding; - /// Address where FAT and root directory sectors must be loaded. - public readonly ushort fatbuf; - /// Unknown. - public readonly ushort unknown; - /// Filename to be loaded for booting. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] fname; - /// Reserved - public readonly ushort reserved; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 455)] - public readonly byte[] boot_code; - /// Big endian word to make big endian sum of all sector words be equal to 0x1234 if disk is bootable. - public readonly ushort checksum; - } + // This ensures UMSDOS takes preference when present + if(!string.IsNullOrEmpty(LinuxName)) + return LinuxName; - /// BIOS Parameter Block as used by MSX-DOS 2. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct MsxParameterBlock - { - /// x86 loop - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT (inclusive) - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT - public readonly ushort spfat; - /// Sectors per track - public readonly ushort sptrk; - /// Heads - public readonly ushort heads; - /// Hidden sectors before BPB - public readonly ushort hsectors; - /// Jump for MSX-DOS 1 boot code - public readonly ushort msxdos_jmp; - /// Set to "VOL_ID" by MSX-DOS 2 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] vol_id; - /// Bigger than 0 if there are deleted files (MSX-DOS 2) - public readonly byte undelete_flag; - /// Volume serial number (MSX-DOS 2) - public readonly uint serial_no; - /// Reserved (MSX-DOS 2) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] reserved; - /// Jump for MSX-DOS 2 boot code (MSX-DOS 2) - public readonly ushort msxdos2_jmp; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 460)] - public readonly byte[] boot_code; - /// Always 0x55 0xAA. - public readonly ushort boot_signature; - } + // This ensures LFN takes preference when eCS is in use + if(!string.IsNullOrEmpty(Lfn)) + return Lfn; - /// DOS 2.0 BIOS Parameter Block. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BiosParameterBlock2 - { - /// x86 jump - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT - public readonly ushort spfat; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 486)] - public readonly byte[] boot_code; - /// 0x55 0xAA if bootable. - public readonly ushort boot_signature; - } + // This ensures Humans takes preference when present + if(!string.IsNullOrEmpty(HumanName)) + return HumanName; - /// DOS 3.0 BIOS Parameter Block. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BiosParameterBlock30 - { - /// x86 jump - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT - public readonly ushort spfat; - /// Sectors per track - public readonly ushort sptrk; - /// Heads - public readonly ushort heads; - /// Hidden sectors before BPB - public readonly ushort hsectors; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 480)] - public readonly byte[] boot_code; - /// Always 0x55 0xAA. - public readonly ushort boot_signature; - } - - /// DOS 3.2 BIOS Parameter Block. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BiosParameterBlock32 - { - /// x86 jump - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT - public readonly ushort spfat; - /// Sectors per track - public readonly ushort sptrk; - /// Heads - public readonly ushort heads; - /// Hidden sectors before BPB - public readonly ushort hsectors; - /// Total sectors including hidden ones - public readonly ushort total_sectors; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 478)] - public readonly byte[] boot_code; - /// Always 0x55 0xAA. - public readonly ushort boot_signature; - } - - /// DOS 3.31 BIOS Parameter Block. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BiosParameterBlock33 - { - /// x86 jump - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT - public readonly ushort spfat; - /// Sectors per track - public readonly ushort sptrk; - /// Heads - public readonly ushort heads; - /// Hidden sectors before BPB - public readonly uint hsectors; - /// Sectors in volume if > 65535 - public uint big_sectors; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 474)] - public readonly byte[] boot_code; - /// Always 0x55 0xAA. - public readonly ushort boot_signature; - } - - /// DOS 3.4 BIOS Parameter Block. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BiosParameterBlockShortEbpb - { - /// x86 jump - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT - public readonly ushort spfat; - /// Sectors per track - public readonly ushort sptrk; - /// Heads - public readonly ushort heads; - /// Hidden sectors before BPB - public readonly uint hsectors; - /// Sectors in volume if > 65535 - public uint big_sectors; - /// Drive number - public readonly byte drive_no; - /// Volume flags - public readonly byte flags; - /// EPB signature, 0x28 - public readonly byte signature; - /// Volume serial number - public readonly uint serial_no; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 467)] - public readonly byte[] boot_code; - /// Always 0x55 0xAA. - public readonly ushort boot_signature; - } - - /// DOS 4.0 or higher BIOS Parameter Block. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BiosParameterBlockEbpb - { - /// x86 jump - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public byte[] oem_name; - /// Bytes per sector - public ushort bps; - /// Sectors per cluster - public byte spc; - /// Reserved sectors between BPB and FAT - public ushort rsectors; - /// Number of FATs - public byte fats_no; - /// Number of entries on root directory - public ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor - public byte media; - /// Sectors per FAT - public ushort spfat; - /// Sectors per track - public ushort sptrk; - /// Heads - public ushort heads; - /// Hidden sectors before BPB - public uint hsectors; - /// Sectors in volume if > 65535 - public uint big_sectors; - /// Drive number - public byte drive_no; - /// Volume flags - public byte flags; - /// EPB signature, 0x29 - public byte signature; - /// Volume serial number - public uint serial_no; - /// Volume label, 11 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] volume_label; - /// Filesystem type, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] fs_type; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 448)] - public byte[] boot_code; - /// Always 0x55 0xAA. - public ushort boot_signature; - } - - /// FAT32 Parameter Block - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct Fat32ParameterBlockShort - { - /// x86 jump - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory, set to 0 - public readonly ushort root_ent; - /// Sectors in volume, set to 0 - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT, set to 0 - public readonly ushort spfat; - /// Sectors per track - public readonly ushort sptrk; - /// Heads - public readonly ushort heads; - /// Hidden sectors before BPB - public readonly uint hsectors; - /// Sectors in volume - public uint big_sectors; - /// Sectors per FAT - public readonly uint big_spfat; - /// FAT flags - public readonly ushort mirror_flags; - /// FAT32 version - public readonly ushort version; - /// Cluster of root directory - public readonly uint root_cluster; - /// Sector of FSINFO structure - public readonly ushort fsinfo_sector; - /// Sector of FAT32PB backup - public readonly ushort backup_sector; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] reserved; - /// Drive number - public readonly byte drive_no; - /// Volume flags - public readonly byte flags; - /// Signature, should be 0x28 - public readonly byte signature; - /// Volume serial number - public readonly uint serial_no; - /// Volume label, 11 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] reserved2; - /// Sectors in volume if equals 0 - public ulong huge_sectors; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 420)] - public readonly byte[] boot_code; - /// Always 0x55 0xAA. - public readonly ushort boot_signature; - } - - /// FAT32 Parameter Block - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct Fat32ParameterBlock - { - /// x86 jump - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// Bytes per sector - public ushort bps; - /// Sectors per cluster - public byte spc; - /// Reserved sectors between BPB and FAT - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory, set to 0 - public readonly ushort root_ent; - /// Sectors in volume, set to 0 - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT, set to 0 - public readonly ushort spfat; - /// Sectors per track - public ushort sptrk; - /// Heads - public readonly ushort heads; - /// Hidden sectors before BPB - public uint hsectors; - /// Sectors in volume - public uint big_sectors; - /// Sectors per FAT - public uint big_spfat; - /// FAT flags - public readonly ushort mirror_flags; - /// FAT32 version - public readonly ushort version; - /// Cluster of root directory - public readonly uint root_cluster; - /// Sector of FSINFO structure - public readonly ushort fsinfo_sector; - /// Sector of FAT32PB backup - public readonly ushort backup_sector; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] reserved; - /// Drive number - public readonly byte drive_no; - /// Volume flags - public readonly byte flags; - /// Signature, should be 0x29 - public readonly byte signature; - /// Volume serial number - public readonly uint serial_no; - /// Volume label, 11 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] volume_label; - /// Filesystem type, 8 bytes, space-padded, must be "FAT32 " - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] fs_type; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 419)] - public readonly byte[] boot_code; - /// Always 0x55 0xAA. - public readonly ushort boot_signature; - } - - /// Apricot Label. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ApricotLabel - { - /// Version of format which created disk - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] version; - /// Operating system. - public readonly byte operatingSystem; - /// Software write protection. - [MarshalAs(UnmanagedType.U1)] - public readonly bool writeProtected; - /// Copy protected. - [MarshalAs(UnmanagedType.U1)] - public readonly bool copyProtected; - /// Boot type. - public readonly byte bootType; - /// Partitions. - public readonly byte partitionCount; - /// Is hard disk?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool winchester; - /// Sector size. - public readonly ushort sectorSize; - /// Sectors per track. - public readonly ushort spt; - /// Tracks per side. - public readonly uint cylinders; - /// Sides. - public readonly byte heads; - /// Interleave factor. - public readonly byte interleave; - /// Skew factor. - public readonly ushort skew; - /// Sector where boot code starts. - public readonly uint bootLocation; - /// Size in sectors of boot code. - public readonly ushort bootSize; - /// Address at which to load boot code. - public readonly uint bootAddress; - /// Offset where to jump to boot. - public readonly ushort bootOffset; - /// Segment where to jump to boot. - public readonly ushort bootSegment; - /// First data sector. - public readonly uint firstDataBlock; - /// Generation. - public readonly ushort generation; - /// Copy count. - public readonly ushort copyCount; - /// Maximum number of copies. - public readonly ushort maxCopies; - /// Serial number. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] serialNumber; - /// Part number. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] partNumber; - /// Copyright. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] - public readonly byte[] copyright; - /// BPB for whole disk. - public ApricotParameterBlock mainBPB; - /// Name of FONT file. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] fontName; - /// Name of KEYBOARD file. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] keyboardName; - /// Minor BIOS version. - public readonly byte biosMinorVersion; - /// Major BIOS version. - public readonly byte biosMajorVersion; - /// Diagnostics enabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool diagnosticsFlag; - /// Printer device. - public readonly byte prnDevice; - /// Bell volume. - public readonly byte bellVolume; - /// Cache enabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool enableCache; - /// Graphics enabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool enableGraphics; - /// Length in sectors of DOS. - public readonly byte dosLength; - /// Length in sectors of FONT file. - public readonly byte fontLength; - /// Length in sectors of KEYBOARD file. - public readonly byte keyboardLength; - /// Starting sector of DOS. - public readonly ushort dosStart; - /// Starting sector of FONT file. - public readonly ushort fontStart; - /// Starting sector of KEYBOARD file. - public readonly ushort keyboardStart; - /// Keyboard click volume. - public readonly byte keyboardVolume; - /// Auto-repeat enabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool autorepeat; - /// Auto-repeat lead-in. - public readonly byte autorepeatLeadIn; - /// Auto-repeat interval. - public readonly byte autorepeatInterval; - /// Microscreen mode. - public readonly byte microscreenMode; - /// Spare area for keyboard values expansion. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] spareKeyboard; - /// Screen line mode. - public readonly byte lineMode; - /// Screen line width. - public readonly byte lineWidth; - /// Screen disabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool imageOff; - /// Spare area for screen values expansion. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] - public readonly byte[] spareScreen; - /// TX baud rate. - public readonly byte txBaudRate; - /// RX baud rate. - public readonly byte rxBaudRate; - /// TX bits. - public readonly byte txBits; - /// RX bits. - public readonly byte rxBits; - /// Stop bits. - public readonly byte stopBits; - /// Parity enabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool parityCheck; - /// Parity type. - public readonly byte parityType; - /// Xon/Xoff enabled on TX. - [MarshalAs(UnmanagedType.U1)] - public readonly bool txXonXoff; - /// Xon/Xoff enabled on RX. - [MarshalAs(UnmanagedType.U1)] - public readonly bool rxXonXoff; - /// Xon character. - public readonly byte xonCharacter; - /// Xoff character. - public readonly byte xoffCharacter; - /// Xon/Xoff buffer on RX. - public readonly ushort rxXonXoffBuffer; - /// DTR/DSR enabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool dtrDsr; - /// CTS/RTS enabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool ctsRts; - /// NULLs after CR. - public readonly byte nullsAfterCr; - /// NULLs after 0xFF. - public readonly byte nullsAfterFF; - /// Send LF after CR in serial port. - [MarshalAs(UnmanagedType.U1)] - public readonly bool lfAfterCRSerial; - /// BIOS error report in serial port. - [MarshalAs(UnmanagedType.U1)] - public readonly bool biosErrorReportSerial; - /// Spare area for serial port values expansion. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 13)] - public readonly byte[] spareSerial; - /// Send LF after CR in parallel port. - [MarshalAs(UnmanagedType.U1)] - public readonly bool lfAfterCrParallel; - /// Select line supported?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool selectLine; - /// Paper empty supported?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool paperEmpty; - /// Fault line supported?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool faultLine; - /// BIOS error report in parallel port. - [MarshalAs(UnmanagedType.U1)] - public readonly bool biosErrorReportParallel; - /// Spare area for parallel port values expansion. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] spareParallel; - /// Spare area for Winchester values expansion. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] - public readonly byte[] spareWinchester; - /// Parking enabled?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool parkingEnabled; - /// Format protection?. - [MarshalAs(UnmanagedType.U1)] - public readonly bool formatProtection; - /// Spare area for RAM disk values expansion. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] spareRamDisk; - /// List of bad blocks. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly ushort[] badBlocks; - /// Array of partition BPBs. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly ApricotParameterBlock[] partitions; - /// Spare area. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] - public readonly byte[] spare; - /// CP/M double side indicator?. - public readonly bool cpmDoubleSided; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ApricotParameterBlock - { - /// Bytes per sector - public readonly ushort bps; - /// Sectors per cluster - public readonly byte spc; - /// Reserved sectors between BPB and FAT - public readonly ushort rsectors; - /// Number of FATs - public readonly byte fats_no; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Sectors in volume - public ushort sectors; - /// Media descriptor - public readonly byte media; - /// Sectors per FAT - public readonly ushort spfat; - /// Disk type - public readonly byte diskType; - /// Volume starting sector - public readonly ushort startSector; - } - - /// FAT32 FS Information Sector - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct FsInfoSector - { - /// Signature must be - public readonly uint signature1; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 480)] - public readonly byte[] reserved1; - /// Signature must be - public readonly uint signature2; - /// Free clusters - public readonly uint free_clusters; - /// cated cluster - public readonly uint last_cluster; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] reserved2; - /// Signature must be - public readonly uint signature3; - } - - /// Human68k Parameter Block, big endian, 512 bytes even on 256 bytes/sector. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HumanParameterBlock - { - /// 68k bra.S - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] jump; - /// OEM Name, 16 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] oem_name; - /// Bytes per cluster - public readonly ushort bpc; - /// Unknown, seen 1, 2 and 16 - public readonly byte unknown1; - /// Unknown, always 512? - public readonly ushort unknown2; - /// Unknown, always 1? - public readonly byte unknown3; - /// Number of entries on root directory - public readonly ushort root_ent; - /// Clusters, set to 0 if more than 65536 - public readonly ushort clusters; - /// Media descriptor - public readonly byte media; - /// Clusters per FAT, set to 0 - public readonly byte cpfat; - /// Clustersin volume - public readonly uint big_clusters; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 478)] - public readonly byte[] boot_code; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryEntry - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] filename; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] extension; - public readonly FatAttributes attributes; - public readonly CaseInfo caseinfo; - public readonly byte ctime_ms; - public readonly ushort ctime; - public readonly ushort cdate; - public readonly ushort adate; - public readonly ushort ea_handle; - public readonly ushort mtime; - public readonly ushort mdate; - public readonly ushort start_cluster; - public readonly uint size; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HumanDirectoryEntry - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] name1; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] extension; - public readonly FatAttributes attributes; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly byte[] name2; - public readonly ushort mtime; - public readonly ushort mdate; - public readonly ushort start_cluster; - public readonly uint size; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct LfnEntry - { - public readonly byte sequence; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly byte[] name1; - public readonly FatAttributes attributes; - public readonly byte type; - public readonly byte checksum; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] name2; - public readonly ushort start_cluster; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] name3; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct EaHeader - { - public readonly ushort magic; - public readonly ushort cluster; - public readonly EaFlags flags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] filename; - public readonly uint unknown; - public readonly ushort zero; - } - - /// This structure is 256 bytes large, depending on the name, only part of it is written to disk - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct UmsdosDirectoryEntry - { - /// if == 0, then this entry is not used - public readonly byte name_len; - /// UMSDOS_xxxx - public readonly UmsdosFlags flags; - /// How many hard links point to this entry - public readonly ushort nlink; - /// Owner user id - public readonly int uid; - /// Group id - public readonly int gid; - /// Access time - public readonly int atime; - /// Last modification time - public readonly int mtime; - /// Creation time - public readonly int ctime; - /// major and minor number of a device - public readonly uint rdev; - /* */ - /// Standard UNIX permissions bits + type of - public readonly ushort mode; - /// unused bytes for future extensions - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] spare; - /// - /// Not '\0' terminated but '\0' padded, so it will allow for adding news fields in this record by reducing the - /// size of name[] - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = UMSDOS_MAXNAME)] - public readonly byte[] name; - } - - [SuppressMessage("ReSharper", "InconsistentNaming")] - enum UmsdosFlags : byte - { - /// Never show this entry in directory search - UMSDOS_HIDDEN = 1, - /// It is a (pseudo) hard link - UMSDOS_HLINK = 2 - } - - class CompleteDirectoryEntry - { - public DirectoryEntry Dirent; - public DirectoryEntry Fat32Ea; - public HumanDirectoryEntry HumanDirent; - public string HumanName; - public string Lfn; - public UmsdosDirectoryEntry LinuxDirent; - public string LinuxName; - public string Longname; - public string Shortname; - - public override string ToString() - { - // This ensures UMSDOS takes preference when present - if(!string.IsNullOrEmpty(LinuxName)) - return LinuxName; - - // This ensures LFN takes preference when eCS is in use - if(!string.IsNullOrEmpty(Lfn)) - return Lfn; - - // This ensures Humans takes preference when present - if(!string.IsNullOrEmpty(HumanName)) - return HumanName; - - return !string.IsNullOrEmpty(Longname) ? Longname : Shortname; - } + return !string.IsNullOrEmpty(Longname) ? Longname : Shortname; } } } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/Super.cs b/Aaru.Filesystems/FAT/Super.cs index 1cdf784c8..61e7264d8 100644 --- a/Aaru.Filesystems/FAT/Super.cs +++ b/Aaru.Filesystems/FAT/Super.cs @@ -47,1071 +47,1070 @@ using Schemas; using FileSystemInfo = Aaru.CommonTypes.Structs.FileSystemInfo; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class FAT { - public sealed partial class FAT + uint _fatEntriesPerSector; + IMediaImage _image; + + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - uint _fatEntriesPerSector; - IMediaImage _image; + XmlFsType = new FileSystemType(); + ErrorNumber errno; - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + options ??= GetDefaultOptions(); + + if(options.TryGetValue("debug", out string debugString)) + bool.TryParse(debugString, out _debug); + + // Default namespace + @namespace ??= "ecs"; + + switch(@namespace.ToLowerInvariant()) { - XmlFsType = new FileSystemType(); - ErrorNumber errno; + case "dos": + _namespace = Namespace.Dos; - options ??= GetDefaultOptions(); + break; + case "nt": + _namespace = Namespace.Nt; - if(options.TryGetValue("debug", out string debugString)) - bool.TryParse(debugString, out _debug); + break; + case "os2": + _namespace = Namespace.Os2; - // Default namespace - @namespace ??= "ecs"; + break; + case "ecs": + _namespace = Namespace.Ecs; - switch(@namespace.ToLowerInvariant()) + break; + case "lfn": + _namespace = Namespace.Lfn; + + break; + case "human": + _namespace = Namespace.Human; + + break; + default: return ErrorNumber.InvalidArgument; + } + + AaruConsole.DebugWriteLine("FAT plugin", "Reading BPB"); + + uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; + + errno = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb, out byte[] bpbSector); + + if(errno != ErrorNumber.NoError) + return errno; + + BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, out BiosParameterBlockEbpb fakeBpb, + out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, + out byte minBootNearJump, out bool andosOemCorrect, out bool bootable); + + _fat12 = false; + _fat16 = false; + _fat32 = false; + _useFirstFat = true; + XmlFsType.Bootable = bootable; + + _statfs = new FileSystemInfo + { + FilenameLength = 11, + Files = 0, // Requires traversing all directories + FreeFiles = 0, + PluginId = Id, + FreeBlocks = 0 // Requires traversing the FAT + }; + + // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field. + uint sectorsPerRealSector = 1; + + // This is needed because some OSes don't put volume label as first entry in the root directory + uint sectorsForRootDirectory = 0; + uint rootDirectoryCluster = 0; + + Encoding = encoding ?? (bpbKind == BpbKind.Human ? Encoding.GetEncoding("shift_jis") + : Encoding.GetEncoding("IBM437")); + + switch(bpbKind) + { + case BpbKind.DecRainbow: + case BpbKind.Hardcoded: + case BpbKind.Msx: + case BpbKind.Apricot: + _fat12 = true; + + break; + case BpbKind.ShortFat32: + case BpbKind.LongFat32: { - case "dos": - _namespace = Namespace.Dos; + _fat32 = true; - break; - case "nt": - _namespace = Namespace.Nt; + Fat32ParameterBlock fat32Bpb = + Marshal.ByteArrayToStructureLittleEndian(bpbSector); - break; - case "os2": - _namespace = Namespace.Os2; + Fat32ParameterBlockShort shortFat32Bpb = + Marshal.ByteArrayToStructureLittleEndian(bpbSector); - break; - case "ecs": - _namespace = Namespace.Ecs; + rootDirectoryCluster = fat32Bpb.root_cluster; - break; - case "lfn": - _namespace = Namespace.Lfn; - - break; - case "human": - _namespace = Namespace.Human; - - break; - default: return ErrorNumber.InvalidArgument; - } - - AaruConsole.DebugWriteLine("FAT plugin", "Reading BPB"); - - uint sectorsPerBpb = imagePlugin.Info.SectorSize < 512 ? 512 / imagePlugin.Info.SectorSize : 1; - - errno = imagePlugin.ReadSectors(0 + partition.Start, sectorsPerBpb, out byte[] bpbSector); - - if(errno != ErrorNumber.NoError) - return errno; - - BpbKind bpbKind = DetectBpbKind(bpbSector, imagePlugin, partition, out BiosParameterBlockEbpb fakeBpb, - out HumanParameterBlock humanBpb, out AtariParameterBlock atariBpb, - out byte minBootNearJump, out bool andosOemCorrect, out bool bootable); - - _fat12 = false; - _fat16 = false; - _fat32 = false; - _useFirstFat = true; - XmlFsType.Bootable = bootable; - - _statfs = new FileSystemInfo - { - FilenameLength = 11, - Files = 0, // Requires traversing all directories - FreeFiles = 0, - PluginId = Id, - FreeBlocks = 0 // Requires traversing the FAT - }; - - // This is needed because for FAT16, GEMDOS increases bytes per sector count instead of using big_sectors field. - uint sectorsPerRealSector = 1; - - // This is needed because some OSes don't put volume label as first entry in the root directory - uint sectorsForRootDirectory = 0; - uint rootDirectoryCluster = 0; - - Encoding = encoding ?? (bpbKind == BpbKind.Human ? Encoding.GetEncoding("shift_jis") - : Encoding.GetEncoding("IBM437")); - - switch(bpbKind) - { - case BpbKind.DecRainbow: - case BpbKind.Hardcoded: - case BpbKind.Msx: - case BpbKind.Apricot: - _fat12 = true; - - break; - case BpbKind.ShortFat32: - case BpbKind.LongFat32: - { - _fat32 = true; - - Fat32ParameterBlock fat32Bpb = - Marshal.ByteArrayToStructureLittleEndian(bpbSector); - - Fat32ParameterBlockShort shortFat32Bpb = - Marshal.ByteArrayToStructureLittleEndian(bpbSector); - - rootDirectoryCluster = fat32Bpb.root_cluster; - - // This is to support FAT partitions on hybrid ISO/USB images - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - fat32Bpb.bps *= 4; - fat32Bpb.spc /= 4; - fat32Bpb.big_spfat /= 4; - fat32Bpb.hsectors /= 4; - fat32Bpb.sptrk /= 4; - } - - XmlFsType.Type = fat32Bpb.version != 0 ? "FAT+" : "FAT32"; - - if(fat32Bpb.oem_name != null && - (fat32Bpb.oem_name[5] != 0x49 || fat32Bpb.oem_name[6] != 0x48 || fat32Bpb.oem_name[7] != 0x43)) - XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); - - _sectorsPerCluster = fat32Bpb.spc; - XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); - _reservedSectors = fat32Bpb.rsectors; - - if(fat32Bpb.big_sectors == 0 && - fat32Bpb.signature == 0x28) - XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; - else if(fat32Bpb.sectors == 0) - XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; - else - XmlFsType.Clusters = (ulong)(fat32Bpb.sectors / fat32Bpb.spc); - - _sectorsPerFat = fat32Bpb.big_spfat; - XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; - - _statfs.Id = new FileSystemId - { - IsInt = true, - Serial32 = fat32Bpb.serial_no - }; - - if((fat32Bpb.flags & 0xF8) == 0x00) - if((fat32Bpb.flags & 0x01) == 0x01) - XmlFsType.Dirty = true; - - if((fat32Bpb.mirror_flags & 0x80) == 0x80) - _useFirstFat = (fat32Bpb.mirror_flags & 0xF) != 1; - - if(fat32Bpb.signature == 0x29) - { - XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fat32Bpb.volume_label, Encoding); - XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); - } - - // Check that jumps to a correct boot code position and has boot signature set. - // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... - XmlFsType.Bootable = - (fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80) || - (fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && - BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && - BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC); - - sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; - _sectorsPerCluster *= sectorsPerRealSector; - - // First root directory sector - _firstClusterSector = - ((ulong)((fat32Bpb.big_spfat * fat32Bpb.fats_no) + fat32Bpb.rsectors) * sectorsPerRealSector) - - (2 * _sectorsPerCluster); - - if(fat32Bpb.fsinfo_sector + partition.Start <= partition.End) - { - errno = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start, - out byte[] fsinfoSector); - - if(errno != ErrorNumber.NoError) - return errno; - - FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian(fsinfoSector); - - if(fsInfo.signature1 == FSINFO_SIGNATURE1 && - fsInfo.signature2 == FSINFO_SIGNATURE2 && - fsInfo.signature3 == FSINFO_SIGNATURE3) - if(fsInfo.free_clusters < 0xFFFFFFFF) - { - XmlFsType.FreeClusters = fsInfo.free_clusters; - XmlFsType.FreeClustersSpecified = true; - } - } - - break; - } - - // Some fields could overflow fake BPB, those will be handled below - case BpbKind.Atari: - { - ushort sum = 0; - - for(int i = 0; i < bpbSector.Length; i += 2) - sum += BigEndianBitConverter.ToUInt16(bpbSector, i); - - // TODO: Check this - if(sum == 0x1234) - XmlFsType.Bootable = true; - - // BGM changes the bytes per sector instead of changing the sectors per cluster. Why?! WHY!? - uint ratio = fakeBpb.bps / imagePlugin.Info.SectorSize; - fakeBpb.bps = (ushort)imagePlugin.Info.SectorSize; - fakeBpb.spc = (byte)(fakeBpb.spc * ratio); - fakeBpb.rsectors = (ushort)(fakeBpb.rsectors * ratio); - fakeBpb.big_sectors = fakeBpb.sectors * ratio; - fakeBpb.sectors = 0; - fakeBpb.spfat = (ushort)(fakeBpb.spfat * ratio); - fakeBpb.sptrk = (ushort)(fakeBpb.sptrk * ratio); - - break; - } - - case BpbKind.Human: - // If not debug set Human68k namespace and ShiftJIS codepage as defaults - if(!_debug) - _namespace = Namespace.Human; - - XmlFsType.Bootable = true; - - break; - } - - ulong firstRootSector = 0; - - if(!_fat32) - { // This is to support FAT partitions on hybrid ISO/USB images if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - fakeBpb.bps *= 4; - fakeBpb.spc /= 4; - fakeBpb.spfat /= 4; - fakeBpb.hsectors /= 4; - fakeBpb.sptrk /= 4; - fakeBpb.rsectors /= 4; - - if(fakeBpb.spc == 0) - fakeBpb.spc = 1; + fat32Bpb.bps *= 4; + fat32Bpb.spc /= 4; + fat32Bpb.big_spfat /= 4; + fat32Bpb.hsectors /= 4; + fat32Bpb.sptrk /= 4; } - ulong clusters; + XmlFsType.Type = fat32Bpb.version != 0 ? "FAT+" : "FAT32"; - if(bpbKind != BpbKind.Human) - { - int reservedSectors = fakeBpb.rsectors + (fakeBpb.fats_no * fakeBpb.spfat) + - (fakeBpb.root_ent * 32 / fakeBpb.bps); + if(fat32Bpb.oem_name != null && + (fat32Bpb.oem_name[5] != 0x49 || fat32Bpb.oem_name[6] != 0x48 || fat32Bpb.oem_name[7] != 0x43)) + XmlFsType.SystemIdentifier = StringHandlers.CToString(fat32Bpb.oem_name); - if(fakeBpb.sectors == 0) - clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.big_sectors - reservedSectors - : (fakeBpb.big_sectors - reservedSectors) / fakeBpb.spc); - else - clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors - reservedSectors - : (fakeBpb.sectors - reservedSectors) / fakeBpb.spc); - } + _sectorsPerCluster = fat32Bpb.spc; + XmlFsType.ClusterSize = (uint)(fat32Bpb.bps * fat32Bpb.spc); + _reservedSectors = fat32Bpb.rsectors; + + if(fat32Bpb.big_sectors == 0 && + fat32Bpb.signature == 0x28) + XmlFsType.Clusters = shortFat32Bpb.huge_sectors / shortFat32Bpb.spc; + else if(fat32Bpb.sectors == 0) + XmlFsType.Clusters = fat32Bpb.big_sectors / fat32Bpb.spc; else - clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters; + XmlFsType.Clusters = (ulong)(fat32Bpb.sectors / fat32Bpb.spc); - // This will walk all the FAT entries and check if they're valid FAT12 or FAT16 entries. - // If the whole table is valid in both senses, it considers the type entry in the BPB. - // BeOS is known to set the type as FAT16 but treat it as FAT12. - if(!_fat12 && - !_fat16) + _sectorsPerFat = fat32Bpb.big_spfat; + XmlFsType.VolumeSerial = $"{fat32Bpb.serial_no:X8}"; + + _statfs.Id = new FileSystemId { - if(clusters < 4089) - { - ushort[] fat12 = new ushort[clusters]; + IsInt = true, + Serial32 = fat32Bpb.serial_no + }; - _reservedSectors = fakeBpb.rsectors; - sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; - _fatFirstSector = partition.Start + (_reservedSectors * sectorsPerRealSector); + if((fat32Bpb.flags & 0xF8) == 0x00) + if((fat32Bpb.flags & 0x01) == 0x01) + XmlFsType.Dirty = true; - errno = imagePlugin.ReadSectors(_fatFirstSector, fakeBpb.spfat, out byte[] fatBytes); + if((fat32Bpb.mirror_flags & 0x80) == 0x80) + _useFirstFat = (fat32Bpb.mirror_flags & 0xF) != 1; - if(errno != ErrorNumber.NoError) - return errno; - - int pos = 0; - - for(int i = 0; i + 3 < fatBytes.Length && pos < fat12.Length; i += 3) - { - fat12[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); - - if(pos >= fat12.Length) - break; - - fat12[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); - } - - bool fat12Valid = fat12[0] >= FAT12_RESERVED && fat12[1] >= FAT12_RESERVED; - - foreach(ushort entry in fat12) - { - if(entry >= FAT12_RESERVED || - entry <= clusters) - continue; - - fat12Valid = false; - - break; - } - - ushort[] fat16 = MemoryMarshal.Cast(fatBytes).ToArray(); - - bool fat16Valid = fat16[0] >= FAT16_RESERVED && fat16[1] >= 0x3FF0; - - foreach(ushort entry in fat16) - { - if(entry >= FAT16_RESERVED || - entry <= clusters) - continue; - - fat16Valid = false; - - break; - } - - _fat12 = fat12Valid; - _fat16 = fat16Valid; - - // Check BPB type - if(_fat12 == _fat16 && - fakeBpb.fs_type != null) - { - _fat12 = Encoding.ASCII.GetString(fakeBpb.fs_type) == "FAT12 "; - _fat16 = Encoding.ASCII.GetString(fakeBpb.fs_type) == "FAT16 "; - } - - // Still undecided (fs_type is null or is not FAT1[2|6]) - if(_fat12 == _fat16) - { - _fat12 = true; - _fat16 = false; - } - } - else - _fat16 = true; - } - - if(_fat12) - XmlFsType.Type = "FAT12"; - else if(_fat16) - XmlFsType.Type = "FAT16"; - - if(bpbKind == BpbKind.Atari) + if(fat32Bpb.signature == 0x29) { - if(atariBpb.serial_no[0] != 0x49 || - atariBpb.serial_no[1] != 0x48 || - atariBpb.serial_no[2] != 0x43) - { - XmlFsType.VolumeSerial = - $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}"; - - _statfs.Id = new FileSystemId - { - IsInt = true, - Serial32 = (uint)((atariBpb.serial_no[0] << 16) + (atariBpb.serial_no[1] << 8) + - atariBpb.serial_no[2]) - }; - } - - XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name); - - if(string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) - XmlFsType.SystemIdentifier = null; + XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fat32Bpb.volume_label, Encoding); + XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); } - else if(fakeBpb.oem_name != null) - { - if(fakeBpb.oem_name[5] != 0x49 || - fakeBpb.oem_name[6] != 0x48 || - fakeBpb.oem_name[7] != 0x43) - { - // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies - // OEM ID should be ASCII, otherwise ignore it - if(fakeBpb.oem_name[0] >= 0x20 && - fakeBpb.oem_name[0] <= 0x7F && - fakeBpb.oem_name[1] >= 0x20 && - fakeBpb.oem_name[1] <= 0x7F && - fakeBpb.oem_name[2] >= 0x20 && - fakeBpb.oem_name[2] <= 0x7F && - fakeBpb.oem_name[3] >= 0x20 && - fakeBpb.oem_name[3] <= 0x7F && - fakeBpb.oem_name[4] >= 0x20 && - fakeBpb.oem_name[4] <= 0x7F && - fakeBpb.oem_name[5] >= 0x20 && - fakeBpb.oem_name[5] <= 0x7F && - fakeBpb.oem_name[6] >= 0x20 && - fakeBpb.oem_name[6] <= 0x7F && - fakeBpb.oem_name[7] >= 0x20 && - fakeBpb.oem_name[7] <= 0x7F) - XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name); - else if(fakeBpb.oem_name[0] < 0x20 && - fakeBpb.oem_name[1] >= 0x20 && - fakeBpb.oem_name[1] <= 0x7F && - fakeBpb.oem_name[2] >= 0x20 && - fakeBpb.oem_name[2] <= 0x7F && - fakeBpb.oem_name[3] >= 0x20 && - fakeBpb.oem_name[3] <= 0x7F && - fakeBpb.oem_name[4] >= 0x20 && - fakeBpb.oem_name[4] <= 0x7F && - fakeBpb.oem_name[5] >= 0x20 && - fakeBpb.oem_name[5] <= 0x7F && - fakeBpb.oem_name[6] >= 0x20 && - fakeBpb.oem_name[6] <= 0x7F && - fakeBpb.oem_name[7] >= 0x20 && - fakeBpb.oem_name[7] <= 0x7F) - XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1); - } - - if(fakeBpb.signature == 0x28 || - fakeBpb.signature == 0x29) - { - XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}"; - - _statfs.Id = new FileSystemId - { - IsInt = true, - Serial32 = fakeBpb.serial_no - }; - } - } - - XmlFsType.Clusters = clusters; - _sectorsPerCluster = fakeBpb.spc; - XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc); - _reservedSectors = fakeBpb.rsectors; - _sectorsPerFat = fakeBpb.spfat; - - if(fakeBpb.signature == 0x28 || - fakeBpb.signature == 0x29 || - andosOemCorrect) - { - if((fakeBpb.flags & 0xF8) == 0x00) - if((fakeBpb.flags & 0x01) == 0x01) - XmlFsType.Dirty = true; - - if(fakeBpb.signature == 0x29 || andosOemCorrect) - { - XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fakeBpb.volume_label, Encoding); - XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); - } - } - - // Workaround that PCExchange jumps into "FAT16 "... - if(XmlFsType.SystemIdentifier == "PCX 2.0 ") - fakeBpb.jump[1] += 8; // Check that jumps to a correct boot code position and has boot signature set. // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... - if(XmlFsType.Bootable == false && - fakeBpb.jump != null) - XmlFsType.Bootable |= - (fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80) || - (fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 && - BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump && - BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC); + XmlFsType.Bootable = + (fat32Bpb.jump[0] == 0xEB && fat32Bpb.jump[1] >= minBootNearJump && fat32Bpb.jump[1] < 0x80) || + (fat32Bpb.jump[0] == 0xE9 && fat32Bpb.jump.Length >= 3 && + BitConverter.ToUInt16(fat32Bpb.jump, 1) >= minBootNearJump && + BitConverter.ToUInt16(fat32Bpb.jump, 1) <= 0x1FC); + + sectorsPerRealSector = fat32Bpb.bps / imagePlugin.Info.SectorSize; + _sectorsPerCluster *= sectorsPerRealSector; // First root directory sector - firstRootSector = - ((ulong)((fakeBpb.spfat * fakeBpb.fats_no) + fakeBpb.rsectors) * sectorsPerRealSector) + - partition.Start; + _firstClusterSector = + ((ulong)((fat32Bpb.big_spfat * fat32Bpb.fats_no) + fat32Bpb.rsectors) * sectorsPerRealSector) - + (2 * _sectorsPerCluster); - sectorsForRootDirectory = (uint)(fakeBpb.root_ent * 32 / imagePlugin.Info.SectorSize); - - sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; - _sectorsPerCluster *= sectorsPerRealSector; - } - - _firstClusterSector += partition.Start; - - _image = imagePlugin; - - if(_fat32) - _fatEntriesPerSector = imagePlugin.Info.SectorSize / 4; - else if(_fat16) - _fatEntriesPerSector = imagePlugin.Info.SectorSize / 2; - else - _fatEntriesPerSector = imagePlugin.Info.SectorSize * 2 / 3; - - _fatFirstSector = partition.Start + (_reservedSectors * sectorsPerRealSector); - - _rootDirectoryCache = new Dictionary(); - byte[] rootDirectory; - - if(!_fat32) - { - _firstClusterSector = firstRootSector + sectorsForRootDirectory - (_sectorsPerCluster * 2); - errno = imagePlugin.ReadSectors(firstRootSector, sectorsForRootDirectory, out rootDirectory); - - if(errno != ErrorNumber.NoError) - return errno; - - if(bpbKind == BpbKind.DecRainbow) + if(fat32Bpb.fsinfo_sector + partition.Start <= partition.End) { - var rootMs = new MemoryStream(); - - foreach(ulong rootSector in new[] - { - 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 - }) - { - errno = imagePlugin.ReadSector(rootSector, out byte[] tmp); - - if(errno != ErrorNumber.NoError) - return errno; - - rootMs.Write(tmp, 0, tmp.Length); - } - - rootDirectory = rootMs.ToArray(); - } - } - else - { - if(rootDirectoryCluster == 0) - return ErrorNumber.InvalidArgument; - - var rootMs = new MemoryStream(); - uint[] rootDirectoryClusters = GetClusters(rootDirectoryCluster); - - foreach(uint cluster in rootDirectoryClusters) - { - errno = imagePlugin.ReadSectors(_firstClusterSector + (cluster * _sectorsPerCluster), - _sectorsPerCluster, out byte[] buffer); + errno = imagePlugin.ReadSector(fat32Bpb.fsinfo_sector + partition.Start, + out byte[] fsinfoSector); if(errno != ErrorNumber.NoError) return errno; - rootMs.Write(buffer, 0, buffer.Length); + FsInfoSector fsInfo = Marshal.ByteArrayToStructureLittleEndian(fsinfoSector); + + if(fsInfo.signature1 == FSINFO_SIGNATURE1 && + fsInfo.signature2 == FSINFO_SIGNATURE2 && + fsInfo.signature3 == FSINFO_SIGNATURE3) + if(fsInfo.free_clusters < 0xFFFFFFFF) + { + XmlFsType.FreeClusters = fsInfo.free_clusters; + XmlFsType.FreeClustersSpecified = true; + } + } + + break; + } + + // Some fields could overflow fake BPB, those will be handled below + case BpbKind.Atari: + { + ushort sum = 0; + + for(int i = 0; i < bpbSector.Length; i += 2) + sum += BigEndianBitConverter.ToUInt16(bpbSector, i); + + // TODO: Check this + if(sum == 0x1234) + XmlFsType.Bootable = true; + + // BGM changes the bytes per sector instead of changing the sectors per cluster. Why?! WHY!? + uint ratio = fakeBpb.bps / imagePlugin.Info.SectorSize; + fakeBpb.bps = (ushort)imagePlugin.Info.SectorSize; + fakeBpb.spc = (byte)(fakeBpb.spc * ratio); + fakeBpb.rsectors = (ushort)(fakeBpb.rsectors * ratio); + fakeBpb.big_sectors = fakeBpb.sectors * ratio; + fakeBpb.sectors = 0; + fakeBpb.spfat = (ushort)(fakeBpb.spfat * ratio); + fakeBpb.sptrk = (ushort)(fakeBpb.sptrk * ratio); + + break; + } + + case BpbKind.Human: + // If not debug set Human68k namespace and ShiftJIS codepage as defaults + if(!_debug) + _namespace = Namespace.Human; + + XmlFsType.Bootable = true; + + break; + } + + ulong firstRootSector = 0; + + if(!_fat32) + { + // This is to support FAT partitions on hybrid ISO/USB images + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) + { + fakeBpb.bps *= 4; + fakeBpb.spc /= 4; + fakeBpb.spfat /= 4; + fakeBpb.hsectors /= 4; + fakeBpb.sptrk /= 4; + fakeBpb.rsectors /= 4; + + if(fakeBpb.spc == 0) + fakeBpb.spc = 1; + } + + ulong clusters; + + if(bpbKind != BpbKind.Human) + { + int reservedSectors = fakeBpb.rsectors + (fakeBpb.fats_no * fakeBpb.spfat) + + (fakeBpb.root_ent * 32 / fakeBpb.bps); + + if(fakeBpb.sectors == 0) + clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.big_sectors - reservedSectors + : (fakeBpb.big_sectors - reservedSectors) / fakeBpb.spc); + else + clusters = (ulong)(fakeBpb.spc == 0 ? fakeBpb.sectors - reservedSectors + : (fakeBpb.sectors - reservedSectors) / fakeBpb.spc); + } + else + clusters = humanBpb.clusters == 0 ? humanBpb.big_clusters : humanBpb.clusters; + + // This will walk all the FAT entries and check if they're valid FAT12 or FAT16 entries. + // If the whole table is valid in both senses, it considers the type entry in the BPB. + // BeOS is known to set the type as FAT16 but treat it as FAT12. + if(!_fat12 && + !_fat16) + { + if(clusters < 4089) + { + ushort[] fat12 = new ushort[clusters]; + + _reservedSectors = fakeBpb.rsectors; + sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; + _fatFirstSector = partition.Start + (_reservedSectors * sectorsPerRealSector); + + errno = imagePlugin.ReadSectors(_fatFirstSector, fakeBpb.spfat, out byte[] fatBytes); + + if(errno != ErrorNumber.NoError) + return errno; + + int pos = 0; + + for(int i = 0; i + 3 < fatBytes.Length && pos < fat12.Length; i += 3) + { + fat12[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); + + if(pos >= fat12.Length) + break; + + fat12[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); + } + + bool fat12Valid = fat12[0] >= FAT12_RESERVED && fat12[1] >= FAT12_RESERVED; + + foreach(ushort entry in fat12) + { + if(entry >= FAT12_RESERVED || + entry <= clusters) + continue; + + fat12Valid = false; + + break; + } + + ushort[] fat16 = MemoryMarshal.Cast(fatBytes).ToArray(); + + bool fat16Valid = fat16[0] >= FAT16_RESERVED && fat16[1] >= 0x3FF0; + + foreach(ushort entry in fat16) + { + if(entry >= FAT16_RESERVED || + entry <= clusters) + continue; + + fat16Valid = false; + + break; + } + + _fat12 = fat12Valid; + _fat16 = fat16Valid; + + // Check BPB type + if(_fat12 == _fat16 && + fakeBpb.fs_type != null) + { + _fat12 = Encoding.ASCII.GetString(fakeBpb.fs_type) == "FAT12 "; + _fat16 = Encoding.ASCII.GetString(fakeBpb.fs_type) == "FAT16 "; + } + + // Still undecided (fs_type is null or is not FAT1[2|6]) + if(_fat12 == _fat16) + { + _fat12 = true; + _fat16 = false; + } + } + else + _fat16 = true; + } + + if(_fat12) + XmlFsType.Type = "FAT12"; + else if(_fat16) + XmlFsType.Type = "FAT16"; + + if(bpbKind == BpbKind.Atari) + { + if(atariBpb.serial_no[0] != 0x49 || + atariBpb.serial_no[1] != 0x48 || + atariBpb.serial_no[2] != 0x43) + { + XmlFsType.VolumeSerial = + $"{atariBpb.serial_no[0]:X2}{atariBpb.serial_no[1]:X2}{atariBpb.serial_no[2]:X2}"; + + _statfs.Id = new FileSystemId + { + IsInt = true, + Serial32 = (uint)((atariBpb.serial_no[0] << 16) + (atariBpb.serial_no[1] << 8) + + atariBpb.serial_no[2]) + }; + } + + XmlFsType.SystemIdentifier = StringHandlers.CToString(atariBpb.oem_name); + + if(string.IsNullOrEmpty(XmlFsType.SystemIdentifier)) + XmlFsType.SystemIdentifier = null; + } + else if(fakeBpb.oem_name != null) + { + if(fakeBpb.oem_name[5] != 0x49 || + fakeBpb.oem_name[6] != 0x48 || + fakeBpb.oem_name[7] != 0x43) + { + // Later versions of Windows create a DOS 3 BPB without OEM name on 8 sectors/track floppies + // OEM ID should be ASCII, otherwise ignore it + if(fakeBpb.oem_name[0] >= 0x20 && + fakeBpb.oem_name[0] <= 0x7F && + fakeBpb.oem_name[1] >= 0x20 && + fakeBpb.oem_name[1] <= 0x7F && + fakeBpb.oem_name[2] >= 0x20 && + fakeBpb.oem_name[2] <= 0x7F && + fakeBpb.oem_name[3] >= 0x20 && + fakeBpb.oem_name[3] <= 0x7F && + fakeBpb.oem_name[4] >= 0x20 && + fakeBpb.oem_name[4] <= 0x7F && + fakeBpb.oem_name[5] >= 0x20 && + fakeBpb.oem_name[5] <= 0x7F && + fakeBpb.oem_name[6] >= 0x20 && + fakeBpb.oem_name[6] <= 0x7F && + fakeBpb.oem_name[7] >= 0x20 && + fakeBpb.oem_name[7] <= 0x7F) + XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name); + else if(fakeBpb.oem_name[0] < 0x20 && + fakeBpb.oem_name[1] >= 0x20 && + fakeBpb.oem_name[1] <= 0x7F && + fakeBpb.oem_name[2] >= 0x20 && + fakeBpb.oem_name[2] <= 0x7F && + fakeBpb.oem_name[3] >= 0x20 && + fakeBpb.oem_name[3] <= 0x7F && + fakeBpb.oem_name[4] >= 0x20 && + fakeBpb.oem_name[4] <= 0x7F && + fakeBpb.oem_name[5] >= 0x20 && + fakeBpb.oem_name[5] <= 0x7F && + fakeBpb.oem_name[6] >= 0x20 && + fakeBpb.oem_name[6] <= 0x7F && + fakeBpb.oem_name[7] >= 0x20 && + fakeBpb.oem_name[7] <= 0x7F) + XmlFsType.SystemIdentifier = StringHandlers.CToString(fakeBpb.oem_name, Encoding, start: 1); + } + + if(fakeBpb.signature == 0x28 || + fakeBpb.signature == 0x29) + { + XmlFsType.VolumeSerial = $"{fakeBpb.serial_no:X8}"; + + _statfs.Id = new FileSystemId + { + IsInt = true, + Serial32 = fakeBpb.serial_no + }; + } + } + + XmlFsType.Clusters = clusters; + _sectorsPerCluster = fakeBpb.spc; + XmlFsType.ClusterSize = (uint)(fakeBpb.bps * fakeBpb.spc); + _reservedSectors = fakeBpb.rsectors; + _sectorsPerFat = fakeBpb.spfat; + + if(fakeBpb.signature == 0x28 || + fakeBpb.signature == 0x29 || + andosOemCorrect) + { + if((fakeBpb.flags & 0xF8) == 0x00) + if((fakeBpb.flags & 0x01) == 0x01) + XmlFsType.Dirty = true; + + if(fakeBpb.signature == 0x29 || andosOemCorrect) + { + XmlFsType.VolumeName = StringHandlers.SpacePaddedToString(fakeBpb.volume_label, Encoding); + XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); + } + } + + // Workaround that PCExchange jumps into "FAT16 "... + if(XmlFsType.SystemIdentifier == "PCX 2.0 ") + fakeBpb.jump[1] += 8; + + // Check that jumps to a correct boot code position and has boot signature set. + // This will mean that the volume will boot, even if just to say "this is not bootable change disk"...... + if(XmlFsType.Bootable == false && + fakeBpb.jump != null) + XmlFsType.Bootable |= + (fakeBpb.jump[0] == 0xEB && fakeBpb.jump[1] >= minBootNearJump && fakeBpb.jump[1] < 0x80) || + (fakeBpb.jump[0] == 0xE9 && fakeBpb.jump.Length >= 3 && + BitConverter.ToUInt16(fakeBpb.jump, 1) >= minBootNearJump && + BitConverter.ToUInt16(fakeBpb.jump, 1) <= 0x1FC); + + // First root directory sector + firstRootSector = + ((ulong)((fakeBpb.spfat * fakeBpb.fats_no) + fakeBpb.rsectors) * sectorsPerRealSector) + + partition.Start; + + sectorsForRootDirectory = (uint)(fakeBpb.root_ent * 32 / imagePlugin.Info.SectorSize); + + sectorsPerRealSector = fakeBpb.bps / imagePlugin.Info.SectorSize; + _sectorsPerCluster *= sectorsPerRealSector; + } + + _firstClusterSector += partition.Start; + + _image = imagePlugin; + + if(_fat32) + _fatEntriesPerSector = imagePlugin.Info.SectorSize / 4; + else if(_fat16) + _fatEntriesPerSector = imagePlugin.Info.SectorSize / 2; + else + _fatEntriesPerSector = imagePlugin.Info.SectorSize * 2 / 3; + + _fatFirstSector = partition.Start + (_reservedSectors * sectorsPerRealSector); + + _rootDirectoryCache = new Dictionary(); + byte[] rootDirectory; + + if(!_fat32) + { + _firstClusterSector = firstRootSector + sectorsForRootDirectory - (_sectorsPerCluster * 2); + errno = imagePlugin.ReadSectors(firstRootSector, sectorsForRootDirectory, out rootDirectory); + + if(errno != ErrorNumber.NoError) + return errno; + + if(bpbKind == BpbKind.DecRainbow) + { + var rootMs = new MemoryStream(); + + foreach(ulong rootSector in new[] + { + 0x17, 0x19, 0x1B, 0x1D, 0x1E, 0x20 + }) + { + errno = imagePlugin.ReadSector(rootSector, out byte[] tmp); + + if(errno != ErrorNumber.NoError) + return errno; + + rootMs.Write(tmp, 0, tmp.Length); } rootDirectory = rootMs.ToArray(); - - // OS/2 FAT32.IFS uses LFN instead of .LONGNAME - if(_namespace == Namespace.Os2) - _namespace = Namespace.Lfn; } - - if(rootDirectory is null) + } + else + { + if(rootDirectoryCluster == 0) return ErrorNumber.InvalidArgument; - byte[] lastLfnName = null; - byte lastLfnChecksum = 0; + var rootMs = new MemoryStream(); + uint[] rootDirectoryClusters = GetClusters(rootDirectoryCluster); - for(int i = 0; i < rootDirectory.Length; i += Marshal.SizeOf()) + foreach(uint cluster in rootDirectoryClusters) { - DirectoryEntry entry = - Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, - Marshal.SizeOf()); + errno = imagePlugin.ReadSectors(_firstClusterSector + (cluster * _sectorsPerCluster), + _sectorsPerCluster, out byte[] buffer); - if(entry.filename[0] == DIRENT_FINISHED) - break; + if(errno != ErrorNumber.NoError) + return errno; - if(entry.attributes.HasFlag(FatAttributes.LFN)) - { - if(_namespace != Namespace.Lfn && - _namespace != Namespace.Ecs) - continue; + rootMs.Write(buffer, 0, buffer.Length); + } - LfnEntry lfnEntry = - Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, - Marshal.SizeOf()); + rootDirectory = rootMs.ToArray(); - int lfnSequence = lfnEntry.sequence & LFN_MASK; + // OS/2 FAT32.IFS uses LFN instead of .LONGNAME + if(_namespace == Namespace.Os2) + _namespace = Namespace.Lfn; + } - if((lfnEntry.sequence & LFN_ERASED) > 0) - continue; + if(rootDirectory is null) + return ErrorNumber.InvalidArgument; - if((lfnEntry.sequence & LFN_LAST) > 0) - { - lastLfnName = new byte[lfnSequence * 26]; - lastLfnChecksum = lfnEntry.checksum; - } + byte[] lastLfnName = null; + byte lastLfnChecksum = 0; - if(lastLfnName is null) - continue; + for(int i = 0; i < rootDirectory.Length; i += Marshal.SizeOf()) + { + DirectoryEntry entry = + Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, + Marshal.SizeOf()); - if(lfnEntry.checksum != lastLfnChecksum) - continue; - - lfnSequence--; - - Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10); - Array.Copy(lfnEntry.name2, 0, lastLfnName, (lfnSequence * 26) + 10, 12); - Array.Copy(lfnEntry.name3, 0, lastLfnName, (lfnSequence * 26) + 22, 4); + if(entry.filename[0] == DIRENT_FINISHED) + break; + if(entry.attributes.HasFlag(FatAttributes.LFN)) + { + if(_namespace != Namespace.Lfn && + _namespace != Namespace.Ecs) continue; + + LfnEntry lfnEntry = + Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, + Marshal.SizeOf()); + + int lfnSequence = lfnEntry.sequence & LFN_MASK; + + if((lfnEntry.sequence & LFN_ERASED) > 0) + continue; + + if((lfnEntry.sequence & LFN_LAST) > 0) + { + lastLfnName = new byte[lfnSequence * 26]; + lastLfnChecksum = lfnEntry.checksum; } - // Not a correct entry - if(entry.filename[0] < DIRENT_MIN && - entry.filename[0] != DIRENT_E5) + if(lastLfnName is null) continue; - // Self - if(Encoding.GetString(entry.filename).TrimEnd() == ".") + if(lfnEntry.checksum != lastLfnChecksum) continue; - // Parent - if(Encoding.GetString(entry.filename).TrimEnd() == "..") - continue; + lfnSequence--; - // Deleted - if(entry.filename[0] == DIRENT_DELETED) - continue; + Array.Copy(lfnEntry.name1, 0, lastLfnName, lfnSequence * 26, 10); + Array.Copy(lfnEntry.name2, 0, lastLfnName, (lfnSequence * 26) + 10, 12); + Array.Copy(lfnEntry.name3, 0, lastLfnName, (lfnSequence * 26) + 22, 4); - string filename; + continue; + } - if(entry.attributes.HasFlag(FatAttributes.VolumeLabel)) + // Not a correct entry + if(entry.filename[0] < DIRENT_MIN && + entry.filename[0] != DIRENT_E5) + continue; + + // Self + if(Encoding.GetString(entry.filename).TrimEnd() == ".") + continue; + + // Parent + if(Encoding.GetString(entry.filename).TrimEnd() == "..") + continue; + + // Deleted + if(entry.filename[0] == DIRENT_DELETED) + continue; + + string filename; + + if(entry.attributes.HasFlag(FatAttributes.VolumeLabel)) + { + byte[] fullname = new byte[11]; + Array.Copy(entry.filename, 0, fullname, 0, 8); + Array.Copy(entry.extension, 0, fullname, 8, 3); + string volname = Encoding.GetString(fullname).Trim(); + + if(!string.IsNullOrEmpty(volname)) + XmlFsType.VolumeName = + entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) && _namespace == Namespace.Nt + ? volname.ToLower() : volname; + + XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); + + if(entry.ctime > 0 && + entry.cdate > 0) { - byte[] fullname = new byte[11]; - Array.Copy(entry.filename, 0, fullname, 0, 8); - Array.Copy(entry.extension, 0, fullname, 8, 3); - string volname = Encoding.GetString(fullname).Trim(); + XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); - if(!string.IsNullOrEmpty(volname)) - XmlFsType.VolumeName = - entry.caseinfo.HasFlag(CaseInfo.AllLowerCase) && _namespace == Namespace.Nt - ? volname.ToLower() : volname; + if(entry.ctime_ms > 0) + XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); - XmlFsType.VolumeName = XmlFsType.VolumeName?.Replace("\0", ""); - - if(entry.ctime > 0 && - entry.cdate > 0) - { - XmlFsType.CreationDate = DateHandlers.DosToDateTime(entry.cdate, entry.ctime); - - if(entry.ctime_ms > 0) - XmlFsType.CreationDate = XmlFsType.CreationDate.AddMilliseconds(entry.ctime_ms * 10); - - XmlFsType.CreationDateSpecified = true; - } - - if(entry.mtime > 0 && - entry.mdate > 0) - { - XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); - XmlFsType.ModificationDateSpecified = true; - } - - continue; + XmlFsType.CreationDateSpecified = true; } - var completeEntry = new CompleteDirectoryEntry + if(entry.mtime > 0 && + entry.mdate > 0) { - Dirent = entry - }; - - if((_namespace == Namespace.Lfn || _namespace == Namespace.Ecs) && - lastLfnName != null) - { - byte calculatedLfnChecksum = LfnChecksum(entry.filename, entry.extension); - - if(calculatedLfnChecksum == lastLfnChecksum) - { - filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true); - - completeEntry.Lfn = filename; - lastLfnName = null; - lastLfnChecksum = 0; - } + XmlFsType.ModificationDate = DateHandlers.DosToDateTime(entry.mdate, entry.mtime); + XmlFsType.ModificationDateSpecified = true; } - if(entry.filename[0] == DIRENT_E5) - entry.filename[0] = DIRENT_DELETED; + continue; + } - string name = Encoding.GetString(entry.filename).TrimEnd(); - string extension = Encoding.GetString(entry.extension).TrimEnd(); + var completeEntry = new CompleteDirectoryEntry + { + Dirent = entry + }; - if(_namespace == Namespace.Nt) + if((_namespace == Namespace.Lfn || _namespace == Namespace.Ecs) && + lastLfnName != null) + { + byte calculatedLfnChecksum = LfnChecksum(entry.filename, entry.extension); + + if(calculatedLfnChecksum == lastLfnChecksum) { - if(entry.caseinfo.HasFlag(CaseInfo.LowerCaseExtension)) - extension = extension.ToLower(CultureInfo.CurrentCulture); + filename = StringHandlers.CToString(lastLfnName, Encoding.Unicode, true); - if(entry.caseinfo.HasFlag(CaseInfo.LowerCaseBasename)) - name = name.ToLower(CultureInfo.CurrentCulture); + completeEntry.Lfn = filename; + lastLfnName = null; + lastLfnChecksum = 0; } + } + + if(entry.filename[0] == DIRENT_E5) + entry.filename[0] = DIRENT_DELETED; + + string name = Encoding.GetString(entry.filename).TrimEnd(); + string extension = Encoding.GetString(entry.extension).TrimEnd(); + + if(_namespace == Namespace.Nt) + { + if(entry.caseinfo.HasFlag(CaseInfo.LowerCaseExtension)) + extension = extension.ToLower(CultureInfo.CurrentCulture); + + if(entry.caseinfo.HasFlag(CaseInfo.LowerCaseBasename)) + name = name.ToLower(CultureInfo.CurrentCulture); + } + + if(extension != "") + filename = name + "." + extension; + else + filename = name; + + if(name == "" && + extension == "") + { + AaruConsole.DebugWriteLine("FAT filesystem", "Found empty filename in root directory"); + + if(!_debug || + (entry.size > 0 && entry.start_cluster == 0)) + continue; // Skip invalid name + + // If debug, add it + name = ":{EMPTYNAME}:"; + + // Try to create a unique filename with an extension from 000 to 999 + for(int uniq = 0; uniq < 1000; uniq++) + { + extension = $"{uniq:D03}"; + + if(!_rootDirectoryCache.ContainsKey($"{name}.{extension}")) + break; + } + + // If we couldn't find it, just skip over + if(_rootDirectoryCache.ContainsKey($"{name}.{extension}")) + continue; + } + + // Atari ST allows slash AND colon so cannot simply substitute one for the other like in Mac filesystems + filename = filename.Replace('/', '\u2215'); + + completeEntry.Shortname = filename; + + if(_namespace == Namespace.Human) + { + HumanDirectoryEntry humanEntry = + Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, + Marshal.SizeOf()); + + completeEntry.HumanDirent = humanEntry; + + name = StringHandlers.CToString(humanEntry.name1, Encoding).TrimEnd(); + extension = StringHandlers.CToString(humanEntry.extension, Encoding).TrimEnd(); + string name2 = StringHandlers.CToString(humanEntry.name2, Encoding).TrimEnd(); if(extension != "") - filename = name + "." + extension; + filename = name + name2 + "." + extension; else - filename = name; + filename = name + name2; - if(name == "" && - extension == "") - { - AaruConsole.DebugWriteLine("FAT filesystem", "Found empty filename in root directory"); + completeEntry.HumanName = filename; + } - if(!_debug || - (entry.size > 0 && entry.start_cluster == 0)) - continue; // Skip invalid name + if(!_fat32 && + filename == "EA DATA. SF") + { + _eaDirEntry = entry; + lastLfnName = null; + lastLfnChecksum = 0; - // If debug, add it - name = ":{EMPTYNAME}:"; + if(_debug) + _rootDirectoryCache[completeEntry.ToString()] = completeEntry; - // Try to create a unique filename with an extension from 000 to 999 - for(int uniq = 0; uniq < 1000; uniq++) - { - extension = $"{uniq:D03}"; + continue; + } - if(!_rootDirectoryCache.ContainsKey($"{name}.{extension}")) - break; - } + _rootDirectoryCache[completeEntry.ToString()] = completeEntry; + lastLfnName = null; + lastLfnChecksum = 0; + } - // If we couldn't find it, just skip over - if(_rootDirectoryCache.ContainsKey($"{name}.{extension}")) - continue; - } + XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim(); + _statfs.Blocks = XmlFsType.Clusters; - // Atari ST allows slash AND colon so cannot simply substitute one for the other like in Mac filesystems - filename = filename.Replace('/', '\u2215'); + switch(bpbKind) + { + case BpbKind.Hardcoded: + _statfs.Type = $"Microsoft FAT{(_fat16 ? "16" : "12")}"; - completeEntry.Shortname = filename; + break; + case BpbKind.Atari: + _statfs.Type = $"Atari FAT{(_fat16 ? "16" : "12")}"; - if(_namespace == Namespace.Human) - { - HumanDirectoryEntry humanEntry = - Marshal.ByteArrayToStructureLittleEndian(rootDirectory, i, - Marshal.SizeOf()); + break; + case BpbKind.Msx: + _statfs.Type = $"MSX FAT{(_fat16 ? "16" : "12")}"; - completeEntry.HumanDirent = humanEntry; + break; + case BpbKind.Dos2: + case BpbKind.Dos3: + case BpbKind.Dos32: + case BpbKind.Dos33: + case BpbKind.ShortExtended: + case BpbKind.Extended: + _statfs.Type = $"Microsoft FAT{(_fat16 ? "16" : "12")}"; - name = StringHandlers.CToString(humanEntry.name1, Encoding).TrimEnd(); - extension = StringHandlers.CToString(humanEntry.extension, Encoding).TrimEnd(); - string name2 = StringHandlers.CToString(humanEntry.name2, Encoding).TrimEnd(); + break; + case BpbKind.ShortFat32: + case BpbKind.LongFat32: + _statfs.Type = XmlFsType.Type == "FAT+" ? "FAT+" : "Microsoft FAT32"; - if(extension != "") - filename = name + name2 + "." + extension; - else - filename = name + name2; + break; + case BpbKind.Andos: + _statfs.Type = $"ANDOS FAT{(_fat16 ? "16" : "12")}"; - completeEntry.HumanName = filename; - } + break; + case BpbKind.Apricot: + _statfs.Type = $"Apricot FAT{(_fat16 ? "16" : "12")}"; - if(!_fat32 && - filename == "EA DATA. SF") - { - _eaDirEntry = entry; - lastLfnName = null; - lastLfnChecksum = 0; + break; + case BpbKind.DecRainbow: + _statfs.Type = $"DEC FAT{(_fat16 ? "16" : "12")}"; - if(_debug) - _rootDirectoryCache[completeEntry.ToString()] = completeEntry; + break; + case BpbKind.Human: + _statfs.Type = $"Human68k FAT{(_fat16 ? "16" : "12")}"; + break; + default: throw new ArgumentOutOfRangeException(); + } + + _bytesPerCluster = _sectorsPerCluster * imagePlugin.Info.SectorSize; + + ushort[] firstFatEntries = new ushort[_statfs.Blocks]; + ushort[] secondFatEntries = new ushort[_statfs.Blocks]; + bool firstFatValid = true; + bool secondFatValid = true; + + if(_fat12) + { + AaruConsole.DebugWriteLine("FAT plugin", "Reading FAT12"); + + errno = imagePlugin.ReadSectors(_fatFirstSector, _sectorsPerFat, out byte[] fatBytes); + + if(errno != ErrorNumber.NoError) + return errno; + + int pos = 0; + + for(int i = 0; i + 3 < fatBytes.Length && pos < firstFatEntries.Length; i += 3) + { + firstFatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); + + if(pos >= firstFatEntries.Length) + break; + + firstFatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); + } + + errno = imagePlugin.ReadSectors(_fatFirstSector + _sectorsPerFat, _sectorsPerFat, out fatBytes); + + if(errno != ErrorNumber.NoError) + return errno; + + _fatEntries = new ushort[_statfs.Blocks]; + + pos = 0; + + for(int i = 0; i + 3 < fatBytes.Length && pos < secondFatEntries.Length; i += 3) + { + secondFatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); + + if(pos >= secondFatEntries.Length) + break; + + secondFatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); + } + + foreach(ushort entry in firstFatEntries) + { + if(entry >= FAT12_RESERVED || + entry <= _statfs.Blocks) continue; - } - _rootDirectoryCache[completeEntry.ToString()] = completeEntry; - lastLfnName = null; - lastLfnChecksum = 0; + firstFatValid = false; + + break; } - XmlFsType.VolumeName = XmlFsType.VolumeName?.Trim(); - _statfs.Blocks = XmlFsType.Clusters; - - switch(bpbKind) + foreach(ushort entry in secondFatEntries) { - case BpbKind.Hardcoded: - _statfs.Type = $"Microsoft FAT{(_fat16 ? "16" : "12")}"; + if(entry >= FAT12_RESERVED || + entry <= _statfs.Blocks) + continue; - break; - case BpbKind.Atari: - _statfs.Type = $"Atari FAT{(_fat16 ? "16" : "12")}"; + secondFatValid = false; - break; - case BpbKind.Msx: - _statfs.Type = $"MSX FAT{(_fat16 ? "16" : "12")}"; - - break; - case BpbKind.Dos2: - case BpbKind.Dos3: - case BpbKind.Dos32: - case BpbKind.Dos33: - case BpbKind.ShortExtended: - case BpbKind.Extended: - _statfs.Type = $"Microsoft FAT{(_fat16 ? "16" : "12")}"; - - break; - case BpbKind.ShortFat32: - case BpbKind.LongFat32: - _statfs.Type = XmlFsType.Type == "FAT+" ? "FAT+" : "Microsoft FAT32"; - - break; - case BpbKind.Andos: - _statfs.Type = $"ANDOS FAT{(_fat16 ? "16" : "12")}"; - - break; - case BpbKind.Apricot: - _statfs.Type = $"Apricot FAT{(_fat16 ? "16" : "12")}"; - - break; - case BpbKind.DecRainbow: - _statfs.Type = $"DEC FAT{(_fat16 ? "16" : "12")}"; - - break; - case BpbKind.Human: - _statfs.Type = $"Human68k FAT{(_fat16 ? "16" : "12")}"; - - break; - default: throw new ArgumentOutOfRangeException(); + break; } - _bytesPerCluster = _sectorsPerCluster * imagePlugin.Info.SectorSize; + if(firstFatValid == secondFatValid) + _fatEntries = _useFirstFat ? firstFatEntries : secondFatEntries; + else if(firstFatValid) + _fatEntries = firstFatEntries; + else + _fatEntries = secondFatEntries; + } + else if(_fat16) + { + AaruConsole.DebugWriteLine("FAT plugin", "Reading FAT16"); - ushort[] firstFatEntries = new ushort[_statfs.Blocks]; - ushort[] secondFatEntries = new ushort[_statfs.Blocks]; - bool firstFatValid = true; - bool secondFatValid = true; + errno = imagePlugin.ReadSectors(_fatFirstSector, _sectorsPerFat, out byte[] fatBytes); - if(_fat12) + if(errno != ErrorNumber.NoError) + return errno; + + AaruConsole.DebugWriteLine("FAT plugin", "Casting FAT"); + firstFatEntries = MemoryMarshal.Cast(fatBytes).ToArray(); + + errno = imagePlugin.ReadSectors(_fatFirstSector + _sectorsPerFat, _sectorsPerFat, out fatBytes); + + if(errno != ErrorNumber.NoError) + return errno; + + AaruConsole.DebugWriteLine("FAT plugin", "Casting FAT"); + secondFatEntries = MemoryMarshal.Cast(fatBytes).ToArray(); + + foreach(ushort entry in firstFatEntries) { - AaruConsole.DebugWriteLine("FAT plugin", "Reading FAT12"); + if(entry >= FAT16_RESERVED || + entry <= _statfs.Blocks) + continue; - errno = imagePlugin.ReadSectors(_fatFirstSector, _sectorsPerFat, out byte[] fatBytes); + firstFatValid = false; - if(errno != ErrorNumber.NoError) - return errno; - - int pos = 0; - - for(int i = 0; i + 3 < fatBytes.Length && pos < firstFatEntries.Length; i += 3) - { - firstFatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); - - if(pos >= firstFatEntries.Length) - break; - - firstFatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); - } - - errno = imagePlugin.ReadSectors(_fatFirstSector + _sectorsPerFat, _sectorsPerFat, out fatBytes); - - if(errno != ErrorNumber.NoError) - return errno; - - _fatEntries = new ushort[_statfs.Blocks]; - - pos = 0; - - for(int i = 0; i + 3 < fatBytes.Length && pos < secondFatEntries.Length; i += 3) - { - secondFatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF) << 8) + fatBytes[i + 0]); - - if(pos >= secondFatEntries.Length) - break; - - secondFatEntries[pos++] = (ushort)(((fatBytes[i + 1] & 0xF0) >> 4) + (fatBytes[i + 2] << 4)); - } - - foreach(ushort entry in firstFatEntries) - { - if(entry >= FAT12_RESERVED || - entry <= _statfs.Blocks) - continue; - - firstFatValid = false; - - break; - } - - foreach(ushort entry in secondFatEntries) - { - if(entry >= FAT12_RESERVED || - entry <= _statfs.Blocks) - continue; - - secondFatValid = false; - - break; - } - - if(firstFatValid == secondFatValid) - _fatEntries = _useFirstFat ? firstFatEntries : secondFatEntries; - else if(firstFatValid) - _fatEntries = firstFatEntries; - else - _fatEntries = secondFatEntries; + break; } - else if(_fat16) + + foreach(ushort entry in secondFatEntries) { - AaruConsole.DebugWriteLine("FAT plugin", "Reading FAT16"); + if(entry >= FAT16_RESERVED || + entry <= _statfs.Blocks) + continue; - errno = imagePlugin.ReadSectors(_fatFirstSector, _sectorsPerFat, out byte[] fatBytes); + secondFatValid = false; - if(errno != ErrorNumber.NoError) - return errno; - - AaruConsole.DebugWriteLine("FAT plugin", "Casting FAT"); - firstFatEntries = MemoryMarshal.Cast(fatBytes).ToArray(); - - errno = imagePlugin.ReadSectors(_fatFirstSector + _sectorsPerFat, _sectorsPerFat, out fatBytes); - - if(errno != ErrorNumber.NoError) - return errno; - - AaruConsole.DebugWriteLine("FAT plugin", "Casting FAT"); - secondFatEntries = MemoryMarshal.Cast(fatBytes).ToArray(); - - foreach(ushort entry in firstFatEntries) - { - if(entry >= FAT16_RESERVED || - entry <= _statfs.Blocks) - continue; - - firstFatValid = false; - - break; - } - - foreach(ushort entry in secondFatEntries) - { - if(entry >= FAT16_RESERVED || - entry <= _statfs.Blocks) - continue; - - secondFatValid = false; - - break; - } - - if(firstFatValid == secondFatValid) - _fatEntries = _useFirstFat ? firstFatEntries : secondFatEntries; - else if(firstFatValid) - _fatEntries = firstFatEntries; - else - _fatEntries = secondFatEntries; + break; } - // TODO: Check how this affects international filenames - _cultureInfo = new CultureInfo("en-US", false); - _directoryCache = new Dictionary>(); + if(firstFatValid == secondFatValid) + _fatEntries = _useFirstFat ? firstFatEntries : secondFatEntries; + else if(firstFatValid) + _fatEntries = firstFatEntries; + else + _fatEntries = secondFatEntries; + } - // Check it is really an OS/2 EA file - if(_eaDirEntry.start_cluster != 0) + // TODO: Check how this affects international filenames + _cultureInfo = new CultureInfo("en-US", false); + _directoryCache = new Dictionary>(); + + // Check it is really an OS/2 EA file + if(_eaDirEntry.start_cluster != 0) + { + CacheEaData(); + ushort eamagic = BitConverter.ToUInt16(_cachedEaData, 0); + + if(eamagic != EADATA_MAGIC) { - CacheEaData(); - ushort eamagic = BitConverter.ToUInt16(_cachedEaData, 0); - - if(eamagic != EADATA_MAGIC) - { - _eaDirEntry = new DirectoryEntry(); - _cachedEaData = null; - } - else - _eaCache = new Dictionary>(); + _eaDirEntry = new DirectoryEntry(); + _cachedEaData = null; } - else if(_fat32) + else _eaCache = new Dictionary>(); - - // Check OS/2 .LONGNAME - if(_eaCache != null && - (_namespace == Namespace.Os2 || _namespace == Namespace.Ecs) && - !_fat32) - { - List> rootFilesWithEas = - _rootDirectoryCache.Where(t => t.Value.Dirent.ea_handle != 0).ToList(); - - foreach(KeyValuePair fileWithEa in rootFilesWithEas) - { - Dictionary eas = GetEas(fileWithEa.Value.Dirent.ea_handle); - - if(eas is null) - continue; - - if(!eas.TryGetValue("com.microsoft.os2.longname", out byte[] longnameEa)) - continue; - - if(BitConverter.ToUInt16(longnameEa, 0) != EAT_ASCII) - continue; - - ushort longnameSize = BitConverter.ToUInt16(longnameEa, 2); - - if(longnameSize + 4 > longnameEa.Length) - continue; - - byte[] longnameBytes = new byte[longnameSize]; - - Array.Copy(longnameEa, 4, longnameBytes, 0, longnameSize); - - string longname = StringHandlers.CToString(longnameBytes, Encoding); - - if(string.IsNullOrWhiteSpace(longname)) - continue; - - // Forward slash is allowed in .LONGNAME, so change it to visually similar division slash - longname = longname.Replace('/', '\u2215'); - - fileWithEa.Value.Longname = longname; - _rootDirectoryCache.Remove(fileWithEa.Key); - _rootDirectoryCache[fileWithEa.Value.ToString()] = fileWithEa.Value; - } - } - - // Check FAT32.IFS EAs - if(_fat32 || _debug) - { - List> fat32EaSidecars = _rootDirectoryCache. - Where(t => t.Key.EndsWith(FAT32_EA_TAIL, true, _cultureInfo)).ToList(); - - foreach(KeyValuePair sidecar in fat32EaSidecars) - { - // No real file this sidecar accompanies - if(!_rootDirectoryCache. - TryGetValue(sidecar.Key.Substring(0, sidecar.Key.Length - FAT32_EA_TAIL.Length), - out CompleteDirectoryEntry fileWithEa)) - continue; - - // If not in debug mode we will consider the lack of EA bitflags to mean the EAs are corrupted or not real - if(!_debug) - if(!fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEaOld) && - !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa) && - !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEa) && - !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa)) - continue; - - fileWithEa.Fat32Ea = sidecar.Value.Dirent; - - if(!_debug) - _rootDirectoryCache.Remove(sidecar.Key); - } - } - - _mounted = true; - - if(string.IsNullOrWhiteSpace(XmlFsType.VolumeName)) - XmlFsType.VolumeName = null; - - return ErrorNumber.NoError; } + else if(_fat32) + _eaCache = new Dictionary>(); - /// - public ErrorNumber Unmount() + // Check OS/2 .LONGNAME + if(_eaCache != null && + (_namespace == Namespace.Os2 || _namespace == Namespace.Ecs) && + !_fat32) { - if(!_mounted) - return ErrorNumber.AccessDenied; + List> rootFilesWithEas = + _rootDirectoryCache.Where(t => t.Value.Dirent.ea_handle != 0).ToList(); - _mounted = false; - _fatEntries = null; + foreach(KeyValuePair fileWithEa in rootFilesWithEas) + { + Dictionary eas = GetEas(fileWithEa.Value.Dirent.ea_handle); - return ErrorNumber.NoError; + if(eas is null) + continue; + + if(!eas.TryGetValue("com.microsoft.os2.longname", out byte[] longnameEa)) + continue; + + if(BitConverter.ToUInt16(longnameEa, 0) != EAT_ASCII) + continue; + + ushort longnameSize = BitConverter.ToUInt16(longnameEa, 2); + + if(longnameSize + 4 > longnameEa.Length) + continue; + + byte[] longnameBytes = new byte[longnameSize]; + + Array.Copy(longnameEa, 4, longnameBytes, 0, longnameSize); + + string longname = StringHandlers.CToString(longnameBytes, Encoding); + + if(string.IsNullOrWhiteSpace(longname)) + continue; + + // Forward slash is allowed in .LONGNAME, so change it to visually similar division slash + longname = longname.Replace('/', '\u2215'); + + fileWithEa.Value.Longname = longname; + _rootDirectoryCache.Remove(fileWithEa.Key); + _rootDirectoryCache[fileWithEa.Value.ToString()] = fileWithEa.Value; + } } - /// - public ErrorNumber StatFs(out FileSystemInfo stat) + // Check FAT32.IFS EAs + if(_fat32 || _debug) { - stat = null; + List> fat32EaSidecars = _rootDirectoryCache. + Where(t => t.Key.EndsWith(FAT32_EA_TAIL, true, _cultureInfo)).ToList(); - if(!_mounted) - return ErrorNumber.AccessDenied; + foreach(KeyValuePair sidecar in fat32EaSidecars) + { + // No real file this sidecar accompanies + if(!_rootDirectoryCache. + TryGetValue(sidecar.Key.Substring(0, sidecar.Key.Length - FAT32_EA_TAIL.Length), + out CompleteDirectoryEntry fileWithEa)) + continue; - stat = _statfs.ShallowCopy(); + // If not in debug mode we will consider the lack of EA bitflags to mean the EAs are corrupted or not real + if(!_debug) + if(!fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEaOld) && + !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa) && + !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.NormalEa) && + !fileWithEa.Dirent.caseinfo.HasFlag(CaseInfo.CriticalEa)) + continue; - return ErrorNumber.NoError; + fileWithEa.Fat32Ea = sidecar.Value.Dirent; + + if(!_debug) + _rootDirectoryCache.Remove(sidecar.Key); + } } + + _mounted = true; + + if(string.IsNullOrWhiteSpace(XmlFsType.VolumeName)) + XmlFsType.VolumeName = null; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Unmount() + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + _mounted = false; + _fatEntries = null; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + stat = _statfs.ShallowCopy(); + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FAT/Xattr.cs b/Aaru.Filesystems/FAT/Xattr.cs index 73a0fcfa9..3f5ed385d 100644 --- a/Aaru.Filesystems/FAT/Xattr.cs +++ b/Aaru.Filesystems/FAT/Xattr.cs @@ -38,217 +38,216 @@ using System.Text; using Aaru.CommonTypes.Enums; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class FAT { - public sealed partial class FAT + Dictionary> _eaCache; + + /// + public ErrorNumber ListXAttr(string path, out List xattrs) { - Dictionary> _eaCache; + xattrs = null; - /// - public ErrorNumber ListXAttr(string path, out List xattrs) + if(!_mounted) + return ErrorNumber.AccessDenied; + + // No other xattr recognized yet + if(_cachedEaData is null && + !_fat32) + return ErrorNumber.NotSupported; + + if(path[0] == '/') + path = path.Substring(1); + + if(_eaCache.TryGetValue(path.ToLower(_cultureInfo), out Dictionary eas)) { - xattrs = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - // No other xattr recognized yet - if(_cachedEaData is null && - !_fat32) - return ErrorNumber.NotSupported; - - if(path[0] == '/') - path = path.Substring(1); - - if(_eaCache.TryGetValue(path.ToLower(_cultureInfo), out Dictionary eas)) - { - xattrs = eas.Keys.ToList(); - - return ErrorNumber.NoError; - } - - ErrorNumber err = GetFileEntry(path, out CompleteDirectoryEntry entry); - - if(err != ErrorNumber.NoError || - entry is null) - return err; - - xattrs = new List(); - - if(!_fat32) - { - if(entry.Dirent.ea_handle == 0) - return ErrorNumber.NoError; - - eas = GetEas(entry.Dirent.ea_handle); - } - else - { - if(entry.Fat32Ea.start_cluster == 0) - return ErrorNumber.NoError; - - eas = GetEas(entry.Fat32Ea); - } - - if(eas is null) - return ErrorNumber.NoError; - - _eaCache.Add(path.ToLower(_cultureInfo), eas); xattrs = eas.Keys.ToList(); return ErrorNumber.NoError; } - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + ErrorNumber err = GetFileEntry(path, out CompleteDirectoryEntry entry); + + if(err != ErrorNumber.NoError || + entry is null) + return err; + + xattrs = new List(); + + if(!_fat32) { - if(!_mounted) - return ErrorNumber.AccessDenied; + if(entry.Dirent.ea_handle == 0) + return ErrorNumber.NoError; - ErrorNumber err = ListXAttr(path, out List xattrs); + eas = GetEas(entry.Dirent.ea_handle); + } + else + { + if(entry.Fat32Ea.start_cluster == 0) + return ErrorNumber.NoError; - if(err != ErrorNumber.NoError) - return err; - - if(path[0] == '/') - path = path.Substring(1); - - if(!xattrs.Contains(xattr.ToLower(_cultureInfo))) - return ErrorNumber.NoSuchExtendedAttribute; - - if(!_eaCache.TryGetValue(path.ToLower(_cultureInfo), out Dictionary eas)) - return ErrorNumber.InvalidArgument; - - if(!eas.TryGetValue(xattr.ToLower(_cultureInfo), out byte[] data)) - return ErrorNumber.InvalidArgument; - - buf = new byte[data.Length]; - data.CopyTo(buf, 0); + eas = GetEas(entry.Fat32Ea); + } + if(eas is null) return ErrorNumber.NoError; - } - Dictionary GetEas(DirectoryEntry entryFat32Ea) + _eaCache.Add(path.ToLower(_cultureInfo), eas); + xattrs = eas.Keys.ToList(); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = ListXAttr(path, out List xattrs); + + if(err != ErrorNumber.NoError) + return err; + + if(path[0] == '/') + path = path.Substring(1); + + if(!xattrs.Contains(xattr.ToLower(_cultureInfo))) + return ErrorNumber.NoSuchExtendedAttribute; + + if(!_eaCache.TryGetValue(path.ToLower(_cultureInfo), out Dictionary eas)) + return ErrorNumber.InvalidArgument; + + if(!eas.TryGetValue(xattr.ToLower(_cultureInfo), out byte[] data)) + return ErrorNumber.InvalidArgument; + + buf = new byte[data.Length]; + data.CopyTo(buf, 0); + + return ErrorNumber.NoError; + } + + Dictionary GetEas(DirectoryEntry entryFat32Ea) + { + var eaMs = new MemoryStream(); + uint[] rootDirectoryClusters = GetClusters(entryFat32Ea.start_cluster); + + foreach(uint cluster in rootDirectoryClusters) { - var eaMs = new MemoryStream(); - uint[] rootDirectoryClusters = GetClusters(entryFat32Ea.start_cluster); + ErrorNumber errno = _image.ReadSectors(_firstClusterSector + (cluster * _sectorsPerCluster), + _sectorsPerCluster, out byte[] buffer); - foreach(uint cluster in rootDirectoryClusters) - { - ErrorNumber errno = _image.ReadSectors(_firstClusterSector + (cluster * _sectorsPerCluster), - _sectorsPerCluster, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return null; - - eaMs.Write(buffer, 0, buffer.Length); - } - - byte[] full = eaMs.ToArray(); - ushort size = BitConverter.ToUInt16(full, 0); - byte[] eas = new byte[size]; - Array.Copy(full, 0, eas, 0, size); - - eaMs.Close(); - - return GetEas(eas); - } - - Dictionary GetEas(ushort eaHandle) - { - int aIndex = eaHandle >> 7; - - // First 0x20 bytes are the magic number and unused words - ushort a = BitConverter.ToUInt16(_cachedEaData, (aIndex * 2) + 0x20); - - ushort b = BitConverter.ToUInt16(_cachedEaData, (eaHandle * 2) + 0x200); - - uint eaCluster = (uint)(a + b); - - if(b == EA_UNUSED) + if(errno != ErrorNumber.NoError) return null; - EaHeader header = - Marshal.ByteArrayToStructureLittleEndian(_cachedEaData, (int)(eaCluster * _bytesPerCluster), - Marshal.SizeOf()); - - if(header.magic != 0x4145) - return null; - - uint eaLen = BitConverter.ToUInt32(_cachedEaData, - (int)(eaCluster * _bytesPerCluster) + Marshal.SizeOf()); - - byte[] eaData = new byte[eaLen]; - - Array.Copy(_cachedEaData, (int)(eaCluster * _bytesPerCluster) + Marshal.SizeOf(), eaData, 0, - eaLen); - - return GetEas(eaData); + eaMs.Write(buffer, 0, buffer.Length); } - Dictionary GetEas(byte[] eaData) + byte[] full = eaMs.ToArray(); + ushort size = BitConverter.ToUInt16(full, 0); + byte[] eas = new byte[size]; + Array.Copy(full, 0, eas, 0, size); + + eaMs.Close(); + + return GetEas(eas); + } + + Dictionary GetEas(ushort eaHandle) + { + int aIndex = eaHandle >> 7; + + // First 0x20 bytes are the magic number and unused words + ushort a = BitConverter.ToUInt16(_cachedEaData, (aIndex * 2) + 0x20); + + ushort b = BitConverter.ToUInt16(_cachedEaData, (eaHandle * 2) + 0x200); + + uint eaCluster = (uint)(a + b); + + if(b == EA_UNUSED) + return null; + + EaHeader header = + Marshal.ByteArrayToStructureLittleEndian(_cachedEaData, (int)(eaCluster * _bytesPerCluster), + Marshal.SizeOf()); + + if(header.magic != 0x4145) + return null; + + uint eaLen = BitConverter.ToUInt32(_cachedEaData, + (int)(eaCluster * _bytesPerCluster) + Marshal.SizeOf()); + + byte[] eaData = new byte[eaLen]; + + Array.Copy(_cachedEaData, (int)(eaCluster * _bytesPerCluster) + Marshal.SizeOf(), eaData, 0, + eaLen); + + return GetEas(eaData); + } + + Dictionary GetEas(byte[] eaData) + { + if(eaData is null || + eaData.Length < 4) + return null; + + Dictionary eas = new(); + + if(_debug) + eas.Add("com.microsoft.os2.fea", eaData); + + int pos = 4; + + while(pos < eaData.Length) { - if(eaData is null || - eaData.Length < 4) - return null; + pos++; // Skip fEA + byte cbName = eaData[pos++]; + ushort cbValue = BitConverter.ToUInt16(eaData, pos); + pos += 2; - Dictionary eas = new(); + string name = Encoding.ASCII.GetString(eaData, pos, cbName); + pos += cbName; + pos++; + byte[] data = new byte[cbValue]; - if(_debug) - eas.Add("com.microsoft.os2.fea", eaData); + Array.Copy(eaData, pos, data, 0, cbValue); + pos += cbValue; - int pos = 4; - - while(pos < eaData.Length) + // OS/2 System Attributes + if(name[0] == '.') { - pos++; // Skip fEA - byte cbName = eaData[pos++]; - ushort cbValue = BitConverter.ToUInt16(eaData, pos); - pos += 2; - - string name = Encoding.ASCII.GetString(eaData, pos, cbName); - pos += cbName; - pos++; - byte[] data = new byte[cbValue]; - - Array.Copy(eaData, pos, data, 0, cbValue); - pos += cbValue; - - // OS/2 System Attributes - if(name[0] == '.') - { - // This is WorkPlace System information so it's IBM - if(name == ".CLASSINFO") - name = "com.ibm.os2.classinfo"; - else - name = "com.microsoft.os2" + name.ToLower(); - } - - eas.Add(name, data); + // This is WorkPlace System information so it's IBM + if(name == ".CLASSINFO") + name = "com.ibm.os2.classinfo"; + else + name = "com.microsoft.os2" + name.ToLower(); } - return eas; + eas.Add(name, data); } - void CacheEaData() + return eas; + } + + void CacheEaData() + { + if(_eaDirEntry.start_cluster == 0) + return; + + var eaDataMs = new MemoryStream(); + + foreach(uint cluster in GetClusters(_eaDirEntry.start_cluster)) { - if(_eaDirEntry.start_cluster == 0) - return; + ErrorNumber errno = _image.ReadSectors(_firstClusterSector + (cluster * _sectorsPerCluster), + _sectorsPerCluster, out byte[] buffer); - var eaDataMs = new MemoryStream(); + if(errno != ErrorNumber.NoError) + break; - foreach(uint cluster in GetClusters(_eaDirEntry.start_cluster)) - { - ErrorNumber errno = _image.ReadSectors(_firstClusterSector + (cluster * _sectorsPerCluster), - _sectorsPerCluster, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - break; - - eaDataMs.Write(buffer, 0, buffer.Length); - } - - _cachedEaData = eaDataMs.ToArray(); + eaDataMs.Write(buffer, 0, buffer.Length); } + + _cachedEaData = eaDataMs.ToArray(); } } \ No newline at end of file diff --git a/Aaru.Filesystems/FATX/Consts.cs b/Aaru.Filesystems/FATX/Consts.cs index 5bd470e4a..b6cc8b27b 100644 --- a/Aaru.Filesystems/FATX/Consts.cs +++ b/Aaru.Filesystems/FATX/Consts.cs @@ -34,36 +34,35 @@ using System; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems -{ - public sealed partial class XboxFatPlugin - { - const uint FATX_MAGIC = 0x58544146; - const uint FATX_CIGAM = 0x46415458; - const byte UNUSED_DIRENTRY = 0x00; - const byte DELETED_DIRENTRY = 0xE5; - const byte FINISHED_DIRENTRY = 0xFF; - const byte MAX_FILENAME = 42; - const int MAX_XFAT16_CLUSTERS = 65525; - const int FAT_START = 4096; - const uint FATX32_ID = 0xFFFFFFF8; - const ushort FATX16_ID = 0xFFF8; - const uint FAT32_MASK = 0x0FFFFFFF; - const uint FAT32_END_MASK = 0xFFFFFF8; - const uint FAT32_FORMATTED = 0xFFFFFF6; - const uint FAT32_BAD = 0xFFFFFF7; - const uint FAT32_RESERVED = 0xFFFFFF0; - const ushort FAT16_END_MASK = 0xFFF8; - const ushort FAT16_FORMATTED = 0xFFF6; - const ushort FAT16_BAD = 0xFFF7; - const ushort FAT16_RESERVED = 0xFFF0; - const ushort FAT_RESERVED = 1; +namespace Aaru.Filesystems; - [Flags] - enum Attributes : byte - { - ReadOnly = 0x01, Hidden = 0x02, System = 0x04, - Directory = 0x10, Archive = 0x20 - } +public sealed partial class XboxFatPlugin +{ + const uint FATX_MAGIC = 0x58544146; + const uint FATX_CIGAM = 0x46415458; + const byte UNUSED_DIRENTRY = 0x00; + const byte DELETED_DIRENTRY = 0xE5; + const byte FINISHED_DIRENTRY = 0xFF; + const byte MAX_FILENAME = 42; + const int MAX_XFAT16_CLUSTERS = 65525; + const int FAT_START = 4096; + const uint FATX32_ID = 0xFFFFFFF8; + const ushort FATX16_ID = 0xFFF8; + const uint FAT32_MASK = 0x0FFFFFFF; + const uint FAT32_END_MASK = 0xFFFFFF8; + const uint FAT32_FORMATTED = 0xFFFFFF6; + const uint FAT32_BAD = 0xFFFFFF7; + const uint FAT32_RESERVED = 0xFFFFFF0; + const ushort FAT16_END_MASK = 0xFFF8; + const ushort FAT16_FORMATTED = 0xFFF6; + const ushort FAT16_BAD = 0xFFF7; + const ushort FAT16_RESERVED = 0xFFF0; + const ushort FAT_RESERVED = 1; + + [Flags] + enum Attributes : byte + { + ReadOnly = 0x01, Hidden = 0x02, System = 0x04, + Directory = 0x10, Archive = 0x20 } } \ No newline at end of file diff --git a/Aaru.Filesystems/FATX/Dir.cs b/Aaru.Filesystems/FATX/Dir.cs index fb45e06f5..9c31746c0 100644 --- a/Aaru.Filesystems/FATX/Dir.cs +++ b/Aaru.Filesystems/FATX/Dir.cs @@ -36,43 +36,57 @@ using System.Linq; using Aaru.CommonTypes.Enums; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class XboxFatPlugin { - public sealed partial class XboxFatPlugin + /// + public ErrorNumber ReadDir(string path, out List contents) { - /// - public ErrorNumber ReadDir(string path, out List contents) + contents = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(string.IsNullOrWhiteSpace(path) || + path == "/") { - contents = null; + contents = _rootDirectory.Keys.ToList(); - if(!_mounted) - return ErrorNumber.AccessDenied; + return ErrorNumber.NoError; + } - if(string.IsNullOrWhiteSpace(path) || - path == "/") - { - contents = _rootDirectory.Keys.ToList(); + string cutPath = path.StartsWith('/') ? path.Substring(1).ToLower(_cultureInfo) + : path.ToLower(_cultureInfo); - return ErrorNumber.NoError; - } + if(_directoryCache.TryGetValue(cutPath, out Dictionary currentDirectory)) + { + contents = currentDirectory.Keys.ToList(); - string cutPath = path.StartsWith('/') ? path.Substring(1).ToLower(_cultureInfo) - : path.ToLower(_cultureInfo); + return ErrorNumber.NoError; + } - if(_directoryCache.TryGetValue(cutPath, out Dictionary currentDirectory)) - { - contents = currentDirectory.Keys.ToList(); + string[] pieces = cutPath.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); - return ErrorNumber.NoError; - } + KeyValuePair entry = + _rootDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[0]); - string[] pieces = cutPath.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + if(string.IsNullOrEmpty(entry.Key)) + return ErrorNumber.NoSuchFile; - KeyValuePair entry = - _rootDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[0]); + if(!entry.Value.attributes.HasFlag(Attributes.Directory)) + return ErrorNumber.NotDirectory; + + string currentPath = pieces[0]; + + currentDirectory = _rootDirectory; + + for(int p = 0; p < pieces.Length; p++) + { + entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[p]); if(string.IsNullOrEmpty(entry.Key)) return ErrorNumber.NoSuchFile; @@ -80,80 +94,65 @@ namespace Aaru.Filesystems if(!entry.Value.attributes.HasFlag(Attributes.Directory)) return ErrorNumber.NotDirectory; - string currentPath = pieces[0]; + currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; + uint currentCluster = entry.Value.firstCluster; - currentDirectory = _rootDirectory; + if(_directoryCache.TryGetValue(currentPath, out currentDirectory)) + continue; - for(int p = 0; p < pieces.Length; p++) + uint[] clusters = GetClusters(currentCluster); + + if(clusters is null) + return ErrorNumber.InvalidArgument; + + byte[] directoryBuffer = new byte[_bytesPerCluster * clusters.Length]; + + for(int i = 0; i < clusters.Length; i++) { - entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[p]); + ErrorNumber errno = + _imagePlugin.ReadSectors(_firstClusterSector + ((clusters[i] - 1) * _sectorsPerCluster), + _sectorsPerCluster, out byte[] buffer); - if(string.IsNullOrEmpty(entry.Key)) - return ErrorNumber.NoSuchFile; + if(errno != ErrorNumber.NoError) + return errno; - if(!entry.Value.attributes.HasFlag(Attributes.Directory)) - return ErrorNumber.NotDirectory; - - currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; - uint currentCluster = entry.Value.firstCluster; - - if(_directoryCache.TryGetValue(currentPath, out currentDirectory)) - continue; - - uint[] clusters = GetClusters(currentCluster); - - if(clusters is null) - return ErrorNumber.InvalidArgument; - - byte[] directoryBuffer = new byte[_bytesPerCluster * clusters.Length]; - - for(int i = 0; i < clusters.Length; i++) - { - ErrorNumber errno = - _imagePlugin.ReadSectors(_firstClusterSector + ((clusters[i] - 1) * _sectorsPerCluster), - _sectorsPerCluster, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - Array.Copy(buffer, 0, directoryBuffer, i * _bytesPerCluster, _bytesPerCluster); - } - - currentDirectory = new Dictionary(); - - int pos = 0; - - while(pos < directoryBuffer.Length) - { - DirectoryEntry dirent = _littleEndian - ? Marshal. - ByteArrayToStructureLittleEndian< - DirectoryEntry>(directoryBuffer, pos, - Marshal.SizeOf()) - : Marshal.ByteArrayToStructureBigEndian(directoryBuffer, - pos, Marshal.SizeOf()); - - pos += Marshal.SizeOf(); - - if(dirent.filenameSize == UNUSED_DIRENTRY || - dirent.filenameSize == FINISHED_DIRENTRY) - break; - - if(dirent.filenameSize == DELETED_DIRENTRY || - dirent.filenameSize > MAX_FILENAME) - continue; - - string filename = Encoding.GetString(dirent.filename, 0, dirent.filenameSize); - - currentDirectory.Add(filename, dirent); - } - - _directoryCache.Add(currentPath, currentDirectory); + Array.Copy(buffer, 0, directoryBuffer, i * _bytesPerCluster, _bytesPerCluster); } - contents = currentDirectory?.Keys.ToList(); + currentDirectory = new Dictionary(); - return ErrorNumber.NoError; + int pos = 0; + + while(pos < directoryBuffer.Length) + { + DirectoryEntry dirent = _littleEndian + ? Marshal. + ByteArrayToStructureLittleEndian< + DirectoryEntry>(directoryBuffer, pos, + Marshal.SizeOf()) + : Marshal.ByteArrayToStructureBigEndian(directoryBuffer, + pos, Marshal.SizeOf()); + + pos += Marshal.SizeOf(); + + if(dirent.filenameSize == UNUSED_DIRENTRY || + dirent.filenameSize == FINISHED_DIRENTRY) + break; + + if(dirent.filenameSize == DELETED_DIRENTRY || + dirent.filenameSize > MAX_FILENAME) + continue; + + string filename = Encoding.GetString(dirent.filename, 0, dirent.filenameSize); + + currentDirectory.Add(filename, dirent); + } + + _directoryCache.Add(currentPath, currentDirectory); } + + contents = currentDirectory?.Keys.ToList(); + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FATX/FATX.cs b/Aaru.Filesystems/FATX/FATX.cs index 00a94cf0f..2409f58c4 100644 --- a/Aaru.Filesystems/FATX/FATX.cs +++ b/Aaru.Filesystems/FATX/FATX.cs @@ -39,71 +39,70 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements the Xbox File Allocation Table (FATX or XTAF) filesystem. +public sealed partial class XboxFatPlugin : IReadOnlyFilesystem { + uint _bytesPerCluster; + CultureInfo _cultureInfo; + bool _debug; + Dictionary> _directoryCache; + ushort[] _fat16; + uint[] _fat32; + ulong _fatStartSector; + ulong _firstClusterSector; + IMediaImage _imagePlugin; + bool _littleEndian; + bool _mounted; + Dictionary _rootDirectory; + uint _sectorsPerCluster; + FileSystemInfo _statfs; + Superblock _superblock; + /// - /// Implements the Xbox File Allocation Table (FATX or XTAF) filesystem. - public sealed partial class XboxFatPlugin : IReadOnlyFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "FATX Filesystem Plugin"; + /// + public Guid Id => new("ED27A721-4A17-4649-89FD-33633B46E228"); + /// + public string Author => "Natalia Portillo"; + + /// + public ErrorNumber ListXAttr(string path, out List xattrs) { - uint _bytesPerCluster; - CultureInfo _cultureInfo; - bool _debug; - Dictionary> _directoryCache; - ushort[] _fat16; - uint[] _fat32; - ulong _fatStartSector; - ulong _firstClusterSector; - IMediaImage _imagePlugin; - bool _littleEndian; - bool _mounted; - Dictionary _rootDirectory; - uint _sectorsPerCluster; - FileSystemInfo _statfs; - Superblock _superblock; + xattrs = null; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "FATX Filesystem Plugin"; - /// - public Guid Id => new("ED27A721-4A17-4649-89FD-33633B46E228"); - /// - public string Author => "Natalia Portillo"; - - /// - public ErrorNumber ListXAttr(string path, out List xattrs) - { - xattrs = null; - - return ErrorNumber.NotSupported; - } - - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) => ErrorNumber.NotSupported; - - /// - public ErrorNumber ReadLink(string path, out string dest) - { - dest = null; - - return ErrorNumber.NotSupported; - } - - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - {}; - - /// - public Dictionary Namespaces => null; - - static Dictionary GetDefaultOptions() => new() - { - { - "debug", false.ToString() - } - }; + return ErrorNumber.NotSupported; } + + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) => ErrorNumber.NotSupported; + + /// + public ErrorNumber ReadLink(string path, out string dest) + { + dest = null; + + return ErrorNumber.NotSupported; + } + + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + {}; + + /// + public Dictionary Namespaces => null; + + static Dictionary GetDefaultOptions() => new() + { + { + "debug", false.ToString() + } + }; } \ No newline at end of file diff --git a/Aaru.Filesystems/FATX/File.cs b/Aaru.Filesystems/FATX/File.cs index 7644f1a42..5ef30a65b 100644 --- a/Aaru.Filesystems/FATX/File.cs +++ b/Aaru.Filesystems/FATX/File.cs @@ -39,256 +39,255 @@ using Aaru.CommonTypes.Structs; using Aaru.Helpers; using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class XboxFatPlugin { - public sealed partial class XboxFatPlugin + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) { - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + deviceBlock = 0; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = Stat(path, out FileEntryInfo stat); + + if(err != ErrorNumber.NoError) + return err; + + if(stat.Attributes.HasFlag(FileAttributes.Directory) && + !_debug) + return ErrorNumber.IsDirectory; + + uint[] clusters = GetClusters((uint)stat.Inode); + + if(fileBlock >= clusters.Length) + return ErrorNumber.InvalidArgument; + + deviceBlock = (long)(_firstClusterSector + ((clusters[fileBlock] - 1) * _sectorsPerCluster)); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + { + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = Stat(path, out FileEntryInfo stat); + + if(err != ErrorNumber.NoError) + return err; + + attributes = stat.Attributes; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = Stat(path, out FileEntryInfo stat); + + if(err != ErrorNumber.NoError) + return err; + + if(stat.Attributes.HasFlag(FileAttributes.Directory) && + !_debug) + return ErrorNumber.IsDirectory; + + if(offset >= stat.Length) + return ErrorNumber.InvalidArgument; + + if(size + offset >= stat.Length) + size = stat.Length - offset; + + uint[] clusters = GetClusters((uint)stat.Inode); + + long firstCluster = offset / _bytesPerCluster; + long offsetInCluster = offset % _bytesPerCluster; + long sizeInClusters = (size + offsetInCluster) / _bytesPerCluster; + + if((size + offsetInCluster) % _bytesPerCluster > 0) + sizeInClusters++; + + var ms = new MemoryStream(); + + for(int i = 0; i < sizeInClusters; i++) { - deviceBlock = 0; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = Stat(path, out FileEntryInfo stat); - - if(err != ErrorNumber.NoError) - return err; - - if(stat.Attributes.HasFlag(FileAttributes.Directory) && - !_debug) - return ErrorNumber.IsDirectory; - - uint[] clusters = GetClusters((uint)stat.Inode); - - if(fileBlock >= clusters.Length) + if(i + firstCluster >= clusters.Length) return ErrorNumber.InvalidArgument; - deviceBlock = (long)(_firstClusterSector + ((clusters[fileBlock] - 1) * _sectorsPerCluster)); + ErrorNumber errno = + _imagePlugin. + ReadSectors(_firstClusterSector + ((clusters[i + firstCluster] - 1) * _sectorsPerCluster), + _sectorsPerCluster, out byte[] buffer); - return ErrorNumber.NoError; + if(errno != ErrorNumber.NoError) + return errno; + + ms.Write(buffer, 0, buffer.Length); } - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + ms.Position = offsetInCluster; + buf = new byte[size]; + ms.Read(buf, 0, (int)size); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(_debug && (string.IsNullOrEmpty(path) || path == "$" || path == "/")) { - attributes = new FileAttributes(); - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = Stat(path, out FileEntryInfo stat); - - if(err != ErrorNumber.NoError) - return err; - - attributes = stat.Attributes; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) - { - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = Stat(path, out FileEntryInfo stat); - - if(err != ErrorNumber.NoError) - return err; - - if(stat.Attributes.HasFlag(FileAttributes.Directory) && - !_debug) - return ErrorNumber.IsDirectory; - - if(offset >= stat.Length) - return ErrorNumber.InvalidArgument; - - if(size + offset >= stat.Length) - size = stat.Length - offset; - - uint[] clusters = GetClusters((uint)stat.Inode); - - long firstCluster = offset / _bytesPerCluster; - long offsetInCluster = offset % _bytesPerCluster; - long sizeInClusters = (size + offsetInCluster) / _bytesPerCluster; - - if((size + offsetInCluster) % _bytesPerCluster > 0) - sizeInClusters++; - - var ms = new MemoryStream(); - - for(int i = 0; i < sizeInClusters; i++) - { - if(i + firstCluster >= clusters.Length) - return ErrorNumber.InvalidArgument; - - ErrorNumber errno = - _imagePlugin. - ReadSectors(_firstClusterSector + ((clusters[i + firstCluster] - 1) * _sectorsPerCluster), - _sectorsPerCluster, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - ms.Write(buffer, 0, buffer.Length); - } - - ms.Position = offsetInCluster; - buf = new byte[size]; - ms.Read(buf, 0, (int)size); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) - { - stat = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - if(_debug && (string.IsNullOrEmpty(path) || path == "$" || path == "/")) - { - stat = new FileEntryInfo - { - Attributes = FileAttributes.Directory | FileAttributes.System | FileAttributes.Hidden, - Blocks = GetClusters(_superblock.rootDirectoryCluster).Length, - BlockSize = _bytesPerCluster, - Length = GetClusters(_superblock.rootDirectoryCluster).Length * _bytesPerCluster, - Inode = _superblock.rootDirectoryCluster, - Links = 1 - }; - - return ErrorNumber.NoError; - } - - ErrorNumber err = GetFileEntry(path, out DirectoryEntry entry); - - if(err != ErrorNumber.NoError) - return err; - stat = new FileEntryInfo { - Attributes = new FileAttributes(), - Blocks = entry.length / _bytesPerCluster, + Attributes = FileAttributes.Directory | FileAttributes.System | FileAttributes.Hidden, + Blocks = GetClusters(_superblock.rootDirectoryCluster).Length, BlockSize = _bytesPerCluster, - Length = entry.length, - Inode = entry.firstCluster, - Links = 1, - CreationTime = _littleEndian - ? DateHandlers.DosToDateTime(entry.creationDate, entry.creationTime).AddYears(20) - : DateHandlers.DosToDateTime(entry.creationTime, entry.creationDate), - AccessTime = _littleEndian - ? DateHandlers.DosToDateTime(entry.lastAccessDate, entry.lastAccessTime).AddYears(20) - : DateHandlers.DosToDateTime(entry.lastAccessTime, entry.lastAccessDate), - LastWriteTime = _littleEndian - ? DateHandlers.DosToDateTime(entry.lastWrittenDate, entry.lastWrittenTime). - AddYears(20) - : DateHandlers.DosToDateTime(entry.lastWrittenTime, entry.lastWrittenDate) + Length = GetClusters(_superblock.rootDirectoryCluster).Length * _bytesPerCluster, + Inode = _superblock.rootDirectoryCluster, + Links = 1 }; - if(entry.length % _bytesPerCluster > 0) - stat.Blocks++; - - if(entry.attributes.HasFlag(Attributes.Directory)) - { - stat.Attributes |= FileAttributes.Directory; - stat.Blocks = GetClusters(entry.firstCluster).Length; - stat.Length = stat.Blocks * stat.BlockSize; - } - - if(entry.attributes.HasFlag(Attributes.ReadOnly)) - stat.Attributes |= FileAttributes.ReadOnly; - - if(entry.attributes.HasFlag(Attributes.Hidden)) - stat.Attributes |= FileAttributes.Hidden; - - if(entry.attributes.HasFlag(Attributes.System)) - stat.Attributes |= FileAttributes.System; - - if(entry.attributes.HasFlag(Attributes.Archive)) - stat.Attributes |= FileAttributes.Archive; - return ErrorNumber.NoError; } - uint[] GetClusters(uint startCluster) - { - if(startCluster == 0) - return null; + ErrorNumber err = GetFileEntry(path, out DirectoryEntry entry); - if(_fat16 is null) + if(err != ErrorNumber.NoError) + return err; + + stat = new FileEntryInfo + { + Attributes = new FileAttributes(), + Blocks = entry.length / _bytesPerCluster, + BlockSize = _bytesPerCluster, + Length = entry.length, + Inode = entry.firstCluster, + Links = 1, + CreationTime = _littleEndian + ? DateHandlers.DosToDateTime(entry.creationDate, entry.creationTime).AddYears(20) + : DateHandlers.DosToDateTime(entry.creationTime, entry.creationDate), + AccessTime = _littleEndian + ? DateHandlers.DosToDateTime(entry.lastAccessDate, entry.lastAccessTime).AddYears(20) + : DateHandlers.DosToDateTime(entry.lastAccessTime, entry.lastAccessDate), + LastWriteTime = _littleEndian + ? DateHandlers.DosToDateTime(entry.lastWrittenDate, entry.lastWrittenTime). + AddYears(20) + : DateHandlers.DosToDateTime(entry.lastWrittenTime, entry.lastWrittenDate) + }; + + if(entry.length % _bytesPerCluster > 0) + stat.Blocks++; + + if(entry.attributes.HasFlag(Attributes.Directory)) + { + stat.Attributes |= FileAttributes.Directory; + stat.Blocks = GetClusters(entry.firstCluster).Length; + stat.Length = stat.Blocks * stat.BlockSize; + } + + if(entry.attributes.HasFlag(Attributes.ReadOnly)) + stat.Attributes |= FileAttributes.ReadOnly; + + if(entry.attributes.HasFlag(Attributes.Hidden)) + stat.Attributes |= FileAttributes.Hidden; + + if(entry.attributes.HasFlag(Attributes.System)) + stat.Attributes |= FileAttributes.System; + + if(entry.attributes.HasFlag(Attributes.Archive)) + stat.Attributes |= FileAttributes.Archive; + + return ErrorNumber.NoError; + } + + uint[] GetClusters(uint startCluster) + { + if(startCluster == 0) + return null; + + if(_fat16 is null) + { + if(startCluster >= _fat32.Length) + return null; + } + else if(startCluster >= _fat16.Length) + return null; + + List clusters = new(); + + uint nextCluster = startCluster; + + if(_fat16 is null) + while((nextCluster & FAT32_MASK) > 0 && + (nextCluster & FAT32_MASK) <= FAT32_RESERVED) { - if(startCluster >= _fat32.Length) - return null; + clusters.Add(nextCluster); + nextCluster = _fat32[nextCluster]; } - else if(startCluster >= _fat16.Length) - return null; - - List clusters = new(); - - uint nextCluster = startCluster; - - if(_fat16 is null) - while((nextCluster & FAT32_MASK) > 0 && - (nextCluster & FAT32_MASK) <= FAT32_RESERVED) - { - clusters.Add(nextCluster); - nextCluster = _fat32[nextCluster]; - } - else - while(nextCluster > 0 && - nextCluster <= FAT16_RESERVED) - { - clusters.Add(nextCluster); - nextCluster = _fat16[nextCluster]; - } - - return clusters.ToArray(); - } - - ErrorNumber GetFileEntry(string path, out DirectoryEntry entry) - { - entry = new DirectoryEntry(); - - string cutPath = path.StartsWith('/') ? path.Substring(1).ToLower(_cultureInfo) - : path.ToLower(_cultureInfo); - - string[] pieces = cutPath.Split(new[] + else + while(nextCluster > 0 && + nextCluster <= FAT16_RESERVED) { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + clusters.Add(nextCluster); + nextCluster = _fat16[nextCluster]; + } - if(pieces.Length == 0) - return ErrorNumber.InvalidArgument; + return clusters.ToArray(); + } - string parentPath = string.Join("/", pieces, 0, pieces.Length - 1); + ErrorNumber GetFileEntry(string path, out DirectoryEntry entry) + { + entry = new DirectoryEntry(); - ErrorNumber err = ReadDir(parentPath, out _); + string cutPath = path.StartsWith('/') ? path.Substring(1).ToLower(_cultureInfo) + : path.ToLower(_cultureInfo); - if(err != ErrorNumber.NoError) - return err; + string[] pieces = cutPath.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); - Dictionary parent; + if(pieces.Length == 0) + return ErrorNumber.InvalidArgument; - if(pieces.Length == 1) - parent = _rootDirectory; - else if(!_directoryCache.TryGetValue(parentPath, out parent)) - return ErrorNumber.InvalidArgument; + string parentPath = string.Join("/", pieces, 0, pieces.Length - 1); - KeyValuePair dirent = - parent.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[^1]); + ErrorNumber err = ReadDir(parentPath, out _); - if(string.IsNullOrEmpty(dirent.Key)) - return ErrorNumber.NoSuchFile; + if(err != ErrorNumber.NoError) + return err; - entry = dirent.Value; + Dictionary parent; - return ErrorNumber.NoError; - } + if(pieces.Length == 1) + parent = _rootDirectory; + else if(!_directoryCache.TryGetValue(parentPath, out parent)) + return ErrorNumber.InvalidArgument; + + KeyValuePair dirent = + parent.FirstOrDefault(t => t.Key.ToLower(_cultureInfo) == pieces[^1]); + + if(string.IsNullOrEmpty(dirent.Key)) + return ErrorNumber.NoSuchFile; + + entry = dirent.Value; + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FATX/Info.cs b/Aaru.Filesystems/FATX/Info.cs index 5ff4bc0aa..fd7cbb99a 100644 --- a/Aaru.Filesystems/FATX/Info.cs +++ b/Aaru.Filesystems/FATX/Info.cs @@ -37,89 +37,88 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class XboxFatPlugin { - public sealed partial class XboxFatPlugin + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(imagePlugin.Info.SectorSize < 512) + return false; + + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + Superblock sb = Marshal.ByteArrayToStructureBigEndian(sector); + + return sb.magic == FATX_MAGIC || sb.magic == FATX_CIGAM; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = Encoding.UTF8; + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + bool bigEndian = true; + + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + Superblock fatxSb = Marshal.ByteArrayToStructureBigEndian(sector); + + if(fatxSb.magic == FATX_CIGAM) { - if(imagePlugin.Info.SectorSize < 512) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - Superblock sb = Marshal.ByteArrayToStructureBigEndian(sector); - - return sb.magic == FATX_MAGIC || sb.magic == FATX_CIGAM; + fatxSb = Marshal.ByteArrayToStructureLittleEndian(sector); + bigEndian = false; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + if(fatxSb.magic != FATX_MAGIC) + return; + + int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && !bigEndian ? 8 : 1; + + var sb = new StringBuilder(); + + sb.AppendLine("FATX filesystem"); + + sb.AppendFormat("{0} logical sectors ({1} bytes) per physical sector", logicalSectorsPerPhysicalSectors, + logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize).AppendLine(); + + sb.AppendFormat("{0} sectors ({1} bytes) per cluster", fatxSb.sectorsPerCluster, + fatxSb.sectorsPerCluster * logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize). + AppendLine(); + + sb.AppendFormat("Root directory starts on cluster {0}", fatxSb.rootDirectoryCluster).AppendLine(); + + string volumeLabel = StringHandlers.CToString(fatxSb.volumeLabel, + bigEndian ? Encoding.BigEndianUnicode : Encoding.Unicode, + true); + + sb.AppendFormat("Volume label: {0}", volumeLabel).AppendLine(); + sb.AppendFormat("Volume serial: {0:X8}", fatxSb.id).AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - Encoding = Encoding.UTF8; - information = ""; + Type = "FATX filesystem", + ClusterSize = (uint)(fatxSb.sectorsPerCluster * logicalSectorsPerPhysicalSectors * + imagePlugin.Info.SectorSize), + VolumeName = volumeLabel, + VolumeSerial = $"{fatxSb.id:X8}" + }; - if(imagePlugin.Info.SectorSize < 512) - return; - - bool bigEndian = true; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - Superblock fatxSb = Marshal.ByteArrayToStructureBigEndian(sector); - - if(fatxSb.magic == FATX_CIGAM) - { - fatxSb = Marshal.ByteArrayToStructureLittleEndian(sector); - bigEndian = false; - } - - if(fatxSb.magic != FATX_MAGIC) - return; - - int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && !bigEndian ? 8 : 1; - - var sb = new StringBuilder(); - - sb.AppendLine("FATX filesystem"); - - sb.AppendFormat("{0} logical sectors ({1} bytes) per physical sector", logicalSectorsPerPhysicalSectors, - logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize).AppendLine(); - - sb.AppendFormat("{0} sectors ({1} bytes) per cluster", fatxSb.sectorsPerCluster, - fatxSb.sectorsPerCluster * logicalSectorsPerPhysicalSectors * imagePlugin.Info.SectorSize). - AppendLine(); - - sb.AppendFormat("Root directory starts on cluster {0}", fatxSb.rootDirectoryCluster).AppendLine(); - - string volumeLabel = StringHandlers.CToString(fatxSb.volumeLabel, - bigEndian ? Encoding.BigEndianUnicode : Encoding.Unicode, - true); - - sb.AppendFormat("Volume label: {0}", volumeLabel).AppendLine(); - sb.AppendFormat("Volume serial: {0:X8}", fatxSb.id).AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "FATX filesystem", - ClusterSize = (uint)(fatxSb.sectorsPerCluster * logicalSectorsPerPhysicalSectors * - imagePlugin.Info.SectorSize), - VolumeName = volumeLabel, - VolumeSerial = $"{fatxSb.id:X8}" - }; - - XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / - XmlFsType.ClusterSize; - } + XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / + XmlFsType.ClusterSize; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FATX/Structs.cs b/Aaru.Filesystems/FATX/Structs.cs index baa524fc0..4a2c72ef6 100644 --- a/Aaru.Filesystems/FATX/Structs.cs +++ b/Aaru.Filesystems/FATX/Structs.cs @@ -32,38 +32,37 @@ using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class XboxFatPlugin { - public sealed partial class XboxFatPlugin + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Superblock { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Superblock - { - public readonly uint magic; - public readonly uint id; - public readonly uint sectorsPerCluster; - public readonly uint rootDirectoryCluster; + public readonly uint magic; + public readonly uint id; + public readonly uint sectorsPerCluster; + public readonly uint rootDirectoryCluster; - // TODO: Undetermined size - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] volumeLabel; - } + // TODO: Undetermined size + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] volumeLabel; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryEntry - { - public readonly byte filenameSize; - public readonly Attributes attributes; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_FILENAME)] - public readonly byte[] filename; - public readonly uint firstCluster; - public readonly uint length; - public readonly ushort lastWrittenTime; - public readonly ushort lastWrittenDate; - public readonly ushort lastAccessTime; - public readonly ushort lastAccessDate; - public readonly ushort creationTime; - public readonly ushort creationDate; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryEntry + { + public readonly byte filenameSize; + public readonly Attributes attributes; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_FILENAME)] + public readonly byte[] filename; + public readonly uint firstCluster; + public readonly uint length; + public readonly ushort lastWrittenTime; + public readonly ushort lastWrittenDate; + public readonly ushort lastAccessTime; + public readonly ushort lastAccessDate; + public readonly ushort creationTime; + public readonly ushort creationDate; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FATX/Super.cs b/Aaru.Filesystems/FATX/Super.cs index 7df615489..bd15579ad 100644 --- a/Aaru.Filesystems/FATX/Super.cs +++ b/Aaru.Filesystems/FATX/Super.cs @@ -44,259 +44,258 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class XboxFatPlugin { - public sealed partial class XboxFatPlugin + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + Encoding = Encoding.GetEncoding("iso-8859-15"); + _littleEndian = true; + + options ??= GetDefaultOptions(); + + if(options.TryGetValue("debug", out string debugString)) + bool.TryParse(debugString, out _debug); + + if(imagePlugin.Info.SectorSize < 512) + return ErrorNumber.InvalidArgument; + + AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading superblock"); + + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return errno; + + _superblock = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(_superblock.magic == FATX_CIGAM) { - Encoding = Encoding.GetEncoding("iso-8859-15"); - _littleEndian = true; + _superblock = Marshal.ByteArrayToStructureBigEndian(sector); + _littleEndian = false; + } - options ??= GetDefaultOptions(); + if(_superblock.magic != FATX_MAGIC) + return ErrorNumber.InvalidArgument; - if(options.TryGetValue("debug", out string debugString)) - bool.TryParse(debugString, out _debug); + AaruConsole.DebugWriteLine("Xbox FAT plugin", + _littleEndian ? "Filesystem is little endian" : "Filesystem is big endian"); - if(imagePlugin.Info.SectorSize < 512) - return ErrorNumber.InvalidArgument; + int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && _littleEndian ? 8 : 1; - AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading superblock"); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "logicalSectorsPerPhysicalSectors = {0}", + logicalSectorsPerPhysicalSectors); - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + string volumeLabel = StringHandlers.CToString(_superblock.volumeLabel, + !_littleEndian ? Encoding.BigEndianUnicode : Encoding.Unicode, + true); + + XmlFsType = new FileSystemType + { + Type = "FATX filesystem", + ClusterSize = (uint)(_superblock.sectorsPerCluster * logicalSectorsPerPhysicalSectors * + imagePlugin.Info.SectorSize), + VolumeName = volumeLabel, + VolumeSerial = $"{_superblock.id:X8}" + }; + + XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / + XmlFsType.ClusterSize; + + _statfs = new FileSystemInfo + { + Blocks = XmlFsType.Clusters, + FilenameLength = MAX_FILENAME, + Files = 0, // Requires traversing all directories + FreeFiles = 0, + Id = + { + IsInt = true, + Serial32 = _superblock.magic + }, + PluginId = Id, + Type = _littleEndian ? "Xbox FAT" : "Xbox 360 FAT", + FreeBlocks = 0 // Requires traversing the FAT + }; + + AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.ClusterSize: {0}", XmlFsType.ClusterSize); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeName: {0}", XmlFsType.VolumeName); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeSerial: {0}", XmlFsType.VolumeSerial); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Blocks: {0}", _statfs.Blocks); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.FilenameLength: {0}", _statfs.FilenameLength); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Id: {0}", _statfs.Id.Serial32); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Type: {0}", _statfs.Type); + + byte[] buffer; + _fatStartSector = (FAT_START / imagePlugin.Info.SectorSize) + partition.Start; + uint fatSize; + + AaruConsole.DebugWriteLine("Xbox FAT plugin", "fatStartSector: {0}", _fatStartSector); + + if(_statfs.Blocks > MAX_XFAT16_CLUSTERS) + { + AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading FAT32"); + + fatSize = (uint)((_statfs.Blocks + 1) * sizeof(uint) / imagePlugin.Info.SectorSize); + + if((uint)((_statfs.Blocks + 1) * sizeof(uint) % imagePlugin.Info.SectorSize) > 0) + fatSize++; + + long fatClusters = fatSize * imagePlugin.Info.SectorSize / 4096; + + if(fatSize * imagePlugin.Info.SectorSize % 4096 > 0) + fatClusters++; + + fatSize = (uint)(fatClusters * 4096 / imagePlugin.Info.SectorSize); + + AaruConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize); + + errno = imagePlugin.ReadSectors(_fatStartSector, fatSize, out buffer); if(errno != ErrorNumber.NoError) return errno; - _superblock = Marshal.ByteArrayToStructureLittleEndian(sector); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "Casting FAT"); + _fat32 = MemoryMarshal.Cast(buffer).ToArray(); - if(_superblock.magic == FATX_CIGAM) - { - _superblock = Marshal.ByteArrayToStructureBigEndian(sector); - _littleEndian = false; - } + if(!_littleEndian) + for(int i = 0; i < _fat32.Length; i++) + _fat32[i] = Swapping.Swap(_fat32[i]); - if(_superblock.magic != FATX_MAGIC) + AaruConsole.DebugWriteLine("Xbox FAT plugin", "fat32[0] == FATX32_ID = {0}", _fat32[0] == FATX32_ID); + + if(_fat32[0] != FATX32_ID) return ErrorNumber.InvalidArgument; + } + else + { + AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading FAT16"); - AaruConsole.DebugWriteLine("Xbox FAT plugin", - _littleEndian ? "Filesystem is little endian" : "Filesystem is big endian"); + fatSize = (uint)((_statfs.Blocks + 1) * sizeof(ushort) / imagePlugin.Info.SectorSize); - int logicalSectorsPerPhysicalSectors = partition.Offset == 0 && _littleEndian ? 8 : 1; + if((uint)((_statfs.Blocks + 1) * sizeof(ushort) % imagePlugin.Info.SectorSize) > 0) + fatSize++; - AaruConsole.DebugWriteLine("Xbox FAT plugin", "logicalSectorsPerPhysicalSectors = {0}", - logicalSectorsPerPhysicalSectors); + long fatClusters = fatSize * imagePlugin.Info.SectorSize / 4096; - string volumeLabel = StringHandlers.CToString(_superblock.volumeLabel, - !_littleEndian ? Encoding.BigEndianUnicode : Encoding.Unicode, - true); + if(fatSize * imagePlugin.Info.SectorSize % 4096 > 0) + fatClusters++; - XmlFsType = new FileSystemType - { - Type = "FATX filesystem", - ClusterSize = (uint)(_superblock.sectorsPerCluster * logicalSectorsPerPhysicalSectors * - imagePlugin.Info.SectorSize), - VolumeName = volumeLabel, - VolumeSerial = $"{_superblock.id:X8}" - }; + fatSize = (uint)(fatClusters * 4096 / imagePlugin.Info.SectorSize); - XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / - XmlFsType.ClusterSize; + AaruConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize); - _statfs = new FileSystemInfo - { - Blocks = XmlFsType.Clusters, - FilenameLength = MAX_FILENAME, - Files = 0, // Requires traversing all directories - FreeFiles = 0, - Id = - { - IsInt = true, - Serial32 = _superblock.magic - }, - PluginId = Id, - Type = _littleEndian ? "Xbox FAT" : "Xbox 360 FAT", - FreeBlocks = 0 // Requires traversing the FAT - }; + errno = imagePlugin.ReadSectors(_fatStartSector, fatSize, out buffer); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.ClusterSize: {0}", XmlFsType.ClusterSize); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeName: {0}", XmlFsType.VolumeName); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "XmlFsType.VolumeSerial: {0}", XmlFsType.VolumeSerial); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Blocks: {0}", _statfs.Blocks); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.FilenameLength: {0}", _statfs.FilenameLength); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Id: {0}", _statfs.Id.Serial32); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "stat.Type: {0}", _statfs.Type); + if(errno != ErrorNumber.NoError) + return errno; - byte[] buffer; - _fatStartSector = (FAT_START / imagePlugin.Info.SectorSize) + partition.Start; - uint fatSize; + AaruConsole.DebugWriteLine("Xbox FAT plugin", "Casting FAT"); + _fat16 = MemoryMarshal.Cast(buffer).ToArray(); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "fatStartSector: {0}", _fatStartSector); + if(!_littleEndian) + for(int i = 0; i < _fat16.Length; i++) + _fat16[i] = Swapping.Swap(_fat16[i]); - if(_statfs.Blocks > MAX_XFAT16_CLUSTERS) - { - AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading FAT32"); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "fat16[0] == FATX16_ID = {0}", _fat16[0] == FATX16_ID); - fatSize = (uint)((_statfs.Blocks + 1) * sizeof(uint) / imagePlugin.Info.SectorSize); - - if((uint)((_statfs.Blocks + 1) * sizeof(uint) % imagePlugin.Info.SectorSize) > 0) - fatSize++; - - long fatClusters = fatSize * imagePlugin.Info.SectorSize / 4096; - - if(fatSize * imagePlugin.Info.SectorSize % 4096 > 0) - fatClusters++; - - fatSize = (uint)(fatClusters * 4096 / imagePlugin.Info.SectorSize); - - AaruConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize); - - errno = imagePlugin.ReadSectors(_fatStartSector, fatSize, out buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - AaruConsole.DebugWriteLine("Xbox FAT plugin", "Casting FAT"); - _fat32 = MemoryMarshal.Cast(buffer).ToArray(); - - if(!_littleEndian) - for(int i = 0; i < _fat32.Length; i++) - _fat32[i] = Swapping.Swap(_fat32[i]); - - AaruConsole.DebugWriteLine("Xbox FAT plugin", "fat32[0] == FATX32_ID = {0}", _fat32[0] == FATX32_ID); - - if(_fat32[0] != FATX32_ID) - return ErrorNumber.InvalidArgument; - } - else - { - AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading FAT16"); - - fatSize = (uint)((_statfs.Blocks + 1) * sizeof(ushort) / imagePlugin.Info.SectorSize); - - if((uint)((_statfs.Blocks + 1) * sizeof(ushort) % imagePlugin.Info.SectorSize) > 0) - fatSize++; - - long fatClusters = fatSize * imagePlugin.Info.SectorSize / 4096; - - if(fatSize * imagePlugin.Info.SectorSize % 4096 > 0) - fatClusters++; - - fatSize = (uint)(fatClusters * 4096 / imagePlugin.Info.SectorSize); - - AaruConsole.DebugWriteLine("Xbox FAT plugin", "FAT is {0} sectors", fatSize); - - errno = imagePlugin.ReadSectors(_fatStartSector, fatSize, out buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - AaruConsole.DebugWriteLine("Xbox FAT plugin", "Casting FAT"); - _fat16 = MemoryMarshal.Cast(buffer).ToArray(); - - if(!_littleEndian) - for(int i = 0; i < _fat16.Length; i++) - _fat16[i] = Swapping.Swap(_fat16[i]); - - AaruConsole.DebugWriteLine("Xbox FAT plugin", "fat16[0] == FATX16_ID = {0}", _fat16[0] == FATX16_ID); - - if(_fat16[0] != FATX16_ID) - return ErrorNumber.InvalidArgument; - } - - _sectorsPerCluster = (uint)(_superblock.sectorsPerCluster * logicalSectorsPerPhysicalSectors); - _imagePlugin = imagePlugin; - _firstClusterSector = _fatStartSector + fatSize; - _bytesPerCluster = _sectorsPerCluster * imagePlugin.Info.SectorSize; - - AaruConsole.DebugWriteLine("Xbox FAT plugin", "sectorsPerCluster = {0}", _sectorsPerCluster); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "bytesPerCluster = {0}", _bytesPerCluster); - AaruConsole.DebugWriteLine("Xbox FAT plugin", "firstClusterSector = {0}", _firstClusterSector); - - uint[] rootDirectoryClusters = GetClusters(_superblock.rootDirectoryCluster); - - if(rootDirectoryClusters is null) + if(_fat16[0] != FATX16_ID) return ErrorNumber.InvalidArgument; - - byte[] rootDirectoryBuffer = new byte[_bytesPerCluster * rootDirectoryClusters.Length]; - - AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading root directory"); - - for(int i = 0; i < rootDirectoryClusters.Length; i++) - { - errno = - imagePlugin.ReadSectors(_firstClusterSector + ((rootDirectoryClusters[i] - 1) * _sectorsPerCluster), - _sectorsPerCluster, out buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - Array.Copy(buffer, 0, rootDirectoryBuffer, i * _bytesPerCluster, _bytesPerCluster); - } - - _rootDirectory = new Dictionary(); - - int pos = 0; - - while(pos < rootDirectoryBuffer.Length) - { - DirectoryEntry entry = _littleEndian - ? Marshal. - ByteArrayToStructureLittleEndian< - DirectoryEntry>(rootDirectoryBuffer, pos, - Marshal.SizeOf()) - : Marshal.ByteArrayToStructureBigEndian(rootDirectoryBuffer, - pos, Marshal.SizeOf()); - - pos += Marshal.SizeOf(); - - if(entry.filenameSize == UNUSED_DIRENTRY || - entry.filenameSize == FINISHED_DIRENTRY) - break; - - if(entry.filenameSize == DELETED_DIRENTRY || - entry.filenameSize > MAX_FILENAME) - continue; - - string filename = Encoding.GetString(entry.filename, 0, entry.filenameSize); - - _rootDirectory.Add(filename, entry); - } - - _cultureInfo = new CultureInfo("en-US", false); - _directoryCache = new Dictionary>(); - _mounted = true; - - return ErrorNumber.NoError; } - /// - public ErrorNumber Unmount() + _sectorsPerCluster = (uint)(_superblock.sectorsPerCluster * logicalSectorsPerPhysicalSectors); + _imagePlugin = imagePlugin; + _firstClusterSector = _fatStartSector + fatSize; + _bytesPerCluster = _sectorsPerCluster * imagePlugin.Info.SectorSize; + + AaruConsole.DebugWriteLine("Xbox FAT plugin", "sectorsPerCluster = {0}", _sectorsPerCluster); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "bytesPerCluster = {0}", _bytesPerCluster); + AaruConsole.DebugWriteLine("Xbox FAT plugin", "firstClusterSector = {0}", _firstClusterSector); + + uint[] rootDirectoryClusters = GetClusters(_superblock.rootDirectoryCluster); + + if(rootDirectoryClusters is null) + return ErrorNumber.InvalidArgument; + + byte[] rootDirectoryBuffer = new byte[_bytesPerCluster * rootDirectoryClusters.Length]; + + AaruConsole.DebugWriteLine("Xbox FAT plugin", "Reading root directory"); + + for(int i = 0; i < rootDirectoryClusters.Length; i++) { - if(!_mounted) - return ErrorNumber.AccessDenied; + errno = + imagePlugin.ReadSectors(_firstClusterSector + ((rootDirectoryClusters[i] - 1) * _sectorsPerCluster), + _sectorsPerCluster, out buffer); - _fat16 = null; - _fat32 = null; - _imagePlugin = null; - _mounted = false; + if(errno != ErrorNumber.NoError) + return errno; - return ErrorNumber.NoError; + Array.Copy(buffer, 0, rootDirectoryBuffer, i * _bytesPerCluster, _bytesPerCluster); } - /// - public ErrorNumber StatFs(out FileSystemInfo stat) + _rootDirectory = new Dictionary(); + + int pos = 0; + + while(pos < rootDirectoryBuffer.Length) { - stat = null; + DirectoryEntry entry = _littleEndian + ? Marshal. + ByteArrayToStructureLittleEndian< + DirectoryEntry>(rootDirectoryBuffer, pos, + Marshal.SizeOf()) + : Marshal.ByteArrayToStructureBigEndian(rootDirectoryBuffer, + pos, Marshal.SizeOf()); - if(!_mounted) - return ErrorNumber.AccessDenied; + pos += Marshal.SizeOf(); - stat = _statfs.ShallowCopy(); + if(entry.filenameSize == UNUSED_DIRENTRY || + entry.filenameSize == FINISHED_DIRENTRY) + break; - return ErrorNumber.NoError; + if(entry.filenameSize == DELETED_DIRENTRY || + entry.filenameSize > MAX_FILENAME) + continue; + + string filename = Encoding.GetString(entry.filename, 0, entry.filenameSize); + + _rootDirectory.Add(filename, entry); } + + _cultureInfo = new CultureInfo("en-US", false); + _directoryCache = new Dictionary>(); + _mounted = true; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Unmount() + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + _fat16 = null; + _fat32 = null; + _imagePlugin = null; + _mounted = false; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + stat = _statfs.ShallowCopy(); + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/FFS.cs b/Aaru.Filesystems/FFS.cs index 3e8a61d7a..f41f8584e 100644 --- a/Aaru.Filesystems/FFS.cs +++ b/Aaru.Filesystems/FFS.cs @@ -45,827 +45,826 @@ using Marshal = Aaru.Helpers.Marshal; using time_t = System.Int32; using ufs_daddr_t = System.Int32; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Using information from Linux kernel headers +/// +/// Implements detection of BSD Fast File System (FFS, aka UNIX File System) +[SuppressMessage("ReSharper", "InconsistentNaming")] +public sealed class FFSPlugin : IFilesystem { - // Using information from Linux kernel headers + const uint block_size = 8192; + + // FreeBSD specifies starts at byte offsets 0, 8192, 65536 and 262144, but in other cases it's following sectors + // Without bootcode + const ulong sb_start_floppy = 0; + + // With bootcode + const ulong sb_start_boot = 1; + + // Dunno, longer boot code + const ulong sb_start_long_boot = 8; + + // Found on AT&T for MD-2D floppieslzio + const ulong sb_start_att_dsdd = 14; + + // Found on hard disks (Atari UNIX e.g.) + const ulong sb_start_piggy = 32; + + // MAGICs + // UFS magic + const uint UFS_MAGIC = 0x00011954; + + // Big-endian UFS magic + const uint UFS_CIGAM = 0x54190100; + + // BorderWare UFS + const uint UFS_MAGIC_BW = 0x0F242697; + + // Big-endian BorderWare UFS + const uint UFS_CIGAM_BW = 0x9726240F; + + // UFS2 magic + const uint UFS2_MAGIC = 0x19540119; + + // Big-endian UFS2 magic + const uint UFS2_CIGAM = 0x19015419; + + // Incomplete newfs + const uint UFS_BAD_MAGIC = 0x19960408; + + // Big-endian incomplete newfs + const uint UFS_BAD_CIGAM = 0x08049619; + /// - /// Implements detection of BSD Fast File System (FFS, aka UNIX File System) - [SuppressMessage("ReSharper", "InconsistentNaming")] - public sealed class FFSPlugin : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "BSD Fast File System (aka UNIX File System, UFS)"; + /// + public Guid Id => new("CC90D342-05DB-48A8-988C-C1FE000034A3"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint block_size = 8192; + if(2 + partition.Start >= partition.End) + return false; - // FreeBSD specifies starts at byte offsets 0, 8192, 65536 and 262144, but in other cases it's following sectors - // Without bootcode - const ulong sb_start_floppy = 0; + uint sbSizeInSectors; - // With bootcode - const ulong sb_start_boot = 1; + if(imagePlugin.Info.SectorSize == 2336 || + imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448) + sbSizeInSectors = block_size / 2048; + else + sbSizeInSectors = block_size / imagePlugin.Info.SectorSize; - // Dunno, longer boot code - const ulong sb_start_long_boot = 8; - - // Found on AT&T for MD-2D floppieslzio - const ulong sb_start_att_dsdd = 14; - - // Found on hard disks (Atari UNIX e.g.) - const ulong sb_start_piggy = 32; - - // MAGICs - // UFS magic - const uint UFS_MAGIC = 0x00011954; - - // Big-endian UFS magic - const uint UFS_CIGAM = 0x54190100; - - // BorderWare UFS - const uint UFS_MAGIC_BW = 0x0F242697; - - // Big-endian BorderWare UFS - const uint UFS_CIGAM_BW = 0x9726240F; - - // UFS2 magic - const uint UFS2_MAGIC = 0x19540119; - - // Big-endian UFS2 magic - const uint UFS2_CIGAM = 0x19015419; - - // Incomplete newfs - const uint UFS_BAD_MAGIC = 0x19960408; - - // Big-endian incomplete newfs - const uint UFS_BAD_CIGAM = 0x08049619; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "BSD Fast File System (aka UNIX File System, UFS)"; - /// - public Guid Id => new("CC90D342-05DB-48A8-988C-C1FE000034A3"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ulong[] locations = { - if(2 + partition.Start >= partition.End) - return false; + sb_start_floppy, sb_start_boot, sb_start_long_boot, sb_start_piggy, sb_start_att_dsdd, + 8192 / imagePlugin.Info.SectorSize, 65536 / imagePlugin.Info.SectorSize, + 262144 / imagePlugin.Info.SectorSize + }; - uint sbSizeInSectors; - - if(imagePlugin.Info.SectorSize == 2336 || - imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448) - sbSizeInSectors = block_size / 2048; - else - sbSizeInSectors = block_size / imagePlugin.Info.SectorSize; - - ulong[] locations = + try + { + foreach(ulong loc in locations) { - sb_start_floppy, sb_start_boot, sb_start_long_boot, sb_start_piggy, sb_start_att_dsdd, - 8192 / imagePlugin.Info.SectorSize, 65536 / imagePlugin.Info.SectorSize, - 262144 / imagePlugin.Info.SectorSize - }; - - try - { - foreach(ulong loc in locations) + if(partition.End > partition.Start + loc + sbSizeInSectors) { - if(partition.End > partition.Start + loc + sbSizeInSectors) - { - ErrorNumber errno = - imagePlugin.ReadSectors(partition.Start + loc, sbSizeInSectors, out byte[] ufsSbSectors); + ErrorNumber errno = + imagePlugin.ReadSectors(partition.Start + loc, sbSizeInSectors, out byte[] ufsSbSectors); - if(errno != ErrorNumber.NoError) - continue; + if(errno != ErrorNumber.NoError) + continue; - uint magic = BitConverter.ToUInt32(ufsSbSectors, 0x055C); + uint magic = BitConverter.ToUInt32(ufsSbSectors, 0x055C); - if(magic == UFS_MAGIC || - magic == UFS_CIGAM || - magic == UFS_MAGIC_BW || - magic == UFS_CIGAM_BW || - magic == UFS2_MAGIC || - magic == UFS2_CIGAM || - magic == UFS_BAD_MAGIC || - magic == UFS_BAD_CIGAM) - return true; - } + if(magic == UFS_MAGIC || + magic == UFS_CIGAM || + magic == UFS_MAGIC_BW || + magic == UFS_CIGAM_BW || + magic == UFS2_MAGIC || + magic == UFS2_CIGAM || + magic == UFS_BAD_MAGIC || + magic == UFS_BAD_CIGAM) + return true; } + } - return false; - } - catch(Exception) - { - return false; - } + return false; } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + catch(Exception) { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - var sbInformation = new StringBuilder(); - - uint magic = 0; - uint sb_size_in_sectors; - byte[] ufs_sb_sectors; - ulong sb_offset = partition.Start; - bool fs_type_42bsd = false; - bool fs_type_43bsd = false; - bool fs_type_44bsd = false; - bool fs_type_ufs = false; - bool fs_type_ufs2 = false; - bool fs_type_sun = false; - bool fs_type_sun86 = false; - - if(imagePlugin.Info.SectorSize == 2336 || - imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448) - sb_size_in_sectors = block_size / 2048; - else - sb_size_in_sectors = block_size / imagePlugin.Info.SectorSize; - - ulong[] locations = - { - sb_start_floppy, sb_start_boot, sb_start_long_boot, sb_start_piggy, sb_start_att_dsdd, - 8192 / imagePlugin.Info.SectorSize, 65536 / imagePlugin.Info.SectorSize, - 262144 / imagePlugin.Info.SectorSize - }; - - ErrorNumber errno; - - foreach(ulong loc in locations.Where(loc => partition.End > partition.Start + loc + sb_size_in_sectors)) - { - errno = imagePlugin.ReadSectors(partition.Start + loc, sb_size_in_sectors, out ufs_sb_sectors); - - if(errno != ErrorNumber.NoError) - continue; - - magic = BitConverter.ToUInt32(ufs_sb_sectors, 0x055C); - - if(magic == UFS_MAGIC || - magic == UFS_CIGAM || - magic == UFS_MAGIC_BW || - magic == UFS_CIGAM_BW || - magic == UFS2_MAGIC || - magic == UFS2_CIGAM || - magic == UFS_BAD_MAGIC || - magic == UFS_BAD_CIGAM) - { - sb_offset = partition.Start + loc; - - break; - } - - magic = 0; - } - - if(magic == 0) - { - information = "Not a UFS filesystem, I shouldn't have arrived here!"; - - return; - } - - XmlFsType = new FileSystemType(); - - switch(magic) - { - case UFS_MAGIC: - sbInformation.AppendLine("UFS filesystem"); - XmlFsType.Type = "UFS"; - - break; - case UFS_CIGAM: - sbInformation.AppendLine("Big-endian UFS filesystem"); - XmlFsType.Type = "UFS"; - - break; - case UFS_MAGIC_BW: - sbInformation.AppendLine("BorderWare UFS filesystem"); - XmlFsType.Type = "UFS"; - - break; - case UFS_CIGAM_BW: - sbInformation.AppendLine("Big-endian BorderWare UFS filesystem"); - XmlFsType.Type = "UFS"; - - break; - case UFS2_MAGIC: - sbInformation.AppendLine("UFS2 filesystem"); - XmlFsType.Type = "UFS2"; - - break; - case UFS2_CIGAM: - sbInformation.AppendLine("Big-endian UFS2 filesystem"); - XmlFsType.Type = "UFS2"; - - break; - case UFS_BAD_MAGIC: - sbInformation.AppendLine("Incompletely initialized UFS filesystem"); - sbInformation.AppendLine("BEWARE!!! Following information may be completely wrong!"); - XmlFsType.Type = "UFS"; - - break; - case UFS_BAD_CIGAM: - sbInformation.AppendLine("Incompletely initialized big-endian UFS filesystem"); - sbInformation.AppendLine("BEWARE!!! Following information may be completely wrong!"); - XmlFsType.Type = "UFS"; - - break; - } - - // Fun with seeking follows on superblock reading! - errno = imagePlugin.ReadSectors(sb_offset, sb_size_in_sectors, out ufs_sb_sectors); - - if(errno != ErrorNumber.NoError) - return; - - SuperBlock sb = Marshal.ByteArrayToStructureLittleEndian(ufs_sb_sectors); - - SuperBlock bs_sfu = Marshal.ByteArrayToStructureBigEndian(ufs_sb_sectors); - - if((bs_sfu.fs_magic == UFS_MAGIC && sb.fs_magic == UFS_CIGAM) || - (bs_sfu.fs_magic == UFS_MAGIC_BW && sb.fs_magic == UFS_CIGAM_BW) || - (bs_sfu.fs_magic == UFS2_MAGIC && sb.fs_magic == UFS2_CIGAM) || - (bs_sfu.fs_magic == UFS_BAD_MAGIC && sb.fs_magic == UFS_BAD_CIGAM)) - { - sb = bs_sfu; - sb.fs_old_cstotal.cs_nbfree = Swapping.Swap(sb.fs_old_cstotal.cs_nbfree); - sb.fs_old_cstotal.cs_ndir = Swapping.Swap(sb.fs_old_cstotal.cs_ndir); - sb.fs_old_cstotal.cs_nffree = Swapping.Swap(sb.fs_old_cstotal.cs_nffree); - sb.fs_old_cstotal.cs_nifree = Swapping.Swap(sb.fs_old_cstotal.cs_nifree); - sb.fs_cstotal.cs_numclusters = Swapping.Swap(sb.fs_cstotal.cs_numclusters); - sb.fs_cstotal.cs_nbfree = Swapping.Swap(sb.fs_cstotal.cs_nbfree); - sb.fs_cstotal.cs_ndir = Swapping.Swap(sb.fs_cstotal.cs_ndir); - sb.fs_cstotal.cs_nffree = Swapping.Swap(sb.fs_cstotal.cs_nffree); - sb.fs_cstotal.cs_nifree = Swapping.Swap(sb.fs_cstotal.cs_nifree); - sb.fs_cstotal.cs_spare[0] = Swapping.Swap(sb.fs_cstotal.cs_spare[0]); - sb.fs_cstotal.cs_spare[1] = Swapping.Swap(sb.fs_cstotal.cs_spare[1]); - sb.fs_cstotal.cs_spare[2] = Swapping.Swap(sb.fs_cstotal.cs_spare[2]); - } - - AaruConsole.DebugWriteLine("FFS plugin", "sb offset: 0x{0:X8}", sb_offset); - AaruConsole.DebugWriteLine("FFS plugin", "fs_rlink: 0x{0:X8}", sb.fs_rlink); - AaruConsole.DebugWriteLine("FFS plugin", "fs_sblkno: 0x{0:X8}", sb.fs_sblkno); - AaruConsole.DebugWriteLine("FFS plugin", "fs_cblkno: 0x{0:X8}", sb.fs_cblkno); - AaruConsole.DebugWriteLine("FFS plugin", "fs_iblkno: 0x{0:X8}", sb.fs_iblkno); - AaruConsole.DebugWriteLine("FFS plugin", "fs_dblkno: 0x{0:X8}", sb.fs_dblkno); - AaruConsole.DebugWriteLine("FFS plugin", "fs_size: 0x{0:X8}", sb.fs_size); - AaruConsole.DebugWriteLine("FFS plugin", "fs_dsize: 0x{0:X8}", sb.fs_dsize); - AaruConsole.DebugWriteLine("FFS plugin", "fs_ncg: 0x{0:X8}", sb.fs_ncg); - AaruConsole.DebugWriteLine("FFS plugin", "fs_bsize: 0x{0:X8}", sb.fs_bsize); - AaruConsole.DebugWriteLine("FFS plugin", "fs_fsize: 0x{0:X8}", sb.fs_fsize); - AaruConsole.DebugWriteLine("FFS plugin", "fs_frag: 0x{0:X8}", sb.fs_frag); - AaruConsole.DebugWriteLine("FFS plugin", "fs_minfree: 0x{0:X8}", sb.fs_minfree); - AaruConsole.DebugWriteLine("FFS plugin", "fs_bmask: 0x{0:X8}", sb.fs_bmask); - AaruConsole.DebugWriteLine("FFS plugin", "fs_fmask: 0x{0:X8}", sb.fs_fmask); - AaruConsole.DebugWriteLine("FFS plugin", "fs_bshift: 0x{0:X8}", sb.fs_bshift); - AaruConsole.DebugWriteLine("FFS plugin", "fs_fshift: 0x{0:X8}", sb.fs_fshift); - AaruConsole.DebugWriteLine("FFS plugin", "fs_maxcontig: 0x{0:X8}", sb.fs_maxcontig); - AaruConsole.DebugWriteLine("FFS plugin", "fs_maxbpg: 0x{0:X8}", sb.fs_maxbpg); - AaruConsole.DebugWriteLine("FFS plugin", "fs_fragshift: 0x{0:X8}", sb.fs_fragshift); - AaruConsole.DebugWriteLine("FFS plugin", "fs_fsbtodb: 0x{0:X8}", sb.fs_fsbtodb); - AaruConsole.DebugWriteLine("FFS plugin", "fs_sbsize: 0x{0:X8}", sb.fs_sbsize); - AaruConsole.DebugWriteLine("FFS plugin", "fs_csmask: 0x{0:X8}", sb.fs_csmask); - AaruConsole.DebugWriteLine("FFS plugin", "fs_csshift: 0x{0:X8}", sb.fs_csshift); - AaruConsole.DebugWriteLine("FFS plugin", "fs_nindir: 0x{0:X8}", sb.fs_nindir); - AaruConsole.DebugWriteLine("FFS plugin", "fs_inopb: 0x{0:X8}", sb.fs_inopb); - AaruConsole.DebugWriteLine("FFS plugin", "fs_optim: 0x{0:X8}", sb.fs_optim); - AaruConsole.DebugWriteLine("FFS plugin", "fs_id_1: 0x{0:X8}", sb.fs_id_1); - AaruConsole.DebugWriteLine("FFS plugin", "fs_id_2: 0x{0:X8}", sb.fs_id_2); - AaruConsole.DebugWriteLine("FFS plugin", "fs_csaddr: 0x{0:X8}", sb.fs_csaddr); - AaruConsole.DebugWriteLine("FFS plugin", "fs_cssize: 0x{0:X8}", sb.fs_cssize); - AaruConsole.DebugWriteLine("FFS plugin", "fs_cgsize: 0x{0:X8}", sb.fs_cgsize); - AaruConsole.DebugWriteLine("FFS plugin", "fs_ipg: 0x{0:X8}", sb.fs_ipg); - AaruConsole.DebugWriteLine("FFS plugin", "fs_fpg: 0x{0:X8}", sb.fs_fpg); - AaruConsole.DebugWriteLine("FFS plugin", "fs_fmod: 0x{0:X2}", sb.fs_fmod); - AaruConsole.DebugWriteLine("FFS plugin", "fs_clean: 0x{0:X2}", sb.fs_clean); - AaruConsole.DebugWriteLine("FFS plugin", "fs_ronly: 0x{0:X2}", sb.fs_ronly); - AaruConsole.DebugWriteLine("FFS plugin", "fs_flags: 0x{0:X2}", sb.fs_flags); - AaruConsole.DebugWriteLine("FFS plugin", "fs_magic: 0x{0:X8}", sb.fs_magic); - - if(sb.fs_magic == UFS2_MAGIC) - fs_type_ufs2 = true; - else - { - const uint - SunOSEpoch = - 0x1A54C580; // We are supposing there cannot be a Sun's fs created before 1/1/1982 00:00:00 - - fs_type_43bsd = - true; // There is no way of knowing this is the version, but there is of knowing it is not. - - if(sb.fs_link > 0) - { - fs_type_42bsd = true; // It was used in 4.2BSD - fs_type_43bsd = false; - } - - if((sb.fs_maxfilesize & 0xFFFFFFFF) > SunOSEpoch && - DateHandlers.UnixUnsignedToDateTime(sb.fs_maxfilesize & 0xFFFFFFFF) < DateTime.Now) - { - fs_type_42bsd = false; - fs_type_sun = true; - fs_type_43bsd = false; - } - - // This is for sure, as it is shared with a sectors/track with non-x86 SunOS, Epoch is absurdly high for that - if(sb.fs_old_npsect > SunOSEpoch && - DateHandlers.UnixToDateTime(sb.fs_old_npsect) < DateTime.Now) - { - fs_type_42bsd = false; - fs_type_sun86 = true; - fs_type_sun = false; - fs_type_43bsd = false; - } - - if(sb.fs_cgrotor > 0x00000000 && - (uint)sb.fs_cgrotor < 0xFFFFFFFF) - { - fs_type_42bsd = false; - fs_type_sun = false; - fs_type_sun86 = false; - fs_type_ufs = true; - fs_type_43bsd = false; - } - - // 4.3BSD code does not use these fields, they are always set up to 0 - fs_type_43bsd &= sb.fs_id_2 == 0 && sb.fs_id_1 == 0; - - // This is the only 4.4BSD inode format - fs_type_44bsd |= sb.fs_old_inodefmt == 2; - } - - if(!fs_type_ufs2) - { - sbInformation.AppendLine("There are a lot of variants of UFS using overlapped values on same fields"); - - sbInformation. - AppendLine("I will try to guess which one it is, but unless it's UFS2, I may be surely wrong"); - } - - if(fs_type_42bsd) - sbInformation.AppendLine("Guessed as 42BSD FFS"); - - if(fs_type_43bsd) - sbInformation.AppendLine("Guessed as 43BSD FFS"); - - if(fs_type_44bsd) - sbInformation.AppendLine("Guessed as 44BSD FFS"); - - if(fs_type_sun) - sbInformation.AppendLine("Guessed as SunOS FFS"); - - if(fs_type_sun86) - sbInformation.AppendLine("Guessed as SunOS/x86 FFS"); - - if(fs_type_ufs) - sbInformation.AppendLine("Guessed as UFS"); - - if(fs_type_42bsd) - sbInformation.AppendFormat("Linked list of filesystems: 0x{0:X8}", sb.fs_link).AppendLine(); - - sbInformation.AppendFormat("Superblock LBA: {0}", sb.fs_sblkno).AppendLine(); - sbInformation.AppendFormat("Cylinder-block LBA: {0}", sb.fs_cblkno).AppendLine(); - sbInformation.AppendFormat("inode-block LBA: {0}", sb.fs_iblkno).AppendLine(); - sbInformation.AppendFormat("First data block LBA: {0}", sb.fs_dblkno).AppendLine(); - sbInformation.AppendFormat("Cylinder group offset in cylinder: {0}", sb.fs_old_cgoffset).AppendLine(); - - sbInformation.AppendFormat("Volume last written on {0}", DateHandlers.UnixToDateTime(sb.fs_old_time)). - AppendLine(); - - XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(sb.fs_old_time); - XmlFsType.ModificationDateSpecified = true; - - sbInformation.AppendFormat("{0} blocks in volume ({1} bytes)", sb.fs_old_size, - (long)sb.fs_old_size * sb.fs_fsize).AppendLine(); - - XmlFsType.Clusters = (ulong)sb.fs_old_size; - XmlFsType.ClusterSize = (uint)sb.fs_fsize; - - sbInformation.AppendFormat("{0} data blocks in volume ({1} bytes)", sb.fs_old_dsize, - (long)sb.fs_old_dsize * sb.fs_fsize).AppendLine(); - - sbInformation.AppendFormat("{0} cylinder groups in volume", sb.fs_ncg).AppendLine(); - sbInformation.AppendFormat("{0} bytes in a basic block", sb.fs_bsize).AppendLine(); - sbInformation.AppendFormat("{0} bytes in a frag block", sb.fs_fsize).AppendLine(); - sbInformation.AppendFormat("{0} frags in a block", sb.fs_frag).AppendLine(); - sbInformation.AppendFormat("{0}% of blocks must be free", sb.fs_minfree).AppendLine(); - sbInformation.AppendFormat("{0}ms for optimal next block", sb.fs_old_rotdelay).AppendLine(); - - sbInformation.AppendFormat("disk rotates {0} times per second ({1}rpm)", sb.fs_old_rps, sb.fs_old_rps * 60). - AppendLine(); - - /* sbInformation.AppendFormat("fs_bmask: 0x{0:X8}", sb.fs_bmask).AppendLine(); - sbInformation.AppendFormat("fs_fmask: 0x{0:X8}", sb.fs_fmask).AppendLine(); - sbInformation.AppendFormat("fs_bshift: 0x{0:X8}", sb.fs_bshift).AppendLine(); - sbInformation.AppendFormat("fs_fshift: 0x{0:X8}", sb.fs_fshift).AppendLine();*/ - sbInformation.AppendFormat("{0} contiguous blocks at maximum", sb.fs_maxcontig).AppendLine(); - sbInformation.AppendFormat("{0} blocks per cylinder group at maximum", sb.fs_maxbpg).AppendLine(); - sbInformation.AppendFormat("Superblock is {0} bytes", sb.fs_sbsize).AppendLine(); - sbInformation.AppendFormat("NINDIR: 0x{0:X8}", sb.fs_nindir).AppendLine(); - sbInformation.AppendFormat("INOPB: 0x{0:X8}", sb.fs_inopb).AppendLine(); - sbInformation.AppendFormat("NSPF: 0x{0:X8}", sb.fs_old_nspf).AppendLine(); - - switch(sb.fs_optim) - { - case 0: - sbInformation.AppendLine("Filesystem will minimize allocation time"); - - break; - case 1: - sbInformation.AppendLine("Filesystem will minimize volume fragmentation"); - - break; - default: - sbInformation.AppendFormat("Unknown optimization value: 0x{0:X8}", sb.fs_optim).AppendLine(); - - break; - } - - if(fs_type_sun) - sbInformation.AppendFormat("{0} sectors/track", sb.fs_old_npsect).AppendLine(); - else if(fs_type_sun86) - sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(sb.fs_old_npsect)). - AppendLine(); - - sbInformation.AppendFormat("Hardware sector interleave: {0}", sb.fs_old_interleave).AppendLine(); - sbInformation.AppendFormat("Sector 0 skew: {0}/track", sb.fs_old_trackskew).AppendLine(); - - if(!fs_type_43bsd && - sb.fs_id_1 > 0 && - sb.fs_id_2 > 0) - sbInformation.AppendFormat("Volume ID: 0x{0:X8}{1:X8}", sb.fs_id_1, sb.fs_id_2).AppendLine(); - else if(fs_type_43bsd && - sb.fs_id_1 > 0 && - sb.fs_id_2 > 0) - { - sbInformation.AppendFormat("{0} µsec for head switch", sb.fs_id_1).AppendLine(); - sbInformation.AppendFormat("{0} µsec for track-to-track seek", sb.fs_id_2).AppendLine(); - } - - sbInformation.AppendFormat("Cylinder group summary LBA: {0}", sb.fs_old_csaddr).AppendLine(); - sbInformation.AppendFormat("{0} bytes in cylinder group summary", sb.fs_cssize).AppendLine(); - sbInformation.AppendFormat("{0} bytes in cylinder group", sb.fs_cgsize).AppendLine(); - sbInformation.AppendFormat("{0} tracks/cylinder", sb.fs_old_ntrak).AppendLine(); - sbInformation.AppendFormat("{0} sectors/track", sb.fs_old_nsect).AppendLine(); - sbInformation.AppendFormat("{0} sectors/cylinder", sb.fs_old_spc).AppendLine(); - sbInformation.AppendFormat("{0} cylinder in volume", sb.fs_old_ncyl).AppendLine(); - sbInformation.AppendFormat("{0} cylinders/group", sb.fs_old_cpg).AppendLine(); - sbInformation.AppendFormat("{0} inodes per cylinder group", sb.fs_ipg).AppendLine(); - sbInformation.AppendFormat("{0} blocks per group", sb.fs_fpg / sb.fs_frag).AppendLine(); - sbInformation.AppendFormat("{0} directories", sb.fs_old_cstotal.cs_ndir).AppendLine(); - - sbInformation.AppendFormat("{0} free blocks ({1} bytes)", sb.fs_old_cstotal.cs_nbfree, - (long)sb.fs_old_cstotal.cs_nbfree * sb.fs_fsize).AppendLine(); - - XmlFsType.FreeClusters = (ulong)sb.fs_old_cstotal.cs_nbfree; - XmlFsType.FreeClustersSpecified = true; - sbInformation.AppendFormat("{0} free inodes", sb.fs_old_cstotal.cs_nifree).AppendLine(); - sbInformation.AppendFormat("{0} free frags", sb.fs_old_cstotal.cs_nffree).AppendLine(); - - if(sb.fs_fmod == 1) - { - sbInformation.AppendLine("Superblock is under modification"); - XmlFsType.Dirty = true; - } - - if(sb.fs_clean == 1) - sbInformation.AppendLine("Volume is clean"); - - if(sb.fs_ronly == 1) - sbInformation.AppendLine("Volume is read-only"); - - sbInformation.AppendFormat("Volume flags: 0x{0:X2}", sb.fs_flags).AppendLine(); - - if(fs_type_ufs) - sbInformation.AppendFormat("Volume last mounted on \"{0}\"", StringHandlers.CToString(sb.fs_fsmnt)). - AppendLine(); - else if(fs_type_ufs2) - { - sbInformation.AppendFormat("Volume last mounted on \"{0}\"", StringHandlers.CToString(sb.fs_fsmnt)). - AppendLine(); - - sbInformation.AppendFormat("Volume name: \"{0}\"", StringHandlers.CToString(sb.fs_volname)). - AppendLine(); - - XmlFsType.VolumeName = StringHandlers.CToString(sb.fs_volname); - sbInformation.AppendFormat("Volume ID: 0x{0:X16}", sb.fs_swuid).AppendLine(); - - //xmlFSType.VolumeSerial = string.Format("{0:X16}", sb.fs_swuid); - sbInformation.AppendFormat("Last searched cylinder group: {0}", sb.fs_cgrotor).AppendLine(); - sbInformation.AppendFormat("{0} contiguously allocated directories", sb.fs_contigdirs).AppendLine(); - sbInformation.AppendFormat("Standard superblock LBA: {0}", sb.fs_sblkno).AppendLine(); - sbInformation.AppendFormat("{0} directories", sb.fs_cstotal.cs_ndir).AppendLine(); - - sbInformation.AppendFormat("{0} free blocks ({1} bytes)", sb.fs_cstotal.cs_nbfree, - sb.fs_cstotal.cs_nbfree * sb.fs_fsize).AppendLine(); - - XmlFsType.FreeClusters = (ulong)sb.fs_cstotal.cs_nbfree; - XmlFsType.FreeClustersSpecified = true; - sbInformation.AppendFormat("{0} free inodes", sb.fs_cstotal.cs_nifree).AppendLine(); - sbInformation.AppendFormat("{0} free frags", sb.fs_cstotal.cs_nffree).AppendLine(); - sbInformation.AppendFormat("{0} free clusters", sb.fs_cstotal.cs_numclusters).AppendLine(); - - sbInformation.AppendFormat("Volume last written on {0}", DateHandlers.UnixToDateTime(sb.fs_time)). - AppendLine(); - - XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(sb.fs_time); - XmlFsType.ModificationDateSpecified = true; - - sbInformation.AppendFormat("{0} blocks ({1} bytes)", sb.fs_size, sb.fs_size * sb.fs_fsize).AppendLine(); - - XmlFsType.Clusters = (ulong)sb.fs_size; - - sbInformation.AppendFormat("{0} data blocks ({1} bytes)", sb.fs_dsize, sb.fs_dsize * sb.fs_fsize). - AppendLine(); - - sbInformation.AppendFormat("Cylinder group summary area LBA: {0}", sb.fs_csaddr).AppendLine(); - sbInformation.AppendFormat("{0} blocks pending of being freed", sb.fs_pendingblocks).AppendLine(); - sbInformation.AppendFormat("{0} inodes pending of being freed", sb.fs_pendinginodes).AppendLine(); - } - - if(fs_type_sun) - sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(sb.fs_old_npsect)). - AppendLine(); - else if(fs_type_sun86) - sbInformation.AppendFormat("{0} sectors/track", sb.fs_state).AppendLine(); - else if(fs_type_44bsd) - { - sbInformation.AppendFormat("{0} blocks on cluster summary array", sb.fs_contigsumsize).AppendLine(); - - sbInformation.AppendFormat("Maximum length of a symbolic link: {0}", sb.fs_maxsymlinklen).AppendLine(); - - sbInformation.AppendFormat("A file can be {0} bytes at max", sb.fs_maxfilesize).AppendLine(); - - sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(sb.fs_state)). - AppendLine(); - } - - if(sb.fs_old_nrpos > 0) - sbInformation.AppendFormat("{0} rotational positions", sb.fs_old_nrpos).AppendLine(); - - if(sb.fs_old_rotbloff > 0) - sbInformation.AppendFormat("{0} blocks per rotation", sb.fs_old_rotbloff).AppendLine(); - - information = sbInformation.ToString(); - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct csum - { - /// number of directories - public int cs_ndir; - /// number of free blocks - public int cs_nbfree; - /// number of free inodes - public int cs_nifree; - /// number of free frags - public int cs_nffree; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct csum_total - { - /// number of directories - public long cs_ndir; - /// number of free blocks - public long cs_nbfree; - /// number of free inodes - public long cs_nifree; - /// number of free frags - public long cs_nffree; - /// number of free clusters - public long cs_numclusters; - /// future expansion - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly long[] cs_spare; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct SuperBlock - { - /// linked list of file systems - public readonly uint fs_link; - /// used for incore super blocks on Sun: uint fs_rolled; // logging only: fs fully rolled - public readonly uint fs_rlink; - /// addr of super-block in filesys - public readonly int fs_sblkno; - /// offset of cyl-block in filesys - public readonly int fs_cblkno; - /// offset of inode-blocks in filesys - public readonly int fs_iblkno; - /// offset of first data after cg - public readonly int fs_dblkno; - /// cylinder group offset in cylinder - public readonly int fs_old_cgoffset; - /// used to calc mod fs_ntrak - public readonly int fs_old_cgmask; - /// last time written - public readonly int fs_old_time; - /// number of blocks in fs - public readonly int fs_old_size; - /// number of data blocks in fs - public readonly int fs_old_dsize; - /// number of cylinder groups - public readonly int fs_ncg; - /// size of basic blocks in fs - public readonly int fs_bsize; - /// size of frag blocks in fs - public readonly int fs_fsize; - /// number of frags in a block in fs - public readonly int fs_frag; - /* these are configuration parameters */ - /// minimum percentage of free blocks - public readonly int fs_minfree; - /// num of ms for optimal next block - public readonly int fs_old_rotdelay; - /// disk revolutions per second - public readonly int fs_old_rps; - /* these fields can be computed from the others */ - /// ``blkoff'' calc of blk offsets - public readonly int fs_bmask; - /// ``fragoff'' calc of frag offsets - public readonly int fs_fmask; - /// ``lblkno'' calc of logical blkno - public readonly int fs_bshift; - /// ``numfrags'' calc number of frags - public readonly int fs_fshift; - /* these are configuration parameters */ - /// max number of contiguous blks - public readonly int fs_maxcontig; - /// max number of blks per cyl group - public readonly int fs_maxbpg; - /* these fields can be computed from the others */ - /// block to frag shift - public readonly int fs_fragshift; - /// fsbtodb and dbtofsb shift constant - public readonly int fs_fsbtodb; - /// actual size of super block - public readonly int fs_sbsize; - /// csum block offset - public readonly int fs_csmask; - /// csum block number - public readonly int fs_csshift; - /// value of NINDIR - public readonly int fs_nindir; - /// value of INOPB - public readonly uint fs_inopb; - /// value of NSPF - public readonly int fs_old_nspf; - /* yet another configuration parameter */ - /// optimization preference, see below On SVR: int fs_state; // file system state - public readonly int fs_optim; - /// # sectors/track including spares - public readonly int fs_old_npsect; - /// hardware sector interleave - public readonly int fs_old_interleave; - /// sector 0 skew, per track On A/UX: int fs_state; // file system state - public readonly int fs_old_trackskew; - /// unique filesystem id On old: int fs_headswitch; // head switch time, usec - public readonly int fs_id_1; - /// unique filesystem id On old: int fs_trkseek; // track-to-track seek, usec - public readonly int fs_id_2; - /* sizes determined by number of cylinder groups and their sizes */ - /// blk addr of cyl grp summary area - public readonly int fs_old_csaddr; - /// size of cyl grp summary area - public readonly int fs_cssize; - /// cylinder group size - public readonly int fs_cgsize; - /* these fields are derived from the hardware */ - /// tracks per cylinder - public readonly int fs_old_ntrak; - /// sectors per track - public readonly int fs_old_nsect; - /// sectors per cylinder - public readonly int fs_old_spc; - /* this comes from the disk driver partitioning */ - /// cylinders in filesystem - public readonly int fs_old_ncyl; - /* these fields can be computed from the others */ - /// cylinders per group - public readonly int fs_old_cpg; - /// inodes per group - public readonly int fs_ipg; - /// blocks per group * fs_frag - public readonly int fs_fpg; - /* this data must be re-computed after crashes */ - /// cylinder summary information - public csum fs_old_cstotal; - /* these fields are cleared at mount time */ - /// super block modified flag - public readonly sbyte fs_fmod; - /// filesystem is clean flag - public readonly sbyte fs_clean; - /// mounted read-only flag - public readonly sbyte fs_ronly; - /// old FS_ flags - public readonly sbyte fs_old_flags; - /// name mounted on - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 468)] - public readonly byte[] fs_fsmnt; - /// volume name - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] fs_volname; - /// system-wide uid - public readonly ulong fs_swuid; - /// due to alignment of fs_swuid - public readonly int fs_pad; - /* these fields retain the current block allocation info */ - /// last cg searched - public readonly int fs_cgrotor; - /// padding; was list of fs_cs buffers - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] - public readonly uint[] fs_ocsp; - /// (u) # of contig. allocated dirs - public readonly uint fs_contigdirs; - /// (u) cg summary info buffer - public readonly uint fs_csp; - /// (u) max cluster in each cyl group - public readonly uint fs_maxcluster; - /// (u) used by snapshots to track fs - public readonly uint fs_active; - /// cyl per cycle in postbl - public readonly int fs_old_cpc; - /// maximum blocking factor permitted - public readonly int fs_maxbsize; - /// number of unreferenced inodes - public readonly long fs_unrefs; - /// size of underlying GEOM provider - public readonly long fs_providersize; - /// size of area reserved for metadata - public readonly long fs_metaspace; - /// old rotation block list head - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] - public readonly long[] fs_sparecon64; - /// byte offset of standard superblock - public readonly long fs_sblockloc; - /// (u) cylinder summary information - public csum_total fs_cstotal; - /// last time written - public readonly long fs_time; - /// number of blocks in fs - public readonly long fs_size; - /// number of data blocks in fs - public readonly long fs_dsize; - /// blk addr of cyl grp summary area - public readonly long fs_csaddr; - /// (u) blocks being freed - public readonly long fs_pendingblocks; - /// (u) inodes being freed - public readonly uint fs_pendinginodes; - /// list of snapshot inode numbers - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly uint[] fs_snapinum; - /// expected average file size - public readonly uint fs_avgfilesize; - /// expected # of files per directory - public readonly uint fs_avgfpdir; - /// save real cg size to use fs_bsize - public readonly int fs_save_cgsize; - /// Last mount or fsck time. - public readonly long fs_mtime; - /// SUJ free list - public readonly int fs_sujfree; - /// reserved for future constants - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 23)] - public readonly int[] fs_sparecon32; - /// see FS_ flags below - public readonly int fs_flags; - /// size of cluster summary array - public readonly int fs_contigsumsize; - /// max length of an internal symlink - public readonly int fs_maxsymlinklen; - /// format of on-disk inodes - public readonly int fs_old_inodefmt; - /// maximum representable file size - public readonly ulong fs_maxfilesize; - /// ~fs_bmask for use with 64-bit size - public readonly long fs_qbmask; - /// ~fs_fmask for use with 64-bit size - public readonly long fs_qfmask; - /// validate fs_clean field - public readonly int fs_state; - /// format of positional layout tables - public readonly int fs_old_postblformat; - /// number of rotational positions - public readonly int fs_old_nrpos; - /// (short) rotation block list head - public readonly int fs_old_postbloff; - /// (uchar_t) blocks for each rotation - public readonly int fs_old_rotbloff; - /// magic number - public readonly uint fs_magic; - /// list of blocks for each rotation - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] - public readonly byte[] fs_rotbl; + return false; } } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + var sbInformation = new StringBuilder(); + + uint magic = 0; + uint sb_size_in_sectors; + byte[] ufs_sb_sectors; + ulong sb_offset = partition.Start; + bool fs_type_42bsd = false; + bool fs_type_43bsd = false; + bool fs_type_44bsd = false; + bool fs_type_ufs = false; + bool fs_type_ufs2 = false; + bool fs_type_sun = false; + bool fs_type_sun86 = false; + + if(imagePlugin.Info.SectorSize == 2336 || + imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448) + sb_size_in_sectors = block_size / 2048; + else + sb_size_in_sectors = block_size / imagePlugin.Info.SectorSize; + + ulong[] locations = + { + sb_start_floppy, sb_start_boot, sb_start_long_boot, sb_start_piggy, sb_start_att_dsdd, + 8192 / imagePlugin.Info.SectorSize, 65536 / imagePlugin.Info.SectorSize, + 262144 / imagePlugin.Info.SectorSize + }; + + ErrorNumber errno; + + foreach(ulong loc in locations.Where(loc => partition.End > partition.Start + loc + sb_size_in_sectors)) + { + errno = imagePlugin.ReadSectors(partition.Start + loc, sb_size_in_sectors, out ufs_sb_sectors); + + if(errno != ErrorNumber.NoError) + continue; + + magic = BitConverter.ToUInt32(ufs_sb_sectors, 0x055C); + + if(magic == UFS_MAGIC || + magic == UFS_CIGAM || + magic == UFS_MAGIC_BW || + magic == UFS_CIGAM_BW || + magic == UFS2_MAGIC || + magic == UFS2_CIGAM || + magic == UFS_BAD_MAGIC || + magic == UFS_BAD_CIGAM) + { + sb_offset = partition.Start + loc; + + break; + } + + magic = 0; + } + + if(magic == 0) + { + information = "Not a UFS filesystem, I shouldn't have arrived here!"; + + return; + } + + XmlFsType = new FileSystemType(); + + switch(magic) + { + case UFS_MAGIC: + sbInformation.AppendLine("UFS filesystem"); + XmlFsType.Type = "UFS"; + + break; + case UFS_CIGAM: + sbInformation.AppendLine("Big-endian UFS filesystem"); + XmlFsType.Type = "UFS"; + + break; + case UFS_MAGIC_BW: + sbInformation.AppendLine("BorderWare UFS filesystem"); + XmlFsType.Type = "UFS"; + + break; + case UFS_CIGAM_BW: + sbInformation.AppendLine("Big-endian BorderWare UFS filesystem"); + XmlFsType.Type = "UFS"; + + break; + case UFS2_MAGIC: + sbInformation.AppendLine("UFS2 filesystem"); + XmlFsType.Type = "UFS2"; + + break; + case UFS2_CIGAM: + sbInformation.AppendLine("Big-endian UFS2 filesystem"); + XmlFsType.Type = "UFS2"; + + break; + case UFS_BAD_MAGIC: + sbInformation.AppendLine("Incompletely initialized UFS filesystem"); + sbInformation.AppendLine("BEWARE!!! Following information may be completely wrong!"); + XmlFsType.Type = "UFS"; + + break; + case UFS_BAD_CIGAM: + sbInformation.AppendLine("Incompletely initialized big-endian UFS filesystem"); + sbInformation.AppendLine("BEWARE!!! Following information may be completely wrong!"); + XmlFsType.Type = "UFS"; + + break; + } + + // Fun with seeking follows on superblock reading! + errno = imagePlugin.ReadSectors(sb_offset, sb_size_in_sectors, out ufs_sb_sectors); + + if(errno != ErrorNumber.NoError) + return; + + SuperBlock sb = Marshal.ByteArrayToStructureLittleEndian(ufs_sb_sectors); + + SuperBlock bs_sfu = Marshal.ByteArrayToStructureBigEndian(ufs_sb_sectors); + + if((bs_sfu.fs_magic == UFS_MAGIC && sb.fs_magic == UFS_CIGAM) || + (bs_sfu.fs_magic == UFS_MAGIC_BW && sb.fs_magic == UFS_CIGAM_BW) || + (bs_sfu.fs_magic == UFS2_MAGIC && sb.fs_magic == UFS2_CIGAM) || + (bs_sfu.fs_magic == UFS_BAD_MAGIC && sb.fs_magic == UFS_BAD_CIGAM)) + { + sb = bs_sfu; + sb.fs_old_cstotal.cs_nbfree = Swapping.Swap(sb.fs_old_cstotal.cs_nbfree); + sb.fs_old_cstotal.cs_ndir = Swapping.Swap(sb.fs_old_cstotal.cs_ndir); + sb.fs_old_cstotal.cs_nffree = Swapping.Swap(sb.fs_old_cstotal.cs_nffree); + sb.fs_old_cstotal.cs_nifree = Swapping.Swap(sb.fs_old_cstotal.cs_nifree); + sb.fs_cstotal.cs_numclusters = Swapping.Swap(sb.fs_cstotal.cs_numclusters); + sb.fs_cstotal.cs_nbfree = Swapping.Swap(sb.fs_cstotal.cs_nbfree); + sb.fs_cstotal.cs_ndir = Swapping.Swap(sb.fs_cstotal.cs_ndir); + sb.fs_cstotal.cs_nffree = Swapping.Swap(sb.fs_cstotal.cs_nffree); + sb.fs_cstotal.cs_nifree = Swapping.Swap(sb.fs_cstotal.cs_nifree); + sb.fs_cstotal.cs_spare[0] = Swapping.Swap(sb.fs_cstotal.cs_spare[0]); + sb.fs_cstotal.cs_spare[1] = Swapping.Swap(sb.fs_cstotal.cs_spare[1]); + sb.fs_cstotal.cs_spare[2] = Swapping.Swap(sb.fs_cstotal.cs_spare[2]); + } + + AaruConsole.DebugWriteLine("FFS plugin", "sb offset: 0x{0:X8}", sb_offset); + AaruConsole.DebugWriteLine("FFS plugin", "fs_rlink: 0x{0:X8}", sb.fs_rlink); + AaruConsole.DebugWriteLine("FFS plugin", "fs_sblkno: 0x{0:X8}", sb.fs_sblkno); + AaruConsole.DebugWriteLine("FFS plugin", "fs_cblkno: 0x{0:X8}", sb.fs_cblkno); + AaruConsole.DebugWriteLine("FFS plugin", "fs_iblkno: 0x{0:X8}", sb.fs_iblkno); + AaruConsole.DebugWriteLine("FFS plugin", "fs_dblkno: 0x{0:X8}", sb.fs_dblkno); + AaruConsole.DebugWriteLine("FFS plugin", "fs_size: 0x{0:X8}", sb.fs_size); + AaruConsole.DebugWriteLine("FFS plugin", "fs_dsize: 0x{0:X8}", sb.fs_dsize); + AaruConsole.DebugWriteLine("FFS plugin", "fs_ncg: 0x{0:X8}", sb.fs_ncg); + AaruConsole.DebugWriteLine("FFS plugin", "fs_bsize: 0x{0:X8}", sb.fs_bsize); + AaruConsole.DebugWriteLine("FFS plugin", "fs_fsize: 0x{0:X8}", sb.fs_fsize); + AaruConsole.DebugWriteLine("FFS plugin", "fs_frag: 0x{0:X8}", sb.fs_frag); + AaruConsole.DebugWriteLine("FFS plugin", "fs_minfree: 0x{0:X8}", sb.fs_minfree); + AaruConsole.DebugWriteLine("FFS plugin", "fs_bmask: 0x{0:X8}", sb.fs_bmask); + AaruConsole.DebugWriteLine("FFS plugin", "fs_fmask: 0x{0:X8}", sb.fs_fmask); + AaruConsole.DebugWriteLine("FFS plugin", "fs_bshift: 0x{0:X8}", sb.fs_bshift); + AaruConsole.DebugWriteLine("FFS plugin", "fs_fshift: 0x{0:X8}", sb.fs_fshift); + AaruConsole.DebugWriteLine("FFS plugin", "fs_maxcontig: 0x{0:X8}", sb.fs_maxcontig); + AaruConsole.DebugWriteLine("FFS plugin", "fs_maxbpg: 0x{0:X8}", sb.fs_maxbpg); + AaruConsole.DebugWriteLine("FFS plugin", "fs_fragshift: 0x{0:X8}", sb.fs_fragshift); + AaruConsole.DebugWriteLine("FFS plugin", "fs_fsbtodb: 0x{0:X8}", sb.fs_fsbtodb); + AaruConsole.DebugWriteLine("FFS plugin", "fs_sbsize: 0x{0:X8}", sb.fs_sbsize); + AaruConsole.DebugWriteLine("FFS plugin", "fs_csmask: 0x{0:X8}", sb.fs_csmask); + AaruConsole.DebugWriteLine("FFS plugin", "fs_csshift: 0x{0:X8}", sb.fs_csshift); + AaruConsole.DebugWriteLine("FFS plugin", "fs_nindir: 0x{0:X8}", sb.fs_nindir); + AaruConsole.DebugWriteLine("FFS plugin", "fs_inopb: 0x{0:X8}", sb.fs_inopb); + AaruConsole.DebugWriteLine("FFS plugin", "fs_optim: 0x{0:X8}", sb.fs_optim); + AaruConsole.DebugWriteLine("FFS plugin", "fs_id_1: 0x{0:X8}", sb.fs_id_1); + AaruConsole.DebugWriteLine("FFS plugin", "fs_id_2: 0x{0:X8}", sb.fs_id_2); + AaruConsole.DebugWriteLine("FFS plugin", "fs_csaddr: 0x{0:X8}", sb.fs_csaddr); + AaruConsole.DebugWriteLine("FFS plugin", "fs_cssize: 0x{0:X8}", sb.fs_cssize); + AaruConsole.DebugWriteLine("FFS plugin", "fs_cgsize: 0x{0:X8}", sb.fs_cgsize); + AaruConsole.DebugWriteLine("FFS plugin", "fs_ipg: 0x{0:X8}", sb.fs_ipg); + AaruConsole.DebugWriteLine("FFS plugin", "fs_fpg: 0x{0:X8}", sb.fs_fpg); + AaruConsole.DebugWriteLine("FFS plugin", "fs_fmod: 0x{0:X2}", sb.fs_fmod); + AaruConsole.DebugWriteLine("FFS plugin", "fs_clean: 0x{0:X2}", sb.fs_clean); + AaruConsole.DebugWriteLine("FFS plugin", "fs_ronly: 0x{0:X2}", sb.fs_ronly); + AaruConsole.DebugWriteLine("FFS plugin", "fs_flags: 0x{0:X2}", sb.fs_flags); + AaruConsole.DebugWriteLine("FFS plugin", "fs_magic: 0x{0:X8}", sb.fs_magic); + + if(sb.fs_magic == UFS2_MAGIC) + fs_type_ufs2 = true; + else + { + const uint + SunOSEpoch = + 0x1A54C580; // We are supposing there cannot be a Sun's fs created before 1/1/1982 00:00:00 + + fs_type_43bsd = + true; // There is no way of knowing this is the version, but there is of knowing it is not. + + if(sb.fs_link > 0) + { + fs_type_42bsd = true; // It was used in 4.2BSD + fs_type_43bsd = false; + } + + if((sb.fs_maxfilesize & 0xFFFFFFFF) > SunOSEpoch && + DateHandlers.UnixUnsignedToDateTime(sb.fs_maxfilesize & 0xFFFFFFFF) < DateTime.Now) + { + fs_type_42bsd = false; + fs_type_sun = true; + fs_type_43bsd = false; + } + + // This is for sure, as it is shared with a sectors/track with non-x86 SunOS, Epoch is absurdly high for that + if(sb.fs_old_npsect > SunOSEpoch && + DateHandlers.UnixToDateTime(sb.fs_old_npsect) < DateTime.Now) + { + fs_type_42bsd = false; + fs_type_sun86 = true; + fs_type_sun = false; + fs_type_43bsd = false; + } + + if(sb.fs_cgrotor > 0x00000000 && + (uint)sb.fs_cgrotor < 0xFFFFFFFF) + { + fs_type_42bsd = false; + fs_type_sun = false; + fs_type_sun86 = false; + fs_type_ufs = true; + fs_type_43bsd = false; + } + + // 4.3BSD code does not use these fields, they are always set up to 0 + fs_type_43bsd &= sb.fs_id_2 == 0 && sb.fs_id_1 == 0; + + // This is the only 4.4BSD inode format + fs_type_44bsd |= sb.fs_old_inodefmt == 2; + } + + if(!fs_type_ufs2) + { + sbInformation.AppendLine("There are a lot of variants of UFS using overlapped values on same fields"); + + sbInformation. + AppendLine("I will try to guess which one it is, but unless it's UFS2, I may be surely wrong"); + } + + if(fs_type_42bsd) + sbInformation.AppendLine("Guessed as 42BSD FFS"); + + if(fs_type_43bsd) + sbInformation.AppendLine("Guessed as 43BSD FFS"); + + if(fs_type_44bsd) + sbInformation.AppendLine("Guessed as 44BSD FFS"); + + if(fs_type_sun) + sbInformation.AppendLine("Guessed as SunOS FFS"); + + if(fs_type_sun86) + sbInformation.AppendLine("Guessed as SunOS/x86 FFS"); + + if(fs_type_ufs) + sbInformation.AppendLine("Guessed as UFS"); + + if(fs_type_42bsd) + sbInformation.AppendFormat("Linked list of filesystems: 0x{0:X8}", sb.fs_link).AppendLine(); + + sbInformation.AppendFormat("Superblock LBA: {0}", sb.fs_sblkno).AppendLine(); + sbInformation.AppendFormat("Cylinder-block LBA: {0}", sb.fs_cblkno).AppendLine(); + sbInformation.AppendFormat("inode-block LBA: {0}", sb.fs_iblkno).AppendLine(); + sbInformation.AppendFormat("First data block LBA: {0}", sb.fs_dblkno).AppendLine(); + sbInformation.AppendFormat("Cylinder group offset in cylinder: {0}", sb.fs_old_cgoffset).AppendLine(); + + sbInformation.AppendFormat("Volume last written on {0}", DateHandlers.UnixToDateTime(sb.fs_old_time)). + AppendLine(); + + XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(sb.fs_old_time); + XmlFsType.ModificationDateSpecified = true; + + sbInformation.AppendFormat("{0} blocks in volume ({1} bytes)", sb.fs_old_size, + (long)sb.fs_old_size * sb.fs_fsize).AppendLine(); + + XmlFsType.Clusters = (ulong)sb.fs_old_size; + XmlFsType.ClusterSize = (uint)sb.fs_fsize; + + sbInformation.AppendFormat("{0} data blocks in volume ({1} bytes)", sb.fs_old_dsize, + (long)sb.fs_old_dsize * sb.fs_fsize).AppendLine(); + + sbInformation.AppendFormat("{0} cylinder groups in volume", sb.fs_ncg).AppendLine(); + sbInformation.AppendFormat("{0} bytes in a basic block", sb.fs_bsize).AppendLine(); + sbInformation.AppendFormat("{0} bytes in a frag block", sb.fs_fsize).AppendLine(); + sbInformation.AppendFormat("{0} frags in a block", sb.fs_frag).AppendLine(); + sbInformation.AppendFormat("{0}% of blocks must be free", sb.fs_minfree).AppendLine(); + sbInformation.AppendFormat("{0}ms for optimal next block", sb.fs_old_rotdelay).AppendLine(); + + sbInformation.AppendFormat("disk rotates {0} times per second ({1}rpm)", sb.fs_old_rps, sb.fs_old_rps * 60). + AppendLine(); + + /* sbInformation.AppendFormat("fs_bmask: 0x{0:X8}", sb.fs_bmask).AppendLine(); + sbInformation.AppendFormat("fs_fmask: 0x{0:X8}", sb.fs_fmask).AppendLine(); + sbInformation.AppendFormat("fs_bshift: 0x{0:X8}", sb.fs_bshift).AppendLine(); + sbInformation.AppendFormat("fs_fshift: 0x{0:X8}", sb.fs_fshift).AppendLine();*/ + sbInformation.AppendFormat("{0} contiguous blocks at maximum", sb.fs_maxcontig).AppendLine(); + sbInformation.AppendFormat("{0} blocks per cylinder group at maximum", sb.fs_maxbpg).AppendLine(); + sbInformation.AppendFormat("Superblock is {0} bytes", sb.fs_sbsize).AppendLine(); + sbInformation.AppendFormat("NINDIR: 0x{0:X8}", sb.fs_nindir).AppendLine(); + sbInformation.AppendFormat("INOPB: 0x{0:X8}", sb.fs_inopb).AppendLine(); + sbInformation.AppendFormat("NSPF: 0x{0:X8}", sb.fs_old_nspf).AppendLine(); + + switch(sb.fs_optim) + { + case 0: + sbInformation.AppendLine("Filesystem will minimize allocation time"); + + break; + case 1: + sbInformation.AppendLine("Filesystem will minimize volume fragmentation"); + + break; + default: + sbInformation.AppendFormat("Unknown optimization value: 0x{0:X8}", sb.fs_optim).AppendLine(); + + break; + } + + if(fs_type_sun) + sbInformation.AppendFormat("{0} sectors/track", sb.fs_old_npsect).AppendLine(); + else if(fs_type_sun86) + sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(sb.fs_old_npsect)). + AppendLine(); + + sbInformation.AppendFormat("Hardware sector interleave: {0}", sb.fs_old_interleave).AppendLine(); + sbInformation.AppendFormat("Sector 0 skew: {0}/track", sb.fs_old_trackskew).AppendLine(); + + if(!fs_type_43bsd && + sb.fs_id_1 > 0 && + sb.fs_id_2 > 0) + sbInformation.AppendFormat("Volume ID: 0x{0:X8}{1:X8}", sb.fs_id_1, sb.fs_id_2).AppendLine(); + else if(fs_type_43bsd && + sb.fs_id_1 > 0 && + sb.fs_id_2 > 0) + { + sbInformation.AppendFormat("{0} µsec for head switch", sb.fs_id_1).AppendLine(); + sbInformation.AppendFormat("{0} µsec for track-to-track seek", sb.fs_id_2).AppendLine(); + } + + sbInformation.AppendFormat("Cylinder group summary LBA: {0}", sb.fs_old_csaddr).AppendLine(); + sbInformation.AppendFormat("{0} bytes in cylinder group summary", sb.fs_cssize).AppendLine(); + sbInformation.AppendFormat("{0} bytes in cylinder group", sb.fs_cgsize).AppendLine(); + sbInformation.AppendFormat("{0} tracks/cylinder", sb.fs_old_ntrak).AppendLine(); + sbInformation.AppendFormat("{0} sectors/track", sb.fs_old_nsect).AppendLine(); + sbInformation.AppendFormat("{0} sectors/cylinder", sb.fs_old_spc).AppendLine(); + sbInformation.AppendFormat("{0} cylinder in volume", sb.fs_old_ncyl).AppendLine(); + sbInformation.AppendFormat("{0} cylinders/group", sb.fs_old_cpg).AppendLine(); + sbInformation.AppendFormat("{0} inodes per cylinder group", sb.fs_ipg).AppendLine(); + sbInformation.AppendFormat("{0} blocks per group", sb.fs_fpg / sb.fs_frag).AppendLine(); + sbInformation.AppendFormat("{0} directories", sb.fs_old_cstotal.cs_ndir).AppendLine(); + + sbInformation.AppendFormat("{0} free blocks ({1} bytes)", sb.fs_old_cstotal.cs_nbfree, + (long)sb.fs_old_cstotal.cs_nbfree * sb.fs_fsize).AppendLine(); + + XmlFsType.FreeClusters = (ulong)sb.fs_old_cstotal.cs_nbfree; + XmlFsType.FreeClustersSpecified = true; + sbInformation.AppendFormat("{0} free inodes", sb.fs_old_cstotal.cs_nifree).AppendLine(); + sbInformation.AppendFormat("{0} free frags", sb.fs_old_cstotal.cs_nffree).AppendLine(); + + if(sb.fs_fmod == 1) + { + sbInformation.AppendLine("Superblock is under modification"); + XmlFsType.Dirty = true; + } + + if(sb.fs_clean == 1) + sbInformation.AppendLine("Volume is clean"); + + if(sb.fs_ronly == 1) + sbInformation.AppendLine("Volume is read-only"); + + sbInformation.AppendFormat("Volume flags: 0x{0:X2}", sb.fs_flags).AppendLine(); + + if(fs_type_ufs) + sbInformation.AppendFormat("Volume last mounted on \"{0}\"", StringHandlers.CToString(sb.fs_fsmnt)). + AppendLine(); + else if(fs_type_ufs2) + { + sbInformation.AppendFormat("Volume last mounted on \"{0}\"", StringHandlers.CToString(sb.fs_fsmnt)). + AppendLine(); + + sbInformation.AppendFormat("Volume name: \"{0}\"", StringHandlers.CToString(sb.fs_volname)). + AppendLine(); + + XmlFsType.VolumeName = StringHandlers.CToString(sb.fs_volname); + sbInformation.AppendFormat("Volume ID: 0x{0:X16}", sb.fs_swuid).AppendLine(); + + //xmlFSType.VolumeSerial = string.Format("{0:X16}", sb.fs_swuid); + sbInformation.AppendFormat("Last searched cylinder group: {0}", sb.fs_cgrotor).AppendLine(); + sbInformation.AppendFormat("{0} contiguously allocated directories", sb.fs_contigdirs).AppendLine(); + sbInformation.AppendFormat("Standard superblock LBA: {0}", sb.fs_sblkno).AppendLine(); + sbInformation.AppendFormat("{0} directories", sb.fs_cstotal.cs_ndir).AppendLine(); + + sbInformation.AppendFormat("{0} free blocks ({1} bytes)", sb.fs_cstotal.cs_nbfree, + sb.fs_cstotal.cs_nbfree * sb.fs_fsize).AppendLine(); + + XmlFsType.FreeClusters = (ulong)sb.fs_cstotal.cs_nbfree; + XmlFsType.FreeClustersSpecified = true; + sbInformation.AppendFormat("{0} free inodes", sb.fs_cstotal.cs_nifree).AppendLine(); + sbInformation.AppendFormat("{0} free frags", sb.fs_cstotal.cs_nffree).AppendLine(); + sbInformation.AppendFormat("{0} free clusters", sb.fs_cstotal.cs_numclusters).AppendLine(); + + sbInformation.AppendFormat("Volume last written on {0}", DateHandlers.UnixToDateTime(sb.fs_time)). + AppendLine(); + + XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(sb.fs_time); + XmlFsType.ModificationDateSpecified = true; + + sbInformation.AppendFormat("{0} blocks ({1} bytes)", sb.fs_size, sb.fs_size * sb.fs_fsize).AppendLine(); + + XmlFsType.Clusters = (ulong)sb.fs_size; + + sbInformation.AppendFormat("{0} data blocks ({1} bytes)", sb.fs_dsize, sb.fs_dsize * sb.fs_fsize). + AppendLine(); + + sbInformation.AppendFormat("Cylinder group summary area LBA: {0}", sb.fs_csaddr).AppendLine(); + sbInformation.AppendFormat("{0} blocks pending of being freed", sb.fs_pendingblocks).AppendLine(); + sbInformation.AppendFormat("{0} inodes pending of being freed", sb.fs_pendinginodes).AppendLine(); + } + + if(fs_type_sun) + sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(sb.fs_old_npsect)). + AppendLine(); + else if(fs_type_sun86) + sbInformation.AppendFormat("{0} sectors/track", sb.fs_state).AppendLine(); + else if(fs_type_44bsd) + { + sbInformation.AppendFormat("{0} blocks on cluster summary array", sb.fs_contigsumsize).AppendLine(); + + sbInformation.AppendFormat("Maximum length of a symbolic link: {0}", sb.fs_maxsymlinklen).AppendLine(); + + sbInformation.AppendFormat("A file can be {0} bytes at max", sb.fs_maxfilesize).AppendLine(); + + sbInformation.AppendFormat("Volume state on {0}", DateHandlers.UnixToDateTime(sb.fs_state)). + AppendLine(); + } + + if(sb.fs_old_nrpos > 0) + sbInformation.AppendFormat("{0} rotational positions", sb.fs_old_nrpos).AppendLine(); + + if(sb.fs_old_rotbloff > 0) + sbInformation.AppendFormat("{0} blocks per rotation", sb.fs_old_rotbloff).AppendLine(); + + information = sbInformation.ToString(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct csum + { + /// number of directories + public int cs_ndir; + /// number of free blocks + public int cs_nbfree; + /// number of free inodes + public int cs_nifree; + /// number of free frags + public int cs_nffree; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct csum_total + { + /// number of directories + public long cs_ndir; + /// number of free blocks + public long cs_nbfree; + /// number of free inodes + public long cs_nifree; + /// number of free frags + public long cs_nffree; + /// number of free clusters + public long cs_numclusters; + /// future expansion + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly long[] cs_spare; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct SuperBlock + { + /// linked list of file systems + public readonly uint fs_link; + /// used for incore super blocks on Sun: uint fs_rolled; // logging only: fs fully rolled + public readonly uint fs_rlink; + /// addr of super-block in filesys + public readonly int fs_sblkno; + /// offset of cyl-block in filesys + public readonly int fs_cblkno; + /// offset of inode-blocks in filesys + public readonly int fs_iblkno; + /// offset of first data after cg + public readonly int fs_dblkno; + /// cylinder group offset in cylinder + public readonly int fs_old_cgoffset; + /// used to calc mod fs_ntrak + public readonly int fs_old_cgmask; + /// last time written + public readonly int fs_old_time; + /// number of blocks in fs + public readonly int fs_old_size; + /// number of data blocks in fs + public readonly int fs_old_dsize; + /// number of cylinder groups + public readonly int fs_ncg; + /// size of basic blocks in fs + public readonly int fs_bsize; + /// size of frag blocks in fs + public readonly int fs_fsize; + /// number of frags in a block in fs + public readonly int fs_frag; + /* these are configuration parameters */ + /// minimum percentage of free blocks + public readonly int fs_minfree; + /// num of ms for optimal next block + public readonly int fs_old_rotdelay; + /// disk revolutions per second + public readonly int fs_old_rps; + /* these fields can be computed from the others */ + /// ``blkoff'' calc of blk offsets + public readonly int fs_bmask; + /// ``fragoff'' calc of frag offsets + public readonly int fs_fmask; + /// ``lblkno'' calc of logical blkno + public readonly int fs_bshift; + /// ``numfrags'' calc number of frags + public readonly int fs_fshift; + /* these are configuration parameters */ + /// max number of contiguous blks + public readonly int fs_maxcontig; + /// max number of blks per cyl group + public readonly int fs_maxbpg; + /* these fields can be computed from the others */ + /// block to frag shift + public readonly int fs_fragshift; + /// fsbtodb and dbtofsb shift constant + public readonly int fs_fsbtodb; + /// actual size of super block + public readonly int fs_sbsize; + /// csum block offset + public readonly int fs_csmask; + /// csum block number + public readonly int fs_csshift; + /// value of NINDIR + public readonly int fs_nindir; + /// value of INOPB + public readonly uint fs_inopb; + /// value of NSPF + public readonly int fs_old_nspf; + /* yet another configuration parameter */ + /// optimization preference, see below On SVR: int fs_state; // file system state + public readonly int fs_optim; + /// # sectors/track including spares + public readonly int fs_old_npsect; + /// hardware sector interleave + public readonly int fs_old_interleave; + /// sector 0 skew, per track On A/UX: int fs_state; // file system state + public readonly int fs_old_trackskew; + /// unique filesystem id On old: int fs_headswitch; // head switch time, usec + public readonly int fs_id_1; + /// unique filesystem id On old: int fs_trkseek; // track-to-track seek, usec + public readonly int fs_id_2; + /* sizes determined by number of cylinder groups and their sizes */ + /// blk addr of cyl grp summary area + public readonly int fs_old_csaddr; + /// size of cyl grp summary area + public readonly int fs_cssize; + /// cylinder group size + public readonly int fs_cgsize; + /* these fields are derived from the hardware */ + /// tracks per cylinder + public readonly int fs_old_ntrak; + /// sectors per track + public readonly int fs_old_nsect; + /// sectors per cylinder + public readonly int fs_old_spc; + /* this comes from the disk driver partitioning */ + /// cylinders in filesystem + public readonly int fs_old_ncyl; + /* these fields can be computed from the others */ + /// cylinders per group + public readonly int fs_old_cpg; + /// inodes per group + public readonly int fs_ipg; + /// blocks per group * fs_frag + public readonly int fs_fpg; + /* this data must be re-computed after crashes */ + /// cylinder summary information + public csum fs_old_cstotal; + /* these fields are cleared at mount time */ + /// super block modified flag + public readonly sbyte fs_fmod; + /// filesystem is clean flag + public readonly sbyte fs_clean; + /// mounted read-only flag + public readonly sbyte fs_ronly; + /// old FS_ flags + public readonly sbyte fs_old_flags; + /// name mounted on + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 468)] + public readonly byte[] fs_fsmnt; + /// volume name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] fs_volname; + /// system-wide uid + public readonly ulong fs_swuid; + /// due to alignment of fs_swuid + public readonly int fs_pad; + /* these fields retain the current block allocation info */ + /// last cg searched + public readonly int fs_cgrotor; + /// padding; was list of fs_cs buffers + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] + public readonly uint[] fs_ocsp; + /// (u) # of contig. allocated dirs + public readonly uint fs_contigdirs; + /// (u) cg summary info buffer + public readonly uint fs_csp; + /// (u) max cluster in each cyl group + public readonly uint fs_maxcluster; + /// (u) used by snapshots to track fs + public readonly uint fs_active; + /// cyl per cycle in postbl + public readonly int fs_old_cpc; + /// maximum blocking factor permitted + public readonly int fs_maxbsize; + /// number of unreferenced inodes + public readonly long fs_unrefs; + /// size of underlying GEOM provider + public readonly long fs_providersize; + /// size of area reserved for metadata + public readonly long fs_metaspace; + /// old rotation block list head + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] + public readonly long[] fs_sparecon64; + /// byte offset of standard superblock + public readonly long fs_sblockloc; + /// (u) cylinder summary information + public csum_total fs_cstotal; + /// last time written + public readonly long fs_time; + /// number of blocks in fs + public readonly long fs_size; + /// number of data blocks in fs + public readonly long fs_dsize; + /// blk addr of cyl grp summary area + public readonly long fs_csaddr; + /// (u) blocks being freed + public readonly long fs_pendingblocks; + /// (u) inodes being freed + public readonly uint fs_pendinginodes; + /// list of snapshot inode numbers + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly uint[] fs_snapinum; + /// expected average file size + public readonly uint fs_avgfilesize; + /// expected # of files per directory + public readonly uint fs_avgfpdir; + /// save real cg size to use fs_bsize + public readonly int fs_save_cgsize; + /// Last mount or fsck time. + public readonly long fs_mtime; + /// SUJ free list + public readonly int fs_sujfree; + /// reserved for future constants + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 23)] + public readonly int[] fs_sparecon32; + /// see FS_ flags below + public readonly int fs_flags; + /// size of cluster summary array + public readonly int fs_contigsumsize; + /// max length of an internal symlink + public readonly int fs_maxsymlinklen; + /// format of on-disk inodes + public readonly int fs_old_inodefmt; + /// maximum representable file size + public readonly ulong fs_maxfilesize; + /// ~fs_bmask for use with 64-bit size + public readonly long fs_qbmask; + /// ~fs_fmask for use with 64-bit size + public readonly long fs_qfmask; + /// validate fs_clean field + public readonly int fs_state; + /// format of positional layout tables + public readonly int fs_old_postblformat; + /// number of rotational positions + public readonly int fs_old_nrpos; + /// (short) rotation block list head + public readonly int fs_old_postbloff; + /// (uchar_t) blocks for each rotation + public readonly int fs_old_rotbloff; + /// magic number + public readonly uint fs_magic; + /// list of blocks for each rotation + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)] + public readonly byte[] fs_rotbl; + } } \ No newline at end of file diff --git a/Aaru.Filesystems/Fossil.cs b/Aaru.Filesystems/Fossil.cs index 1b81b5c5d..92df09a32 100644 --- a/Aaru.Filesystems/Fossil.cs +++ b/Aaru.Filesystems/Fossil.cs @@ -41,160 +41,159 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection for the Plan-9 Fossil on-disk filesystem +public sealed class Fossil : IFilesystem { + const uint FOSSIL_HDR_MAGIC = 0x3776AE89; + const uint FOSSIL_SB_MAGIC = 0x2340A3B1; + + // Fossil header starts at 128KiB + const ulong HEADER_POS = 128 * 1024; + /// - /// Implements detection for the Plan-9 Fossil on-disk filesystem - public sealed class Fossil : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Fossil Filesystem Plugin"; + /// + public Guid Id => new("932BF104-43F6-494F-973C-45EF58A51DA9"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint FOSSIL_HDR_MAGIC = 0x3776AE89; - const uint FOSSIL_SB_MAGIC = 0x2340A3B1; + ulong hdrSector = HEADER_POS / imagePlugin.Info.SectorSize; - // Fossil header starts at 128KiB - const ulong HEADER_POS = 128 * 1024; + if(partition.Start + hdrSector > imagePlugin.Info.Sectors) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Fossil Filesystem Plugin"; - /// - public Guid Id => new("932BF104-43F6-494F-973C-45EF58A51DA9"); - /// - public string Author => "Natalia Portillo"; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + hdrSector, out byte[] sector); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(errno != ErrorNumber.NoError) + return false; + + Header hdr = Marshal.ByteArrayToStructureBigEndian
(sector); + + AaruConsole.DebugWriteLine("Fossil plugin", "magic at 0x{0:X8} (expected 0x{1:X8})", hdr.magic, + FOSSIL_HDR_MAGIC); + + return hdr.magic == FOSSIL_HDR_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + // Technically everything on Plan 9 from Bell Labs is in UTF-8 + Encoding = Encoding.UTF8; + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + ulong hdrSector = HEADER_POS / imagePlugin.Info.SectorSize; + + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + hdrSector, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + Header hdr = Marshal.ByteArrayToStructureBigEndian
(sector); + + AaruConsole.DebugWriteLine("Fossil plugin", "magic at 0x{0:X8} (expected 0x{1:X8})", hdr.magic, + FOSSIL_HDR_MAGIC); + + var sb = new StringBuilder(); + + sb.AppendLine("Fossil"); + sb.AppendFormat("Filesystem version {0}", hdr.version).AppendLine(); + sb.AppendFormat("{0} bytes per block", hdr.blockSize).AppendLine(); + sb.AppendFormat("Superblock resides in block {0}", hdr.super).AppendLine(); + sb.AppendFormat("Labels resides in block {0}", hdr.label).AppendLine(); + sb.AppendFormat("Data starts at block {0}", hdr.data).AppendLine(); + sb.AppendFormat("Volume has {0} blocks", hdr.end).AppendLine(); + + ulong sbLocation = (hdr.super * (hdr.blockSize / imagePlugin.Info.SectorSize)) + partition.Start; + + XmlFsType = new FileSystemType { - ulong hdrSector = HEADER_POS / imagePlugin.Info.SectorSize; + Type = "Fossil filesystem", + ClusterSize = hdr.blockSize, + Clusters = hdr.end + }; - if(partition.Start + hdrSector > imagePlugin.Info.Sectors) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + hdrSector, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - Header hdr = Marshal.ByteArrayToStructureBigEndian
(sector); - - AaruConsole.DebugWriteLine("Fossil plugin", "magic at 0x{0:X8} (expected 0x{1:X8})", hdr.magic, - FOSSIL_HDR_MAGIC); - - return hdr.magic == FOSSIL_HDR_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + if(sbLocation <= partition.End) { - // Technically everything on Plan 9 from Bell Labs is in UTF-8 - Encoding = Encoding.UTF8; - information = ""; + errno = imagePlugin.ReadSector(sbLocation, out sector); + SuperBlock fsb = Marshal.ByteArrayToStructureBigEndian(sector); - if(imagePlugin.Info.SectorSize < 512) - return; + AaruConsole.DebugWriteLine("Fossil plugin", "magic 0x{0:X8} (expected 0x{1:X8})", fsb.magic, + FOSSIL_SB_MAGIC); - ulong hdrSector = HEADER_POS / imagePlugin.Info.SectorSize; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + hdrSector, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - Header hdr = Marshal.ByteArrayToStructureBigEndian
(sector); - - AaruConsole.DebugWriteLine("Fossil plugin", "magic at 0x{0:X8} (expected 0x{1:X8})", hdr.magic, - FOSSIL_HDR_MAGIC); - - var sb = new StringBuilder(); - - sb.AppendLine("Fossil"); - sb.AppendFormat("Filesystem version {0}", hdr.version).AppendLine(); - sb.AppendFormat("{0} bytes per block", hdr.blockSize).AppendLine(); - sb.AppendFormat("Superblock resides in block {0}", hdr.super).AppendLine(); - sb.AppendFormat("Labels resides in block {0}", hdr.label).AppendLine(); - sb.AppendFormat("Data starts at block {0}", hdr.data).AppendLine(); - sb.AppendFormat("Volume has {0} blocks", hdr.end).AppendLine(); - - ulong sbLocation = (hdr.super * (hdr.blockSize / imagePlugin.Info.SectorSize)) + partition.Start; - - XmlFsType = new FileSystemType + if(fsb.magic == FOSSIL_SB_MAGIC) { - Type = "Fossil filesystem", - ClusterSize = hdr.blockSize, - Clusters = hdr.end - }; - - if(sbLocation <= partition.End) - { - errno = imagePlugin.ReadSector(sbLocation, out sector); - SuperBlock fsb = Marshal.ByteArrayToStructureBigEndian(sector); - - AaruConsole.DebugWriteLine("Fossil plugin", "magic 0x{0:X8} (expected 0x{1:X8})", fsb.magic, - FOSSIL_SB_MAGIC); - - if(fsb.magic == FOSSIL_SB_MAGIC) - { - sb.AppendFormat("Epoch low {0}", fsb.epochLow).AppendLine(); - sb.AppendFormat("Epoch high {0}", fsb.epochHigh).AppendLine(); - sb.AppendFormat("Next QID {0}", fsb.qid).AppendLine(); - sb.AppendFormat("Active root block {0}", fsb.active).AppendLine(); - sb.AppendFormat("Next root block {0}", fsb.next).AppendLine(); - sb.AppendFormat("Current root block {0}", fsb.current).AppendLine(); - sb.AppendFormat("Volume label: \"{0}\"", StringHandlers.CToString(fsb.name, Encoding)).AppendLine(); - XmlFsType.VolumeName = StringHandlers.CToString(fsb.name, Encoding); - } + sb.AppendFormat("Epoch low {0}", fsb.epochLow).AppendLine(); + sb.AppendFormat("Epoch high {0}", fsb.epochHigh).AppendLine(); + sb.AppendFormat("Next QID {0}", fsb.qid).AppendLine(); + sb.AppendFormat("Active root block {0}", fsb.active).AppendLine(); + sb.AppendFormat("Next root block {0}", fsb.next).AppendLine(); + sb.AppendFormat("Current root block {0}", fsb.current).AppendLine(); + sb.AppendFormat("Volume label: \"{0}\"", StringHandlers.CToString(fsb.name, Encoding)).AppendLine(); + XmlFsType.VolumeName = StringHandlers.CToString(fsb.name, Encoding); } - - information = sb.ToString(); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Header - { - /// Magic number - public readonly uint magic; - /// Header version - public readonly ushort version; - /// Block size - public readonly ushort blockSize; - /// Block containing superblock - public readonly uint super; - /// Block containing labels - public readonly uint label; - /// Where do data blocks start - public readonly uint data; - /// How many data blocks does it have - public readonly uint end; - } + information = sb.ToString(); + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - /// Magic number - public readonly uint magic; - /// Header version - public readonly ushort version; - /// file system low epoch - public readonly uint epochLow; - /// file system high(active) epoch - public readonly uint epochHigh; - /// next qid to allocate - public readonly ulong qid; - /// data block number: root of active file system - public readonly int active; - /// data block number: root of next file system to archive - public readonly int next; - /// data block number: root of file system currently being archived - public readonly int current; - /// Venti score of last successful archive - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly byte[] last; - /// name of file system(just a comment) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] name; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Header + { + /// Magic number + public readonly uint magic; + /// Header version + public readonly ushort version; + /// Block size + public readonly ushort blockSize; + /// Block containing superblock + public readonly uint super; + /// Block containing labels + public readonly uint label; + /// Where do data blocks start + public readonly uint data; + /// How many data blocks does it have + public readonly uint end; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + /// Magic number + public readonly uint magic; + /// Header version + public readonly ushort version; + /// file system low epoch + public readonly uint epochLow; + /// file system high(active) epoch + public readonly uint epochHigh; + /// next qid to allocate + public readonly ulong qid; + /// data block number: root of active file system + public readonly int active; + /// data block number: root of next file system to archive + public readonly int next; + /// data block number: root of file system currently being archived + public readonly int current; + /// Venti score of last successful archive + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly byte[] last; + /// name of file system(just a comment) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] name; } } \ No newline at end of file diff --git a/Aaru.Filesystems/HAMMER.cs b/Aaru.Filesystems/HAMMER.cs index ff54a0255..9e36ec145 100644 --- a/Aaru.Filesystems/HAMMER.cs +++ b/Aaru.Filesystems/HAMMER.cs @@ -46,213 +46,212 @@ using Marshal = Aaru.Helpers.Marshal; #pragma warning disable 169 -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection for the HAMMER filesystem +public sealed class HAMMER : IFilesystem { + const ulong HAMMER_FSBUF_VOLUME = 0xC8414D4DC5523031; + const ulong HAMMER_FSBUF_VOLUME_REV = 0x313052C54D4D41C8; + const uint HAMMER_VOLHDR_SIZE = 1928; + const int HAMMER_BIGBLOCK_SIZE = 8192 * 1024; + /// - /// Implements detection for the HAMMER filesystem - public sealed class HAMMER : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "HAMMER Filesystem"; + /// + public Guid Id => new("91A188BF-5FD7-4677-BBD3-F59EBA9C864D"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const ulong HAMMER_FSBUF_VOLUME = 0xC8414D4DC5523031; - const ulong HAMMER_FSBUF_VOLUME_REV = 0x313052C54D4D41C8; - const uint HAMMER_VOLHDR_SIZE = 1928; - const int HAMMER_BIGBLOCK_SIZE = 8192 * 1024; + uint run = HAMMER_VOLHDR_SIZE / imagePlugin.Info.SectorSize; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "HAMMER Filesystem"; - /// - public Guid Id => new("91A188BF-5FD7-4677-BBD3-F59EBA9C864D"); - /// - public string Author => "Natalia Portillo"; + if(HAMMER_VOLHDR_SIZE % imagePlugin.Info.SectorSize > 0) + run++; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(run + partition.Start >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, run, out byte[] sbSector); + + if(errno != ErrorNumber.NoError) + return false; + + ulong magic = BitConverter.ToUInt64(sbSector, 0); + + return magic == HAMMER_FSBUF_VOLUME || magic == HAMMER_FSBUF_VOLUME_REV; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + + SuperBlock superBlock; + + uint run = HAMMER_VOLHDR_SIZE / imagePlugin.Info.SectorSize; + + if(HAMMER_VOLHDR_SIZE % imagePlugin.Info.SectorSize > 0) + run++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, run, out byte[] sbSector); + + if(errno != ErrorNumber.NoError) + return; + + ulong magic = BitConverter.ToUInt64(sbSector, 0); + + superBlock = magic == HAMMER_FSBUF_VOLUME ? Marshal.ByteArrayToStructureLittleEndian(sbSector) + : Marshal.ByteArrayToStructureBigEndian(sbSector); + + sb.AppendLine("HAMMER filesystem"); + + sb.AppendFormat("Volume version: {0}", superBlock.vol_version).AppendLine(); + + sb.AppendFormat("Volume {0} of {1} on this filesystem", superBlock.vol_no + 1, superBlock.vol_count). + AppendLine(); + + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(superBlock.vol_label, Encoding)).AppendLine(); + sb.AppendFormat("Volume serial: {0}", superBlock.vol_fsid).AppendLine(); + sb.AppendFormat("Filesystem type: {0}", superBlock.vol_fstype).AppendLine(); + sb.AppendFormat("Boot area starts at {0}", superBlock.vol_bot_beg).AppendLine(); + sb.AppendFormat("Memory log starts at {0}", superBlock.vol_mem_beg).AppendLine(); + sb.AppendFormat("First volume buffer starts at {0}", superBlock.vol_buf_beg).AppendLine(); + sb.AppendFormat("Volume ends at {0}", superBlock.vol_buf_end).AppendLine(); + + XmlFsType = new FileSystemType { - uint run = HAMMER_VOLHDR_SIZE / imagePlugin.Info.SectorSize; + Clusters = partition.Size / HAMMER_BIGBLOCK_SIZE, + ClusterSize = HAMMER_BIGBLOCK_SIZE, + Dirty = false, + Type = "HAMMER", + VolumeName = StringHandlers.CToString(superBlock.vol_label, Encoding), + VolumeSerial = superBlock.vol_fsid.ToString() + }; - if(HAMMER_VOLHDR_SIZE % imagePlugin.Info.SectorSize > 0) - run++; + if(superBlock.vol_no == superBlock.vol_rootvol) + { + sb.AppendFormat("Filesystem contains {0} \"big-blocks\" ({1} bytes)", superBlock.vol0_stat_bigblocks, + superBlock.vol0_stat_bigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); - if(run + partition.Start >= partition.End) - return false; + sb.AppendFormat("Filesystem has {0} \"big-blocks\" free ({1} bytes)", + superBlock.vol0_stat_freebigblocks, + superBlock.vol0_stat_freebigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, run, out byte[] sbSector); + sb.AppendFormat("Filesystem has {0} inode used", superBlock.vol0_stat_inodes).AppendLine(); - if(errno != ErrorNumber.NoError) - return false; - - ulong magic = BitConverter.ToUInt64(sbSector, 0); - - return magic == HAMMER_FSBUF_VOLUME || magic == HAMMER_FSBUF_VOLUME_REV; + XmlFsType.Clusters = (ulong)superBlock.vol0_stat_bigblocks; + XmlFsType.FreeClusters = (ulong)superBlock.vol0_stat_freebigblocks; + XmlFsType.FreeClustersSpecified = true; + XmlFsType.Files = (ulong)superBlock.vol0_stat_inodes; + XmlFsType.FilesSpecified = true; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + // 0 ? + //sb.AppendFormat("Volume header CRC: 0x{0:X8}", afs_sb.vol_crc).AppendLine(); - var sb = new StringBuilder(); + information = sb.ToString(); + } - SuperBlock superBlock; + /// Hammer superblock + [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")] + readonly struct SuperBlock + { + /// for a valid header + public readonly ulong vol_signature; - uint run = HAMMER_VOLHDR_SIZE / imagePlugin.Info.SectorSize; + /* These are relative to block device offset, not zone offsets. */ + /// offset of boot area + public readonly long vol_bot_beg; + /// offset of memory log + public readonly long vol_mem_beg; + /// offset of the first buffer in volume + public readonly long vol_buf_beg; + /// offset of volume EOF (on buffer boundary) + public readonly long vol_buf_end; + public readonly long vol_reserved01; - if(HAMMER_VOLHDR_SIZE % imagePlugin.Info.SectorSize > 0) - run++; + /// identify filesystem + public readonly Guid vol_fsid; + /// identify filesystem type + public readonly Guid vol_fstype; + /// filesystem label + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] vol_label; - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, run, out byte[] sbSector); + /// volume number within filesystem + public readonly int vol_no; + /// number of volumes making up filesystem + public readonly int vol_count; - if(errno != ErrorNumber.NoError) - return; + /// version control information + public readonly uint vol_version; + /// header crc + public readonly hammer_crc_t vol_crc; + /// volume flags + public readonly uint vol_flags; + /// the root volume number (must be 0) + public readonly uint vol_rootvol; - ulong magic = BitConverter.ToUInt64(sbSector, 0); + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly uint[] vol_reserved; - superBlock = magic == HAMMER_FSBUF_VOLUME ? Marshal.ByteArrayToStructureLittleEndian(sbSector) - : Marshal.ByteArrayToStructureBigEndian(sbSector); + /* + * These fields are initialized and space is reserved in every + * volume making up a HAMMER filesystem, but only the root volume + * contains valid data. Note that vol0_stat_bigblocks does not + * include big-blocks for freemap and undomap initially allocated + * by newfs_hammer(8). + */ + /// total big-blocks when fs is empty + public readonly long vol0_stat_bigblocks; + /// number of free big-blocks + public readonly long vol0_stat_freebigblocks; + public readonly long vol0_reserved01; + /// for statfs only + public readonly long vol0_stat_inodes; + public readonly long vol0_reserved02; + /// B-Tree root offset in zone-8 + public readonly hammer_off_t vol0_btree_root; + /// highest partially synchronized TID + public readonly hammer_tid_t vol0_next_tid; + public readonly hammer_off_t vol0_reserved03; - sb.AppendLine("HAMMER filesystem"); + /// + /// Blockmaps for zones. Not all zones use a blockmap. Note that the entire root blockmap is cached in the + /// hammer_mount structure. + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly HammerBlockMap[] vol0_blockmap; - sb.AppendFormat("Volume version: {0}", superBlock.vol_version).AppendLine(); + /// Array of zone-2 addresses for undo FIFO. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly hammer_off_t[] vol0_undo_array; + } - sb.AppendFormat("Volume {0} of {1} on this filesystem", superBlock.vol_no + 1, superBlock.vol_count). - AppendLine(); - - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(superBlock.vol_label, Encoding)).AppendLine(); - sb.AppendFormat("Volume serial: {0}", superBlock.vol_fsid).AppendLine(); - sb.AppendFormat("Filesystem type: {0}", superBlock.vol_fstype).AppendLine(); - sb.AppendFormat("Boot area starts at {0}", superBlock.vol_bot_beg).AppendLine(); - sb.AppendFormat("Memory log starts at {0}", superBlock.vol_mem_beg).AppendLine(); - sb.AppendFormat("First volume buffer starts at {0}", superBlock.vol_buf_beg).AppendLine(); - sb.AppendFormat("Volume ends at {0}", superBlock.vol_buf_end).AppendLine(); - - XmlFsType = new FileSystemType - { - Clusters = partition.Size / HAMMER_BIGBLOCK_SIZE, - ClusterSize = HAMMER_BIGBLOCK_SIZE, - Dirty = false, - Type = "HAMMER", - VolumeName = StringHandlers.CToString(superBlock.vol_label, Encoding), - VolumeSerial = superBlock.vol_fsid.ToString() - }; - - if(superBlock.vol_no == superBlock.vol_rootvol) - { - sb.AppendFormat("Filesystem contains {0} \"big-blocks\" ({1} bytes)", superBlock.vol0_stat_bigblocks, - superBlock.vol0_stat_bigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); - - sb.AppendFormat("Filesystem has {0} \"big-blocks\" free ({1} bytes)", - superBlock.vol0_stat_freebigblocks, - superBlock.vol0_stat_freebigblocks * HAMMER_BIGBLOCK_SIZE).AppendLine(); - - sb.AppendFormat("Filesystem has {0} inode used", superBlock.vol0_stat_inodes).AppendLine(); - - XmlFsType.Clusters = (ulong)superBlock.vol0_stat_bigblocks; - XmlFsType.FreeClusters = (ulong)superBlock.vol0_stat_freebigblocks; - XmlFsType.FreeClustersSpecified = true; - XmlFsType.Files = (ulong)superBlock.vol0_stat_inodes; - XmlFsType.FilesSpecified = true; - } - - // 0 ? - //sb.AppendFormat("Volume header CRC: 0x{0:X8}", afs_sb.vol_crc).AppendLine(); - - information = sb.ToString(); - } - - /// Hammer superblock - [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")] - readonly struct SuperBlock - { - /// for a valid header - public readonly ulong vol_signature; - - /* These are relative to block device offset, not zone offsets. */ - /// offset of boot area - public readonly long vol_bot_beg; - /// offset of memory log - public readonly long vol_mem_beg; - /// offset of the first buffer in volume - public readonly long vol_buf_beg; - /// offset of volume EOF (on buffer boundary) - public readonly long vol_buf_end; - public readonly long vol_reserved01; - - /// identify filesystem - public readonly Guid vol_fsid; - /// identify filesystem type - public readonly Guid vol_fstype; - /// filesystem label - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] vol_label; - - /// volume number within filesystem - public readonly int vol_no; - /// number of volumes making up filesystem - public readonly int vol_count; - - /// version control information - public readonly uint vol_version; - /// header crc - public readonly hammer_crc_t vol_crc; - /// volume flags - public readonly uint vol_flags; - /// the root volume number (must be 0) - public readonly uint vol_rootvol; - - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly uint[] vol_reserved; - - /* - * These fields are initialized and space is reserved in every - * volume making up a HAMMER filesystem, but only the root volume - * contains valid data. Note that vol0_stat_bigblocks does not - * include big-blocks for freemap and undomap initially allocated - * by newfs_hammer(8). - */ - /// total big-blocks when fs is empty - public readonly long vol0_stat_bigblocks; - /// number of free big-blocks - public readonly long vol0_stat_freebigblocks; - public readonly long vol0_reserved01; - /// for statfs only - public readonly long vol0_stat_inodes; - public readonly long vol0_reserved02; - /// B-Tree root offset in zone-8 - public readonly hammer_off_t vol0_btree_root; - /// highest partially synchronized TID - public readonly hammer_tid_t vol0_next_tid; - public readonly hammer_off_t vol0_reserved03; - - /// - /// Blockmaps for zones. Not all zones use a blockmap. Note that the entire root blockmap is cached in the - /// hammer_mount structure. - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly HammerBlockMap[] vol0_blockmap; - - /// Array of zone-2 addresses for undo FIFO. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly hammer_off_t[] vol0_undo_array; - } - - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")] - struct HammerBlockMap - { - /// zone-2 offset only used by zone-4 - public hammer_off_t phys_offset; - /// zone-X offset only used by zone-3 - public hammer_off_t first_offset; - /// zone-X offset for allocation - public hammer_off_t next_offset; - /// zone-X offset only used by zone-3 - public hammer_off_t alloc_offset; - public uint reserved01; - public hammer_crc_t entry_crc; - } + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")] + struct HammerBlockMap + { + /// zone-2 offset only used by zone-4 + public hammer_off_t phys_offset; + /// zone-X offset only used by zone-3 + public hammer_off_t first_offset; + /// zone-X offset for allocation + public hammer_off_t next_offset; + /// zone-X offset only used by zone-3 + public hammer_off_t alloc_offset; + public uint reserved01; + public hammer_crc_t entry_crc; } } \ No newline at end of file diff --git a/Aaru.Filesystems/HPFS.cs b/Aaru.Filesystems/HPFS.cs index df2732c5d..483b5ae3f 100644 --- a/Aaru.Filesystems/HPFS.cs +++ b/Aaru.Filesystems/HPFS.cs @@ -41,365 +41,364 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from an old unnamed document +/// +/// Implements detection of IBM's High Performance File System (HPFS) +public sealed class HPFS : IFilesystem { - // Information from an old unnamed document /// - /// Implements detection of IBM's High Performance File System (HPFS) - public sealed class HPFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "OS/2 High Performance File System"; + /// + public Guid Id => new("33513B2C-f590-4acb-8bf2-0b1d5e19dec5"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "OS/2 High Performance File System"; - /// - public Guid Id => new("33513B2C-f590-4acb-8bf2-0b1d5e19dec5"); - /// - public string Author => "Natalia Portillo"; + if(16 + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(16 + partition.Start >= partition.End) - return false; + ErrorNumber errno = + imagePlugin.ReadSector(16 + partition.Start, + out byte[] hpfsSbSector); // Seek to superblock, on logical sector 16 - ErrorNumber errno = - imagePlugin.ReadSector(16 + partition.Start, + if(errno != ErrorNumber.NoError) + return false; + + uint magic1 = BitConverter.ToUInt32(hpfsSbSector, 0x000); + uint magic2 = BitConverter.ToUInt32(hpfsSbSector, 0x004); + + return magic1 == 0xF995E849 && magic2 == 0xFA53E9C5; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("ibm850"); + information = ""; + + var sb = new StringBuilder(); + + ErrorNumber errno = + imagePlugin.ReadSector(0 + partition.Start, + out byte[] hpfsBpbSector); // Seek to BIOS parameter block, on logical sector 0 + + if(errno != ErrorNumber.NoError) + return; + + errno = imagePlugin.ReadSector(16 + partition.Start, out byte[] hpfsSbSector); // Seek to superblock, on logical sector 16 - if(errno != ErrorNumber.NoError) - return false; + if(errno != ErrorNumber.NoError) + return; - uint magic1 = BitConverter.ToUInt32(hpfsSbSector, 0x000); - uint magic2 = BitConverter.ToUInt32(hpfsSbSector, 0x004); + errno = imagePlugin.ReadSector(17 + partition.Start, + out byte[] hpfsSpSector); // Seek to spareblock, on logical sector 17 - return magic1 == 0xF995E849 && magic2 == 0xFA53E9C5; - } + if(errno != ErrorNumber.NoError) + return; - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + BiosParameterBlock bpb = Marshal.ByteArrayToStructureLittleEndian(hpfsBpbSector); + + SuperBlock hpfsSb = Marshal.ByteArrayToStructureLittleEndian(hpfsSbSector); + + SpareBlock sp = Marshal.ByteArrayToStructureLittleEndian(hpfsSpSector); + + if(StringHandlers.CToString(bpb.fs_type) != "HPFS " || + hpfsSb.magic1 != 0xF995E849 || + hpfsSb.magic2 != 0xFA53E9C5 || + sp.magic1 != 0xF9911849 || + sp.magic2 != 0xFA5229C5) { - Encoding = encoding ?? Encoding.GetEncoding("ibm850"); - information = ""; - - var sb = new StringBuilder(); - - ErrorNumber errno = - imagePlugin.ReadSector(0 + partition.Start, - out byte[] hpfsBpbSector); // Seek to BIOS parameter block, on logical sector 0 - - if(errno != ErrorNumber.NoError) - return; - - errno = imagePlugin.ReadSector(16 + partition.Start, - out byte[] hpfsSbSector); // Seek to superblock, on logical sector 16 - - if(errno != ErrorNumber.NoError) - return; - - errno = imagePlugin.ReadSector(17 + partition.Start, - out byte[] hpfsSpSector); // Seek to spareblock, on logical sector 17 - - if(errno != ErrorNumber.NoError) - return; - - BiosParameterBlock bpb = Marshal.ByteArrayToStructureLittleEndian(hpfsBpbSector); - - SuperBlock hpfsSb = Marshal.ByteArrayToStructureLittleEndian(hpfsSbSector); - - SpareBlock sp = Marshal.ByteArrayToStructureLittleEndian(hpfsSpSector); - - if(StringHandlers.CToString(bpb.fs_type) != "HPFS " || - hpfsSb.magic1 != 0xF995E849 || - hpfsSb.magic2 != 0xFA53E9C5 || - sp.magic1 != 0xF9911849 || - sp.magic2 != 0xFA5229C5) - { - sb.AppendLine("This may not be HPFS, following information may be not correct."); - sb.AppendFormat("File system type: \"{0}\" (Should be \"HPFS \")", bpb.fs_type).AppendLine(); - sb.AppendFormat("Superblock magic1: 0x{0:X8} (Should be 0xF995E849)", hpfsSb.magic1).AppendLine(); - sb.AppendFormat("Superblock magic2: 0x{0:X8} (Should be 0xFA53E9C5)", hpfsSb.magic2).AppendLine(); - sb.AppendFormat("Spareblock magic1: 0x{0:X8} (Should be 0xF9911849)", sp.magic1).AppendLine(); - sb.AppendFormat("Spareblock magic2: 0x{0:X8} (Should be 0xFA5229C5)", sp.magic2).AppendLine(); - } - - sb.AppendFormat("OEM name: {0}", StringHandlers.CToString(bpb.oem_name)).AppendLine(); - sb.AppendFormat("{0} bytes per sector", bpb.bps).AppendLine(); - - // sb.AppendFormat("{0} sectors per cluster", hpfs_bpb.spc).AppendLine(); - // sb.AppendFormat("{0} reserved sectors", hpfs_bpb.rsectors).AppendLine(); - // sb.AppendFormat("{0} FATs", hpfs_bpb.fats_no).AppendLine(); - // sb.AppendFormat("{0} entries on root directory", hpfs_bpb.root_ent).AppendLine(); - // sb.AppendFormat("{0} mini sectors on volume", hpfs_bpb.sectors).AppendLine(); - sb.AppendFormat("Media descriptor: 0x{0:X2}", bpb.media).AppendLine(); - - // sb.AppendFormat("{0} sectors per FAT", hpfs_bpb.spfat).AppendLine(); - // sb.AppendFormat("{0} sectors per track", hpfs_bpb.sptrk).AppendLine(); - // sb.AppendFormat("{0} heads", hpfs_bpb.heads).AppendLine(); - sb.AppendFormat("{0} sectors hidden before BPB", bpb.hsectors).AppendLine(); - - sb.AppendFormat("{0} sectors on volume ({1} bytes)", hpfsSb.sectors, hpfsSb.sectors * bpb.bps).AppendLine(); - - // sb.AppendFormat("{0} sectors on volume ({1} bytes)", hpfs_bpb.big_sectors, hpfs_bpb.big_sectors * hpfs_bpb.bps).AppendLine(); - sb.AppendFormat("BIOS Drive Number: 0x{0:X2}", bpb.drive_no).AppendLine(); - sb.AppendFormat("NT Flags: 0x{0:X2}", bpb.nt_flags).AppendLine(); - sb.AppendFormat("Signature: 0x{0:X2}", bpb.signature).AppendLine(); - sb.AppendFormat("Serial number: 0x{0:X8}", bpb.serial_no).AppendLine(); - sb.AppendFormat("Volume label: {0}", StringHandlers.CToString(bpb.volume_label, Encoding)).AppendLine(); - - // sb.AppendFormat("Filesystem type: \"{0}\"", hpfs_bpb.fs_type).AppendLine(); - - DateTime lastChk = DateHandlers.UnixToDateTime(hpfsSb.last_chkdsk); - DateTime lastOptim = DateHandlers.UnixToDateTime(hpfsSb.last_optim); - - sb.AppendFormat("HPFS version: {0}", hpfsSb.version).AppendLine(); - sb.AppendFormat("Functional version: {0}", hpfsSb.func_version).AppendLine(); - sb.AppendFormat("Sector of root directory FNode: {0}", hpfsSb.root_fnode).AppendLine(); - sb.AppendFormat("{0} sectors are marked bad", hpfsSb.badblocks).AppendLine(); - sb.AppendFormat("Sector of free space bitmaps: {0}", hpfsSb.bitmap_lsn).AppendLine(); - sb.AppendFormat("Sector of bad blocks list: {0}", hpfsSb.badblock_lsn).AppendLine(); - - if(hpfsSb.last_chkdsk > 0) - sb.AppendFormat("Date of last integrity check: {0}", lastChk).AppendLine(); - else - sb.AppendLine("Filesystem integrity has never been checked"); - - if(hpfsSb.last_optim > 0) - sb.AppendFormat("Date of last optimization {0}", lastOptim).AppendLine(); - else - sb.AppendLine("Filesystem has never been optimized"); - - sb.AppendFormat("Directory band has {0} sectors", hpfsSb.dband_sectors).AppendLine(); - sb.AppendFormat("Directory band starts at sector {0}", hpfsSb.dband_start).AppendLine(); - sb.AppendFormat("Directory band ends at sector {0}", hpfsSb.dband_last).AppendLine(); - sb.AppendFormat("Sector of directory band bitmap: {0}", hpfsSb.dband_bitmap).AppendLine(); - sb.AppendFormat("Sector of ACL directory: {0}", hpfsSb.acl_start).AppendLine(); - - sb.AppendFormat("Sector of Hotfix directory: {0}", sp.hotfix_start).AppendLine(); - sb.AppendFormat("{0} used Hotfix entries", sp.hotfix_used).AppendLine(); - sb.AppendFormat("{0} total Hotfix entries", sp.hotfix_entries).AppendLine(); - sb.AppendFormat("{0} free spare DNodes", sp.spare_dnodes_free).AppendLine(); - sb.AppendFormat("{0} total spare DNodes", sp.spare_dnodes).AppendLine(); - sb.AppendFormat("Sector of codepage directory: {0}", sp.codepage_lsn).AppendLine(); - sb.AppendFormat("{0} codepages used in the volume", sp.codepages).AppendLine(); - sb.AppendFormat("SuperBlock CRC32: {0:X8}", sp.sb_crc32).AppendLine(); - sb.AppendFormat("SpareBlock CRC32: {0:X8}", sp.sp_crc32).AppendLine(); - - sb.AppendLine("Flags:"); - sb.AppendLine((sp.flags1 & 0x01) == 0x01 ? "Filesystem is dirty." : "Filesystem is clean."); - - if((sp.flags1 & 0x02) == 0x02) - sb.AppendLine("Spare directory blocks are in use"); - - if((sp.flags1 & 0x04) == 0x04) - sb.AppendLine("Hotfixes are in use"); - - if((sp.flags1 & 0x08) == 0x08) - sb.AppendLine("Disk contains bad sectors"); - - if((sp.flags1 & 0x10) == 0x10) - sb.AppendLine("Disk has a bad bitmap"); - - if((sp.flags1 & 0x20) == 0x20) - sb.AppendLine("Filesystem was formatted fast"); - - if((sp.flags1 & 0x40) == 0x40) - sb.AppendLine("Unknown flag 0x40 on flags1 is active"); - - if((sp.flags1 & 0x80) == 0x80) - sb.AppendLine("Filesystem has been mounted by an old IFS"); - - if((sp.flags2 & 0x01) == 0x01) - sb.AppendLine("Install DASD limits"); - - if((sp.flags2 & 0x02) == 0x02) - sb.AppendLine("Resync DASD limits"); - - if((sp.flags2 & 0x04) == 0x04) - sb.AppendLine("DASD limits are operational"); - - if((sp.flags2 & 0x08) == 0x08) - sb.AppendLine("Multimedia is active"); - - if((sp.flags2 & 0x10) == 0x10) - sb.AppendLine("DCE ACLs are active"); - - if((sp.flags2 & 0x20) == 0x20) - sb.AppendLine("DASD limits are dirty"); - - if((sp.flags2 & 0x40) == 0x40) - sb.AppendLine("Unknown flag 0x40 on flags2 is active"); - - if((sp.flags2 & 0x80) == 0x80) - sb.AppendLine("Unknown flag 0x80 on flags2 is active"); - - XmlFsType = new FileSystemType(); - - // Theoretically everything from BPB to SB is boot code, should I hash everything or only the sector loaded by BIOS itself? - if(bpb.jump[0] == 0xEB && - bpb.jump[1] > 0x3C && - bpb.jump[1] < 0x80 && - bpb.signature2 == 0xAA55) - { - XmlFsType.Bootable = true; - string bootChk = Sha1Context.Data(bpb.boot_code, out byte[] _); - sb.AppendLine("Volume is bootable"); - sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine(); - } - - XmlFsType.Dirty |= (sp.flags1 & 0x01) == 0x01; - XmlFsType.Clusters = hpfsSb.sectors; - XmlFsType.ClusterSize = bpb.bps; - XmlFsType.Type = "HPFS"; - XmlFsType.VolumeName = StringHandlers.CToString(bpb.volume_label, Encoding); - XmlFsType.VolumeSerial = $"{bpb.serial_no:X8}"; - XmlFsType.SystemIdentifier = StringHandlers.CToString(bpb.oem_name); - - information = sb.ToString(); + sb.AppendLine("This may not be HPFS, following information may be not correct."); + sb.AppendFormat("File system type: \"{0}\" (Should be \"HPFS \")", bpb.fs_type).AppendLine(); + sb.AppendFormat("Superblock magic1: 0x{0:X8} (Should be 0xF995E849)", hpfsSb.magic1).AppendLine(); + sb.AppendFormat("Superblock magic2: 0x{0:X8} (Should be 0xFA53E9C5)", hpfsSb.magic2).AppendLine(); + sb.AppendFormat("Spareblock magic1: 0x{0:X8} (Should be 0xF9911849)", sp.magic1).AppendLine(); + sb.AppendFormat("Spareblock magic2: 0x{0:X8} (Should be 0xFA5229C5)", sp.magic2).AppendLine(); } - /// BIOS Parameter Block, at sector 0 - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BiosParameterBlock + sb.AppendFormat("OEM name: {0}", StringHandlers.CToString(bpb.oem_name)).AppendLine(); + sb.AppendFormat("{0} bytes per sector", bpb.bps).AppendLine(); + + // sb.AppendFormat("{0} sectors per cluster", hpfs_bpb.spc).AppendLine(); + // sb.AppendFormat("{0} reserved sectors", hpfs_bpb.rsectors).AppendLine(); + // sb.AppendFormat("{0} FATs", hpfs_bpb.fats_no).AppendLine(); + // sb.AppendFormat("{0} entries on root directory", hpfs_bpb.root_ent).AppendLine(); + // sb.AppendFormat("{0} mini sectors on volume", hpfs_bpb.sectors).AppendLine(); + sb.AppendFormat("Media descriptor: 0x{0:X2}", bpb.media).AppendLine(); + + // sb.AppendFormat("{0} sectors per FAT", hpfs_bpb.spfat).AppendLine(); + // sb.AppendFormat("{0} sectors per track", hpfs_bpb.sptrk).AppendLine(); + // sb.AppendFormat("{0} heads", hpfs_bpb.heads).AppendLine(); + sb.AppendFormat("{0} sectors hidden before BPB", bpb.hsectors).AppendLine(); + + sb.AppendFormat("{0} sectors on volume ({1} bytes)", hpfsSb.sectors, hpfsSb.sectors * bpb.bps).AppendLine(); + + // sb.AppendFormat("{0} sectors on volume ({1} bytes)", hpfs_bpb.big_sectors, hpfs_bpb.big_sectors * hpfs_bpb.bps).AppendLine(); + sb.AppendFormat("BIOS Drive Number: 0x{0:X2}", bpb.drive_no).AppendLine(); + sb.AppendFormat("NT Flags: 0x{0:X2}", bpb.nt_flags).AppendLine(); + sb.AppendFormat("Signature: 0x{0:X2}", bpb.signature).AppendLine(); + sb.AppendFormat("Serial number: 0x{0:X8}", bpb.serial_no).AppendLine(); + sb.AppendFormat("Volume label: {0}", StringHandlers.CToString(bpb.volume_label, Encoding)).AppendLine(); + + // sb.AppendFormat("Filesystem type: \"{0}\"", hpfs_bpb.fs_type).AppendLine(); + + DateTime lastChk = DateHandlers.UnixToDateTime(hpfsSb.last_chkdsk); + DateTime lastOptim = DateHandlers.UnixToDateTime(hpfsSb.last_optim); + + sb.AppendFormat("HPFS version: {0}", hpfsSb.version).AppendLine(); + sb.AppendFormat("Functional version: {0}", hpfsSb.func_version).AppendLine(); + sb.AppendFormat("Sector of root directory FNode: {0}", hpfsSb.root_fnode).AppendLine(); + sb.AppendFormat("{0} sectors are marked bad", hpfsSb.badblocks).AppendLine(); + sb.AppendFormat("Sector of free space bitmaps: {0}", hpfsSb.bitmap_lsn).AppendLine(); + sb.AppendFormat("Sector of bad blocks list: {0}", hpfsSb.badblock_lsn).AppendLine(); + + if(hpfsSb.last_chkdsk > 0) + sb.AppendFormat("Date of last integrity check: {0}", lastChk).AppendLine(); + else + sb.AppendLine("Filesystem integrity has never been checked"); + + if(hpfsSb.last_optim > 0) + sb.AppendFormat("Date of last optimization {0}", lastOptim).AppendLine(); + else + sb.AppendLine("Filesystem has never been optimized"); + + sb.AppendFormat("Directory band has {0} sectors", hpfsSb.dband_sectors).AppendLine(); + sb.AppendFormat("Directory band starts at sector {0}", hpfsSb.dband_start).AppendLine(); + sb.AppendFormat("Directory band ends at sector {0}", hpfsSb.dband_last).AppendLine(); + sb.AppendFormat("Sector of directory band bitmap: {0}", hpfsSb.dband_bitmap).AppendLine(); + sb.AppendFormat("Sector of ACL directory: {0}", hpfsSb.acl_start).AppendLine(); + + sb.AppendFormat("Sector of Hotfix directory: {0}", sp.hotfix_start).AppendLine(); + sb.AppendFormat("{0} used Hotfix entries", sp.hotfix_used).AppendLine(); + sb.AppendFormat("{0} total Hotfix entries", sp.hotfix_entries).AppendLine(); + sb.AppendFormat("{0} free spare DNodes", sp.spare_dnodes_free).AppendLine(); + sb.AppendFormat("{0} total spare DNodes", sp.spare_dnodes).AppendLine(); + sb.AppendFormat("Sector of codepage directory: {0}", sp.codepage_lsn).AppendLine(); + sb.AppendFormat("{0} codepages used in the volume", sp.codepages).AppendLine(); + sb.AppendFormat("SuperBlock CRC32: {0:X8}", sp.sb_crc32).AppendLine(); + sb.AppendFormat("SpareBlock CRC32: {0:X8}", sp.sp_crc32).AppendLine(); + + sb.AppendLine("Flags:"); + sb.AppendLine((sp.flags1 & 0x01) == 0x01 ? "Filesystem is dirty." : "Filesystem is clean."); + + if((sp.flags1 & 0x02) == 0x02) + sb.AppendLine("Spare directory blocks are in use"); + + if((sp.flags1 & 0x04) == 0x04) + sb.AppendLine("Hotfixes are in use"); + + if((sp.flags1 & 0x08) == 0x08) + sb.AppendLine("Disk contains bad sectors"); + + if((sp.flags1 & 0x10) == 0x10) + sb.AppendLine("Disk has a bad bitmap"); + + if((sp.flags1 & 0x20) == 0x20) + sb.AppendLine("Filesystem was formatted fast"); + + if((sp.flags1 & 0x40) == 0x40) + sb.AppendLine("Unknown flag 0x40 on flags1 is active"); + + if((sp.flags1 & 0x80) == 0x80) + sb.AppendLine("Filesystem has been mounted by an old IFS"); + + if((sp.flags2 & 0x01) == 0x01) + sb.AppendLine("Install DASD limits"); + + if((sp.flags2 & 0x02) == 0x02) + sb.AppendLine("Resync DASD limits"); + + if((sp.flags2 & 0x04) == 0x04) + sb.AppendLine("DASD limits are operational"); + + if((sp.flags2 & 0x08) == 0x08) + sb.AppendLine("Multimedia is active"); + + if((sp.flags2 & 0x10) == 0x10) + sb.AppendLine("DCE ACLs are active"); + + if((sp.flags2 & 0x20) == 0x20) + sb.AppendLine("DASD limits are dirty"); + + if((sp.flags2 & 0x40) == 0x40) + sb.AppendLine("Unknown flag 0x40 on flags2 is active"); + + if((sp.flags2 & 0x80) == 0x80) + sb.AppendLine("Unknown flag 0x80 on flags2 is active"); + + XmlFsType = new FileSystemType(); + + // Theoretically everything from BPB to SB is boot code, should I hash everything or only the sector loaded by BIOS itself? + if(bpb.jump[0] == 0xEB && + bpb.jump[1] > 0x3C && + bpb.jump[1] < 0x80 && + bpb.signature2 == 0xAA55) { - /// 0x000, Jump to boot code - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// 0x003, OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// 0x00B, Bytes per sector - public readonly ushort bps; - /// 0x00D, Sectors per cluster - public readonly byte spc; - /// 0x00E, Reserved sectors between BPB and... does it have sense in HPFS? - public readonly ushort rsectors; - /// 0x010, Number of FATs... seriously? - public readonly byte fats_no; - /// 0x011, Number of entries on root directory... ok - public readonly ushort root_ent; - /// 0x013, Sectors in volume... doubt it - public readonly ushort sectors; - /// 0x015, Media descriptor - public readonly byte media; - /// 0x016, Sectors per FAT... again - public readonly ushort spfat; - /// 0x018, Sectors per track... you're kidding - public readonly ushort sptrk; - /// 0x01A, Heads... stop! - public readonly ushort heads; - /// 0x01C, Hidden sectors before BPB - public readonly uint hsectors; - /// 0x024, Sectors in volume if > 65535... - public readonly uint big_sectors; - /// 0x028, Drive number - public readonly byte drive_no; - /// 0x029, Volume flags? - public readonly byte nt_flags; - /// 0x02A, EPB signature, 0x29 - public readonly byte signature; - /// 0x02B, Volume serial number - public readonly uint serial_no; - /// 0x02F, Volume label, 11 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] volume_label; - /// 0x03A, Filesystem type, 8 bytes, space-padded ("HPFS ") - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] fs_type; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 448)] - public readonly byte[] boot_code; - /// 0x1FE, 0xAA55 - public readonly ushort signature2; + XmlFsType.Bootable = true; + string bootChk = Sha1Context.Data(bpb.boot_code, out byte[] _); + sb.AppendLine("Volume is bootable"); + sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine(); } - /// HPFS superblock at sector 16 - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - /// 0x000, 0xF995E849 - public readonly uint magic1; - /// 0x004, 0xFA53E9C5 - public readonly uint magic2; - /// 0x008, HPFS version - public readonly byte version; - /// 0x009, 2 if <= 4 GiB, 3 if > 4 GiB - public readonly byte func_version; - /// 0x00A, Alignment - public readonly ushort dummy; - /// 0x00C, LSN pointer to root fnode - public readonly uint root_fnode; - /// 0x010, Sectors on volume - public readonly uint sectors; - /// 0x014, Bad blocks on volume - public readonly uint badblocks; - /// 0x018, LSN pointer to volume bitmap - public readonly uint bitmap_lsn; - /// 0x01C, 0 - public readonly uint zero1; - /// 0x020, LSN pointer to badblock directory - public readonly uint badblock_lsn; - /// 0x024, 0 - public readonly uint zero2; - /// 0x028, Time of last CHKDSK - public readonly int last_chkdsk; - /// 0x02C, Time of last optimization - public readonly int last_optim; - /// 0x030, Sectors of dir band - public readonly uint dband_sectors; - /// 0x034, Start sector of dir band - public readonly uint dband_start; - /// 0x038, Last sector of dir band - public readonly uint dband_last; - /// 0x03C, LSN of free space bitmap - public readonly uint dband_bitmap; - /// 0x040, Can be used for volume name (32 bytes) - public readonly ulong zero3; - /// 0x048, ... - public readonly ulong zero4; - /// 0x04C, ... - public readonly ulong zero5; - /// 0x050, ...; - public readonly ulong zero6; - /// 0x058, LSN pointer to ACLs (only HPFS386) - public readonly uint acl_start; - } + XmlFsType.Dirty |= (sp.flags1 & 0x01) == 0x01; + XmlFsType.Clusters = hpfsSb.sectors; + XmlFsType.ClusterSize = bpb.bps; + XmlFsType.Type = "HPFS"; + XmlFsType.VolumeName = StringHandlers.CToString(bpb.volume_label, Encoding); + XmlFsType.VolumeSerial = $"{bpb.serial_no:X8}"; + XmlFsType.SystemIdentifier = StringHandlers.CToString(bpb.oem_name); - /// HPFS spareblock at sector 17 - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SpareBlock - { - /// 0x000, 0xF9911849 - public readonly uint magic1; - /// 0x004, 0xFA5229C5 - public readonly uint magic2; - /// 0x008, HPFS flags - public readonly byte flags1; - /// 0x009, HPFS386 flags - public readonly byte flags2; - /// 0x00A, Alignment - public readonly ushort dummy; - /// 0x00C, LSN of hotfix directory - public readonly uint hotfix_start; - /// 0x010, Used hotfixes - public readonly uint hotfix_used; - /// 0x014, Total hotfixes available - public readonly uint hotfix_entries; - /// 0x018, Unused spare dnodes - public readonly uint spare_dnodes_free; - /// 0x01C, Length of spare dnodes list - public readonly uint spare_dnodes; - /// 0x020, LSN of codepage directory - public readonly uint codepage_lsn; - /// 0x024, Number of codepages used - public readonly uint codepages; - /// 0x028, SuperBlock CRC32 (only HPFS386) - public readonly uint sb_crc32; - /// 0x02C, SpareBlock CRC32 (only HPFS386) - public readonly uint sp_crc32; - } + information = sb.ToString(); + } + + /// BIOS Parameter Block, at sector 0 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BiosParameterBlock + { + /// 0x000, Jump to boot code + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// 0x003, OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// 0x00B, Bytes per sector + public readonly ushort bps; + /// 0x00D, Sectors per cluster + public readonly byte spc; + /// 0x00E, Reserved sectors between BPB and... does it have sense in HPFS? + public readonly ushort rsectors; + /// 0x010, Number of FATs... seriously? + public readonly byte fats_no; + /// 0x011, Number of entries on root directory... ok + public readonly ushort root_ent; + /// 0x013, Sectors in volume... doubt it + public readonly ushort sectors; + /// 0x015, Media descriptor + public readonly byte media; + /// 0x016, Sectors per FAT... again + public readonly ushort spfat; + /// 0x018, Sectors per track... you're kidding + public readonly ushort sptrk; + /// 0x01A, Heads... stop! + public readonly ushort heads; + /// 0x01C, Hidden sectors before BPB + public readonly uint hsectors; + /// 0x024, Sectors in volume if > 65535... + public readonly uint big_sectors; + /// 0x028, Drive number + public readonly byte drive_no; + /// 0x029, Volume flags? + public readonly byte nt_flags; + /// 0x02A, EPB signature, 0x29 + public readonly byte signature; + /// 0x02B, Volume serial number + public readonly uint serial_no; + /// 0x02F, Volume label, 11 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] volume_label; + /// 0x03A, Filesystem type, 8 bytes, space-padded ("HPFS ") + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] fs_type; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 448)] + public readonly byte[] boot_code; + /// 0x1FE, 0xAA55 + public readonly ushort signature2; + } + + /// HPFS superblock at sector 16 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + /// 0x000, 0xF995E849 + public readonly uint magic1; + /// 0x004, 0xFA53E9C5 + public readonly uint magic2; + /// 0x008, HPFS version + public readonly byte version; + /// 0x009, 2 if <= 4 GiB, 3 if > 4 GiB + public readonly byte func_version; + /// 0x00A, Alignment + public readonly ushort dummy; + /// 0x00C, LSN pointer to root fnode + public readonly uint root_fnode; + /// 0x010, Sectors on volume + public readonly uint sectors; + /// 0x014, Bad blocks on volume + public readonly uint badblocks; + /// 0x018, LSN pointer to volume bitmap + public readonly uint bitmap_lsn; + /// 0x01C, 0 + public readonly uint zero1; + /// 0x020, LSN pointer to badblock directory + public readonly uint badblock_lsn; + /// 0x024, 0 + public readonly uint zero2; + /// 0x028, Time of last CHKDSK + public readonly int last_chkdsk; + /// 0x02C, Time of last optimization + public readonly int last_optim; + /// 0x030, Sectors of dir band + public readonly uint dband_sectors; + /// 0x034, Start sector of dir band + public readonly uint dband_start; + /// 0x038, Last sector of dir band + public readonly uint dband_last; + /// 0x03C, LSN of free space bitmap + public readonly uint dband_bitmap; + /// 0x040, Can be used for volume name (32 bytes) + public readonly ulong zero3; + /// 0x048, ... + public readonly ulong zero4; + /// 0x04C, ... + public readonly ulong zero5; + /// 0x050, ...; + public readonly ulong zero6; + /// 0x058, LSN pointer to ACLs (only HPFS386) + public readonly uint acl_start; + } + + /// HPFS spareblock at sector 17 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SpareBlock + { + /// 0x000, 0xF9911849 + public readonly uint magic1; + /// 0x004, 0xFA5229C5 + public readonly uint magic2; + /// 0x008, HPFS flags + public readonly byte flags1; + /// 0x009, HPFS386 flags + public readonly byte flags2; + /// 0x00A, Alignment + public readonly ushort dummy; + /// 0x00C, LSN of hotfix directory + public readonly uint hotfix_start; + /// 0x010, Used hotfixes + public readonly uint hotfix_used; + /// 0x014, Total hotfixes available + public readonly uint hotfix_entries; + /// 0x018, Unused spare dnodes + public readonly uint spare_dnodes_free; + /// 0x01C, Length of spare dnodes list + public readonly uint spare_dnodes; + /// 0x020, LSN of codepage directory + public readonly uint codepage_lsn; + /// 0x024, Number of codepages used + public readonly uint codepages; + /// 0x028, SuperBlock CRC32 (only HPFS386) + public readonly uint sb_crc32; + /// 0x02C, SpareBlock CRC32 (only HPFS386) + public readonly uint sp_crc32; } } \ No newline at end of file diff --git a/Aaru.Filesystems/HPOFS/Consts.cs b/Aaru.Filesystems/HPOFS/Consts.cs index 2f07b0db2..1825c849c 100644 --- a/Aaru.Filesystems/HPOFS/Consts.cs +++ b/Aaru.Filesystems/HPOFS/Consts.cs @@ -30,21 +30,20 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class HPOFS { - public sealed partial class HPOFS + readonly byte[] _type = { - readonly byte[] _type = - { - 0x48, 0x50, 0x4F, 0x46, 0x53, 0x00, 0x00, 0x00 - }; - readonly byte[] _medinfoSignature = - { - 0x4D, 0x45, 0x44, 0x49, 0x4E, 0x46, 0x4F, 0x20 - }; - readonly byte[] _volinfoSignature = - { - 0x56, 0x4F, 0x4C, 0x49, 0x4E, 0x46, 0x4F, 0x20 - }; - } + 0x48, 0x50, 0x4F, 0x46, 0x53, 0x00, 0x00, 0x00 + }; + readonly byte[] _medinfoSignature = + { + 0x4D, 0x45, 0x44, 0x49, 0x4E, 0x46, 0x4F, 0x20 + }; + readonly byte[] _volinfoSignature = + { + 0x56, 0x4F, 0x4C, 0x49, 0x4E, 0x46, 0x4F, 0x20 + }; } \ No newline at end of file diff --git a/Aaru.Filesystems/HPOFS/HPOFS.cs b/Aaru.Filesystems/HPOFS/HPOFS.cs index c5d792cbb..cc91ada30 100644 --- a/Aaru.Filesystems/HPOFS/HPOFS.cs +++ b/Aaru.Filesystems/HPOFS/HPOFS.cs @@ -35,23 +35,22 @@ using System.Text; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from test floppy images created with OS/2 HPOFS 2.0 +// Need to get IBM document GA32-0224 -> IBM 3995 Optical Library Dataserver Products: Optical Disk Format +/// +/// Implements identification of IBM's High Performance Optical File System +public sealed partial class HPOFS : IFilesystem { - // Information from test floppy images created with OS/2 HPOFS 2.0 - // Need to get IBM document GA32-0224 -> IBM 3995 Optical Library Dataserver Products: Optical Disk Format /// - /// Implements identification of IBM's High Performance Optical File System - public sealed partial class HPOFS : IFilesystem - { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "High Performance Optical File System"; - /// - public Guid Id => new Guid("1b72dcd5-d031-4757-8a9f-8d2fb18c59e2"); - /// - public string Author => "Natalia Portillo"; - } + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "High Performance Optical File System"; + /// + public Guid Id => new Guid("1b72dcd5-d031-4757-8a9f-8d2fb18c59e2"); + /// + public string Author => "Natalia Portillo"; } \ No newline at end of file diff --git a/Aaru.Filesystems/HPOFS/Info.cs b/Aaru.Filesystems/HPOFS/Info.cs index b73df7cf6..b2739737b 100644 --- a/Aaru.Filesystems/HPOFS/Info.cs +++ b/Aaru.Filesystems/HPOFS/Info.cs @@ -40,193 +40,192 @@ using Aaru.Console; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class HPOFS { - public sealed partial class HPOFS + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(16 + partition.Start >= partition.End) + return false; + + ErrorNumber errno = + imagePlugin.ReadSector(0 + partition.Start, + out byte[] hpofsBpbSector); // Seek to BIOS parameter block, on logical sector 0 + + if(errno != ErrorNumber.NoError) + return false; + + if(hpofsBpbSector.Length < 512) + return false; + + BiosParameterBlock bpb = Marshal.ByteArrayToStructureLittleEndian(hpofsBpbSector); + + return bpb.fs_type.SequenceEqual(_type); + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("ibm850"); + information = ""; + + var sb = new StringBuilder(); + + ErrorNumber errno = + imagePlugin.ReadSector(0 + partition.Start, + out byte[] hpofsBpbSector); // Seek to BIOS parameter block, on logical sector 0 + + if(errno != ErrorNumber.NoError) + return; + + errno = imagePlugin.ReadSector(13 + partition.Start, + out byte[] medInfoSector); // Seek to media information block, on logical sector 13 + + if(errno != ErrorNumber.NoError) + return; + + errno = imagePlugin.ReadSector(14 + partition.Start, + out byte[] volInfoSector); // Seek to volume information block, on logical sector 14 + + if(errno != ErrorNumber.NoError) + return; + + BiosParameterBlock bpb = Marshal.ByteArrayToStructureLittleEndian(hpofsBpbSector); + MediaInformationBlock mib = Marshal.ByteArrayToStructureBigEndian(medInfoSector); + VolumeInformationBlock vib = Marshal.ByteArrayToStructureBigEndian(volInfoSector); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.oem_name = \"{0}\"", + StringHandlers.CToString(bpb.oem_name)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.bps = {0}", bpb.bps); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.spc = {0}", bpb.spc); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.rsectors = {0}", bpb.rsectors); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.fats_no = {0}", bpb.fats_no); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.root_ent = {0}", bpb.root_ent); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.sectors = {0}", bpb.sectors); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.media = 0x{0:X2}", bpb.media); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.spfat = {0}", bpb.spfat); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.sptrk = {0}", bpb.sptrk); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.heads = {0}", bpb.heads); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.hsectors = {0}", bpb.hsectors); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.big_sectors = {0}", bpb.big_sectors); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.drive_no = 0x{0:X2}", bpb.drive_no); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.nt_flags = {0}", bpb.nt_flags); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.signature = 0x{0:X2}", bpb.signature); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.serial_no = 0x{0:X8}", bpb.serial_no); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.volume_label = \"{0}\"", + StringHandlers.SpacePaddedToString(bpb.volume_label)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.fs_type = \"{0}\"", StringHandlers.CToString(bpb.fs_type)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.boot_code is empty? = {0}", + ArrayHelpers.ArrayIsNullOrEmpty(bpb.boot_code)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.unknown = {0}", bpb.unknown); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.unknown2 = {0}", bpb.unknown2); + AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.signature2 = {0}", bpb.signature2); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.blockId = \"{0}\"", StringHandlers.CToString(mib.blockId)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.volumeLabel = \"{0}\"", + StringHandlers.SpacePaddedToString(mib.volumeLabel)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.comment = \"{0}\"", + StringHandlers.SpacePaddedToString(mib.comment)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.serial = 0x{0:X8}", mib.serial); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.creationTimestamp = {0}", + DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.codepageType = {0}", mib.codepageType); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.codepage = {0}", mib.codepage); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.rps = {0}", mib.rps); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.bps = {0}", mib.bps); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.bpc = {0}", mib.bpc); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown2 = {0}", mib.unknown2); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.sectors = {0}", mib.sectors); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown3 = {0}", mib.unknown3); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown4 = {0}", mib.unknown4); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.major = {0}", mib.major); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.minor = {0}", mib.minor); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown5 = {0}", mib.unknown5); + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown6 = {0}", mib.unknown6); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.filler is empty? = {0}", + ArrayHelpers.ArrayIsNullOrEmpty(mib.filler)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.blockId = \"{0}\"", StringHandlers.CToString(vib.blockId)); + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown = {0}", vib.unknown); + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown2 = {0}", vib.unknown2); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown3 is empty? = {0}", + ArrayHelpers.ArrayIsNullOrEmpty(vib.unknown3)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown4 = \"{0}\"", + StringHandlers.SpacePaddedToString(vib.unknown4)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.owner = \"{0}\"", + StringHandlers.SpacePaddedToString(vib.owner)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown5 = \"{0}\"", + StringHandlers.SpacePaddedToString(vib.unknown5)); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown6 = {0}", vib.unknown6); + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.percentFull = {0}", vib.percentFull); + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown7 = {0}", vib.unknown7); + + AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.filler is empty? = {0}", + ArrayHelpers.ArrayIsNullOrEmpty(vib.filler)); + + sb.AppendLine("High Performance Optical File System"); + sb.AppendFormat("OEM name: {0}", StringHandlers.SpacePaddedToString(bpb.oem_name)).AppendLine(); + sb.AppendFormat("{0} bytes per sector", bpb.bps).AppendLine(); + sb.AppendFormat("{0} sectors per cluster", bpb.spc).AppendLine(); + sb.AppendFormat("Media descriptor: 0x{0:X2}", bpb.media).AppendLine(); + sb.AppendFormat("{0} sectors per track", bpb.sptrk).AppendLine(); + sb.AppendFormat("{0} heads", bpb.heads).AppendLine(); + sb.AppendFormat("{0} sectors hidden before BPB", bpb.hsectors).AppendLine(); + sb.AppendFormat("{0} sectors on volume ({1} bytes)", mib.sectors, mib.sectors * bpb.bps).AppendLine(); + sb.AppendFormat("BIOS Drive Number: 0x{0:X2}", bpb.drive_no).AppendLine(); + sb.AppendFormat("Serial number: 0x{0:X8}", mib.serial).AppendLine(); + + sb.AppendFormat("Volume label: {0}", StringHandlers.SpacePaddedToString(mib.volumeLabel, Encoding)). + AppendLine(); + + sb.AppendFormat("Volume comment: {0}", StringHandlers.SpacePaddedToString(mib.comment, Encoding)). + AppendLine(); + + sb.AppendFormat("Volume owner: {0}", StringHandlers.SpacePaddedToString(vib.owner, Encoding)).AppendLine(); + + sb.AppendFormat("Volume created on {0}", DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime)). + AppendLine(); + + sb.AppendFormat("Volume uses {0} codepage {1}", mib.codepageType > 0 && mib.codepageType < 3 + ? mib.codepageType == 2 + ? "EBCDIC" + : "ASCII" : "Unknown", mib.codepage).AppendLine(); + + sb.AppendFormat("RPS level: {0}", mib.rps).AppendLine(); + sb.AppendFormat("Filesystem version: {0}.{1}", mib.major, mib.minor).AppendLine(); + sb.AppendFormat("Volume can be filled up to {0}%", vib.percentFull).AppendLine(); + + XmlFsType = new FileSystemType { - if(16 + partition.Start >= partition.End) - return false; + Clusters = mib.sectors / bpb.spc, + ClusterSize = (uint)(bpb.bps * bpb.spc), + CreationDate = DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime), + CreationDateSpecified = true, + DataPreparerIdentifier = StringHandlers.SpacePaddedToString(vib.owner, Encoding), + Type = "HPOFS", + VolumeName = StringHandlers.SpacePaddedToString(mib.volumeLabel, Encoding), + VolumeSerial = $"{mib.serial:X8}", + SystemIdentifier = StringHandlers.SpacePaddedToString(bpb.oem_name) + }; - ErrorNumber errno = - imagePlugin.ReadSector(0 + partition.Start, - out byte[] hpofsBpbSector); // Seek to BIOS parameter block, on logical sector 0 - - if(errno != ErrorNumber.NoError) - return false; - - if(hpofsBpbSector.Length < 512) - return false; - - BiosParameterBlock bpb = Marshal.ByteArrayToStructureLittleEndian(hpofsBpbSector); - - return bpb.fs_type.SequenceEqual(_type); - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("ibm850"); - information = ""; - - var sb = new StringBuilder(); - - ErrorNumber errno = - imagePlugin.ReadSector(0 + partition.Start, - out byte[] hpofsBpbSector); // Seek to BIOS parameter block, on logical sector 0 - - if(errno != ErrorNumber.NoError) - return; - - errno = imagePlugin.ReadSector(13 + partition.Start, - out byte[] medInfoSector); // Seek to media information block, on logical sector 13 - - if(errno != ErrorNumber.NoError) - return; - - errno = imagePlugin.ReadSector(14 + partition.Start, - out byte[] volInfoSector); // Seek to volume information block, on logical sector 14 - - if(errno != ErrorNumber.NoError) - return; - - BiosParameterBlock bpb = Marshal.ByteArrayToStructureLittleEndian(hpofsBpbSector); - MediaInformationBlock mib = Marshal.ByteArrayToStructureBigEndian(medInfoSector); - VolumeInformationBlock vib = Marshal.ByteArrayToStructureBigEndian(volInfoSector); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.oem_name = \"{0}\"", - StringHandlers.CToString(bpb.oem_name)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.bps = {0}", bpb.bps); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.spc = {0}", bpb.spc); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.rsectors = {0}", bpb.rsectors); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.fats_no = {0}", bpb.fats_no); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.root_ent = {0}", bpb.root_ent); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.sectors = {0}", bpb.sectors); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.media = 0x{0:X2}", bpb.media); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.spfat = {0}", bpb.spfat); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.sptrk = {0}", bpb.sptrk); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.heads = {0}", bpb.heads); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.hsectors = {0}", bpb.hsectors); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.big_sectors = {0}", bpb.big_sectors); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.drive_no = 0x{0:X2}", bpb.drive_no); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.nt_flags = {0}", bpb.nt_flags); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.signature = 0x{0:X2}", bpb.signature); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.serial_no = 0x{0:X8}", bpb.serial_no); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.volume_label = \"{0}\"", - StringHandlers.SpacePaddedToString(bpb.volume_label)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.fs_type = \"{0}\"", StringHandlers.CToString(bpb.fs_type)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.boot_code is empty? = {0}", - ArrayHelpers.ArrayIsNullOrEmpty(bpb.boot_code)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.unknown = {0}", bpb.unknown); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.unknown2 = {0}", bpb.unknown2); - AaruConsole.DebugWriteLine("HPOFS Plugin", "bpb.signature2 = {0}", bpb.signature2); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.blockId = \"{0}\"", StringHandlers.CToString(mib.blockId)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.volumeLabel = \"{0}\"", - StringHandlers.SpacePaddedToString(mib.volumeLabel)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.comment = \"{0}\"", - StringHandlers.SpacePaddedToString(mib.comment)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.serial = 0x{0:X8}", mib.serial); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.creationTimestamp = {0}", - DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.codepageType = {0}", mib.codepageType); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.codepage = {0}", mib.codepage); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.rps = {0}", mib.rps); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.bps = {0}", mib.bps); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.bpc = {0}", mib.bpc); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown2 = {0}", mib.unknown2); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.sectors = {0}", mib.sectors); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown3 = {0}", mib.unknown3); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown4 = {0}", mib.unknown4); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.major = {0}", mib.major); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.minor = {0}", mib.minor); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown5 = {0}", mib.unknown5); - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.unknown6 = {0}", mib.unknown6); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "mib.filler is empty? = {0}", - ArrayHelpers.ArrayIsNullOrEmpty(mib.filler)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.blockId = \"{0}\"", StringHandlers.CToString(vib.blockId)); - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown = {0}", vib.unknown); - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown2 = {0}", vib.unknown2); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown3 is empty? = {0}", - ArrayHelpers.ArrayIsNullOrEmpty(vib.unknown3)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown4 = \"{0}\"", - StringHandlers.SpacePaddedToString(vib.unknown4)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.owner = \"{0}\"", - StringHandlers.SpacePaddedToString(vib.owner)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown5 = \"{0}\"", - StringHandlers.SpacePaddedToString(vib.unknown5)); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown6 = {0}", vib.unknown6); - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.percentFull = {0}", vib.percentFull); - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.unknown7 = {0}", vib.unknown7); - - AaruConsole.DebugWriteLine("HPOFS Plugin", "vib.filler is empty? = {0}", - ArrayHelpers.ArrayIsNullOrEmpty(vib.filler)); - - sb.AppendLine("High Performance Optical File System"); - sb.AppendFormat("OEM name: {0}", StringHandlers.SpacePaddedToString(bpb.oem_name)).AppendLine(); - sb.AppendFormat("{0} bytes per sector", bpb.bps).AppendLine(); - sb.AppendFormat("{0} sectors per cluster", bpb.spc).AppendLine(); - sb.AppendFormat("Media descriptor: 0x{0:X2}", bpb.media).AppendLine(); - sb.AppendFormat("{0} sectors per track", bpb.sptrk).AppendLine(); - sb.AppendFormat("{0} heads", bpb.heads).AppendLine(); - sb.AppendFormat("{0} sectors hidden before BPB", bpb.hsectors).AppendLine(); - sb.AppendFormat("{0} sectors on volume ({1} bytes)", mib.sectors, mib.sectors * bpb.bps).AppendLine(); - sb.AppendFormat("BIOS Drive Number: 0x{0:X2}", bpb.drive_no).AppendLine(); - sb.AppendFormat("Serial number: 0x{0:X8}", mib.serial).AppendLine(); - - sb.AppendFormat("Volume label: {0}", StringHandlers.SpacePaddedToString(mib.volumeLabel, Encoding)). - AppendLine(); - - sb.AppendFormat("Volume comment: {0}", StringHandlers.SpacePaddedToString(mib.comment, Encoding)). - AppendLine(); - - sb.AppendFormat("Volume owner: {0}", StringHandlers.SpacePaddedToString(vib.owner, Encoding)).AppendLine(); - - sb.AppendFormat("Volume created on {0}", DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime)). - AppendLine(); - - sb.AppendFormat("Volume uses {0} codepage {1}", mib.codepageType > 0 && mib.codepageType < 3 - ? mib.codepageType == 2 - ? "EBCDIC" - : "ASCII" : "Unknown", mib.codepage).AppendLine(); - - sb.AppendFormat("RPS level: {0}", mib.rps).AppendLine(); - sb.AppendFormat("Filesystem version: {0}.{1}", mib.major, mib.minor).AppendLine(); - sb.AppendFormat("Volume can be filled up to {0}%", vib.percentFull).AppendLine(); - - XmlFsType = new FileSystemType - { - Clusters = mib.sectors / bpb.spc, - ClusterSize = (uint)(bpb.bps * bpb.spc), - CreationDate = DateHandlers.DosToDateTime(mib.creationDate, mib.creationTime), - CreationDateSpecified = true, - DataPreparerIdentifier = StringHandlers.SpacePaddedToString(vib.owner, Encoding), - Type = "HPOFS", - VolumeName = StringHandlers.SpacePaddedToString(mib.volumeLabel, Encoding), - VolumeSerial = $"{mib.serial:X8}", - SystemIdentifier = StringHandlers.SpacePaddedToString(bpb.oem_name) - }; - - information = sb.ToString(); - } + information = sb.ToString(); } } \ No newline at end of file diff --git a/Aaru.Filesystems/HPOFS/Structs.cs b/Aaru.Filesystems/HPOFS/Structs.cs index 8dbb388ca..57219eb21 100644 --- a/Aaru.Filesystems/HPOFS/Structs.cs +++ b/Aaru.Filesystems/HPOFS/Structs.cs @@ -33,327 +33,326 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +[SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed partial class HPOFS { - [SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed partial class HPOFS + /// BIOS Parameter Block, at sector 0, little-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BiosParameterBlock { - /// BIOS Parameter Block, at sector 0, little-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BiosParameterBlock - { - /// 0x000, Jump to boot code - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// 0x003, OEM Name, 8 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// 0x00B, Bytes per sector - public readonly ushort bps; - /// 0x00D, Sectors per cluster - public readonly byte spc; - /// 0x00E, Reserved sectors between BPB and... does it have sense in HPFS? - public readonly ushort rsectors; - /// 0x010, Number of FATs... seriously? - public readonly byte fats_no; - /// 0x011, Number of entries on root directory... ok - public readonly ushort root_ent; - /// 0x013, Sectors in volume... doubt it - public readonly ushort sectors; - /// 0x015, Media descriptor - public readonly byte media; - /// 0x016, Sectors per FAT... again - public readonly ushort spfat; - /// 0x018, Sectors per track... you're kidding - public readonly ushort sptrk; - /// 0x01A, Heads... stop! - public readonly ushort heads; - /// 0x01C, Hidden sectors before BPB - public readonly uint hsectors; - /// 0x024, Sectors in volume if > 65535... - public readonly uint big_sectors; - /// 0x028, Drive number - public readonly byte drive_no; - /// 0x029, Volume flags? - public readonly byte nt_flags; - /// 0x02A, EPB signature, 0x29 - public readonly byte signature; - /// 0x02B, Volume serial number - public readonly uint serial_no; - /// 0x02F, Volume label, 11 bytes, space-padded - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] volume_label; - /// 0x03A, Filesystem type, 8 bytes, space-padded ("HPOFS ") - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] fs_type; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 442)] - public readonly byte[] boot_code; - /// 0x1F8, Unknown - public readonly uint unknown; - /// 0x1FC, Unknown - public readonly ushort unknown2; - /// 0x1FE, 0xAA55 - public readonly ushort signature2; - } + /// 0x000, Jump to boot code + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// 0x003, OEM Name, 8 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// 0x00B, Bytes per sector + public readonly ushort bps; + /// 0x00D, Sectors per cluster + public readonly byte spc; + /// 0x00E, Reserved sectors between BPB and... does it have sense in HPFS? + public readonly ushort rsectors; + /// 0x010, Number of FATs... seriously? + public readonly byte fats_no; + /// 0x011, Number of entries on root directory... ok + public readonly ushort root_ent; + /// 0x013, Sectors in volume... doubt it + public readonly ushort sectors; + /// 0x015, Media descriptor + public readonly byte media; + /// 0x016, Sectors per FAT... again + public readonly ushort spfat; + /// 0x018, Sectors per track... you're kidding + public readonly ushort sptrk; + /// 0x01A, Heads... stop! + public readonly ushort heads; + /// 0x01C, Hidden sectors before BPB + public readonly uint hsectors; + /// 0x024, Sectors in volume if > 65535... + public readonly uint big_sectors; + /// 0x028, Drive number + public readonly byte drive_no; + /// 0x029, Volume flags? + public readonly byte nt_flags; + /// 0x02A, EPB signature, 0x29 + public readonly byte signature; + /// 0x02B, Volume serial number + public readonly uint serial_no; + /// 0x02F, Volume label, 11 bytes, space-padded + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] volume_label; + /// 0x03A, Filesystem type, 8 bytes, space-padded ("HPOFS ") + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] fs_type; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 442)] + public readonly byte[] boot_code; + /// 0x1F8, Unknown + public readonly uint unknown; + /// 0x1FC, Unknown + public readonly ushort unknown2; + /// 0x1FE, 0xAA55 + public readonly ushort signature2; + } - /// Media Information Block, at sector 13, big-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct MediaInformationBlock - { - /// Block identifier "MEDINFO " - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] blockId; - /// Volume label - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] volumeLabel; - /// Volume comment - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 160)] - public readonly byte[] comment; - /// Volume serial number - public readonly uint serial; - /// Volume creation date, DOS format - public readonly ushort creationDate; - /// Volume creation time, DOS format - public readonly ushort creationTime; - /// Codepage type: 1 ASCII, 2 EBCDIC - public readonly ushort codepageType; - /// Codepage - public readonly ushort codepage; - /// RPS level - public readonly uint rps; - /// Coincides with bytes per sector, and bytes per cluster, need more media - public readonly ushort bps; - /// Coincides with bytes per sector, and bytes per cluster, need more media - public readonly ushort bpc; - /// Unknown, empty - public readonly uint unknown2; - /// Sectors (or clusters) - public readonly uint sectors; - /// Unknown, coincides with bps but changing it makes nothing - public readonly uint unknown3; - /// Empty? - public readonly ulong unknown4; - /// Format major version - public readonly ushort major; - /// Format minor version - public readonly ushort minor; - /// Empty? - public readonly uint unknown5; - /// Unknown, non-empty - public readonly uint unknown6; - /// Empty - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] - public readonly byte[] filler; - } + /// Media Information Block, at sector 13, big-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct MediaInformationBlock + { + /// Block identifier "MEDINFO " + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] blockId; + /// Volume label + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] volumeLabel; + /// Volume comment + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 160)] + public readonly byte[] comment; + /// Volume serial number + public readonly uint serial; + /// Volume creation date, DOS format + public readonly ushort creationDate; + /// Volume creation time, DOS format + public readonly ushort creationTime; + /// Codepage type: 1 ASCII, 2 EBCDIC + public readonly ushort codepageType; + /// Codepage + public readonly ushort codepage; + /// RPS level + public readonly uint rps; + /// Coincides with bytes per sector, and bytes per cluster, need more media + public readonly ushort bps; + /// Coincides with bytes per sector, and bytes per cluster, need more media + public readonly ushort bpc; + /// Unknown, empty + public readonly uint unknown2; + /// Sectors (or clusters) + public readonly uint sectors; + /// Unknown, coincides with bps but changing it makes nothing + public readonly uint unknown3; + /// Empty? + public readonly ulong unknown4; + /// Format major version + public readonly ushort major; + /// Format minor version + public readonly ushort minor; + /// Empty? + public readonly uint unknown5; + /// Unknown, non-empty + public readonly uint unknown6; + /// Empty + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] + public readonly byte[] filler; + } - /// Volume Information Block, at sector 14, big-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct VolumeInformationBlock - { - /// Block identifier "VOLINFO " - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] blockId; - /// Unknown - public readonly uint unknown; - /// Unknown - public readonly uint unknown2; - /// Some kind of counter - public readonly uint dir_intent_cnt; - /// Some kind of counter, another - public readonly uint dir_update_cnt; - /// Unknown - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)] - public readonly byte[] unknown3; - /// Unknown, space-padded string - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] unknown4; - /// Owner, space-padded string - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] owner; - /// Unknown, space-padded string - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] unknown5; - /// Unknown, empty? - public readonly uint unknown6; - /// Maximum percent full - public readonly ushort percentFull; - /// Unknown, empty? - public readonly ushort unknown7; - /// Empty - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 384)] - public readonly byte[] filler; - } + /// Volume Information Block, at sector 14, big-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct VolumeInformationBlock + { + /// Block identifier "VOLINFO " + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] blockId; + /// Unknown + public readonly uint unknown; + /// Unknown + public readonly uint unknown2; + /// Some kind of counter + public readonly uint dir_intent_cnt; + /// Some kind of counter, another + public readonly uint dir_update_cnt; + /// Unknown + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)] + public readonly byte[] unknown3; + /// Unknown, space-padded string + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] unknown4; + /// Owner, space-padded string + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] owner; + /// Unknown, space-padded string + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] unknown5; + /// Unknown, empty? + public readonly uint unknown6; + /// Maximum percent full + public readonly ushort percentFull; + /// Unknown, empty? + public readonly ushort unknown7; + /// Empty + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 384)] + public readonly byte[] filler; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Extent - { - /// Extent length in sectors - public readonly ushort length; - /// Unknown - public readonly short unknown; - /// Extent starting sector - public readonly int start; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Extent + { + /// Extent length in sectors + public readonly ushort length; + /// Unknown + public readonly short unknown; + /// Extent starting sector + public readonly int start; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SubFile - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly Extent[] extents; - /// Unknown - public readonly uint unknown; - /// Unknown - public readonly uint unknown2; - /// Logical size in bytes - public readonly uint logical_size; - /// Unknown - public readonly uint unknown3; - /// Physical size in bytes - public readonly uint physical_size; - /// Unknown - public readonly uint unknown4; - /// Physical size in bytes - public readonly uint physical_size2; - /// Unknown - public readonly uint unknown5; - /// Unknown - public readonly uint unknown6; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SubFile + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly Extent[] extents; + /// Unknown + public readonly uint unknown; + /// Unknown + public readonly uint unknown2; + /// Logical size in bytes + public readonly uint logical_size; + /// Unknown + public readonly uint unknown3; + /// Physical size in bytes + public readonly uint physical_size; + /// Unknown + public readonly uint unknown4; + /// Physical size in bytes + public readonly uint physical_size2; + /// Unknown + public readonly uint unknown5; + /// Unknown + public readonly uint unknown6; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Direct - { - /// Unknown - public readonly uint unknown; - /// Unknown - public readonly uint unknown2; - /// Unknown - public readonly uint unknown3; - /// Mask 0x6000 - public readonly ushort subfiles_no; - /// Unknown - public readonly ushort unknown4; - /// Unknown - public readonly uint unknown5; - /// Unknown - public readonly uint unknown6; - /// Unknown - public readonly uint unknown7; - /// Some date - public readonly ushort date1; - /// Some time - public readonly ushort time1; - /// Some date - public readonly ushort date2; - /// Some time - public readonly ushort time2; - /// Unknown - public readonly uint unknown8; - /// Unknown - public readonly uint unknown9; - /// Unknown - public readonly uint unknown10; - /// Unknown - public readonly uint unknown11; - /// Unknown - public readonly uint unknown12; - /// Unknown - public readonly uint unknown13; - /// Unknown - public readonly uint unknown14; - /// Subfiles, length unknown - public readonly SubFile[] subfiles; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Direct + { + /// Unknown + public readonly uint unknown; + /// Unknown + public readonly uint unknown2; + /// Unknown + public readonly uint unknown3; + /// Mask 0x6000 + public readonly ushort subfiles_no; + /// Unknown + public readonly ushort unknown4; + /// Unknown + public readonly uint unknown5; + /// Unknown + public readonly uint unknown6; + /// Unknown + public readonly uint unknown7; + /// Some date + public readonly ushort date1; + /// Some time + public readonly ushort time1; + /// Some date + public readonly ushort date2; + /// Some time + public readonly ushort time2; + /// Unknown + public readonly uint unknown8; + /// Unknown + public readonly uint unknown9; + /// Unknown + public readonly uint unknown10; + /// Unknown + public readonly uint unknown11; + /// Unknown + public readonly uint unknown12; + /// Unknown + public readonly uint unknown13; + /// Unknown + public readonly uint unknown14; + /// Subfiles, length unknown + public readonly SubFile[] subfiles; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct MasterRecord - { - /// "MAST" - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] blockId; - /// Unknown - public readonly uint unknown; - /// Unknown - public readonly ushort unknown2; - /// Unknown - public readonly ushort unknown3; - /// Unknown - public readonly uint unknown4; - /// Unknown - public readonly ushort unknown5; - /// Unknown - public readonly ushort unknown6; - /// Unknown - public readonly ushort unknown7; - /// Unknown - public readonly ushort unknown8; - /// Unknown - public readonly uint unknown9; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct MasterRecord + { + /// "MAST" + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] blockId; + /// Unknown + public readonly uint unknown; + /// Unknown + public readonly ushort unknown2; + /// Unknown + public readonly ushort unknown3; + /// Unknown + public readonly uint unknown4; + /// Unknown + public readonly ushort unknown5; + /// Unknown + public readonly ushort unknown6; + /// Unknown + public readonly ushort unknown7; + /// Unknown + public readonly ushort unknown8; + /// Unknown + public readonly uint unknown9; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DciKey - { - /// Unknown - public readonly byte unknown; - /// Name size + 2 - public readonly byte size; - /// Unknown - public readonly byte unknown2; - /// Unknown - public readonly byte unknown3; - /// Unknown - public readonly byte unknown4; - /// Unknown - public readonly byte unknown5; - /// Name, length = size - 2 - public readonly byte[] name; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DciKey + { + /// Unknown + public readonly byte unknown; + /// Name size + 2 + public readonly byte size; + /// Unknown + public readonly byte unknown2; + /// Unknown + public readonly byte unknown3; + /// Unknown + public readonly byte unknown4; + /// Unknown + public readonly byte unknown5; + /// Name, length = size - 2 + public readonly byte[] name; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DciEntry - { - /// Key length - public readonly ushort key_len; - /// Record length - public readonly ushort record_len; - /// dci key - public readonly DciKey key; - /// Padding? Size is key_len - size of DciKey - public readonly byte[] padding; - /// Direct - public readonly Direct dir; - /// Padding? Size is record_len - size of Direct - public readonly byte[] unknown; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DciEntry + { + /// Key length + public readonly ushort key_len; + /// Record length + public readonly ushort record_len; + /// dci key + public readonly DciKey key; + /// Padding? Size is key_len - size of DciKey + public readonly byte[] padding; + /// Direct + public readonly Direct dir; + /// Padding? Size is record_len - size of Direct + public readonly byte[] unknown; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Dci - { - /// "DATA" - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] blockId; - /// Unknown - public readonly uint unknown; - /// Unknown - public readonly uint unknown2; - /// Unknown - public readonly uint unknown3; - /// Unknown - public readonly uint unknown4; - /// Unknown - public readonly uint unknown5; - /// Unknown - public readonly ushort unknown6; - /// Unknown - public readonly ushort unknown7; - /// Unknown - public readonly uint unknown8; - /// Unknown - public readonly uint unknown9; - /// Entries, size unknown - public readonly DciEntry[] entries; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Dci + { + /// "DATA" + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] blockId; + /// Unknown + public readonly uint unknown; + /// Unknown + public readonly uint unknown2; + /// Unknown + public readonly uint unknown3; + /// Unknown + public readonly uint unknown4; + /// Unknown + public readonly uint unknown5; + /// Unknown + public readonly ushort unknown6; + /// Unknown + public readonly ushort unknown7; + /// Unknown + public readonly uint unknown8; + /// Unknown + public readonly uint unknown9; + /// Entries, size unknown + public readonly DciEntry[] entries; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/AAIP.cs b/Aaru.Filesystems/ISO9660/Consts/AAIP.cs index 8a91d8e44..58b3fdb01 100644 --- a/Aaru.Filesystems/ISO9660/Consts/AAIP.cs +++ b/Aaru.Filesystems/ISO9660/Consts/AAIP.cs @@ -35,17 +35,16 @@ using System; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems -{ - public sealed partial class ISO9660 - { - const ushort AAIP_MAGIC = 0x414C; // "AL" - const ushort AAIP_MAGIC_OLD = 0x4141; // "AA" +namespace Aaru.Filesystems; - [Flags] - enum AAIPFlags : byte - { - Continue = 1 - } +public sealed partial class ISO9660 +{ + const ushort AAIP_MAGIC = 0x414C; // "AL" + const ushort AAIP_MAGIC_OLD = 0x4141; // "AA" + + [Flags] + enum AAIPFlags : byte + { + Continue = 1 } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/Amiga.cs b/Aaru.Filesystems/ISO9660/Consts/Amiga.cs index 6418b8fdf..e5ec72b03 100644 --- a/Aaru.Filesystems/ISO9660/Consts/Amiga.cs +++ b/Aaru.Filesystems/ISO9660/Consts/Amiga.cs @@ -35,32 +35,31 @@ using System; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + const ushort AMIGA_MAGIC = 0x4153; // "AS" + + [Flags] + enum AmigaFlags : byte { - const ushort AMIGA_MAGIC = 0x4153; // "AS" + Protection = 1 << 0, Comment = 1 << 1, CommentContinues = 1 << 2 + } - [Flags] - enum AmigaFlags : byte - { - Protection = 1 << 0, Comment = 1 << 1, CommentContinues = 1 << 2 - } + [Flags] + enum AmigaMultiuser : byte + { + GroupDelete = 1 << 0, GroupExec = 1 << 1, GroupWrite = 1 << 2, + GroupRead = 1 << 3, OtherDelete = 1 << 4, OtherExec = 1 << 5, + OtherWrite = 1 << 6, OtherRead = 1 << 7 + } - [Flags] - enum AmigaMultiuser : byte - { - GroupDelete = 1 << 0, GroupExec = 1 << 1, GroupWrite = 1 << 2, - GroupRead = 1 << 3, OtherDelete = 1 << 4, OtherExec = 1 << 5, - OtherWrite = 1 << 6, OtherRead = 1 << 7 - } - - [Flags] - enum AmigaAttributes : byte - { - OwnerDelete = 1 << 0, OwnerExec = 1 << 1, OwnerWrite = 1 << 2, - OwnerRead = 1 << 3, Archive = 1 << 4, Reentrant = 1 << 5, - Script = 1 << 6, Reserved = 1 << 7 - } + [Flags] + enum AmigaAttributes : byte + { + OwnerDelete = 1 << 0, OwnerExec = 1 << 1, OwnerWrite = 1 << 2, + OwnerRead = 1 << 3, Archive = 1 << 4, Reentrant = 1 << 5, + Script = 1 << 6, Reserved = 1 << 7 } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/Apple.cs b/Aaru.Filesystems/ISO9660/Consts/Apple.cs index 94496d564..58f299bc2 100644 --- a/Aaru.Filesystems/ISO9660/Consts/Apple.cs +++ b/Aaru.Filesystems/ISO9660/Consts/Apple.cs @@ -31,22 +31,21 @@ // In the loving memory of Facunda "Tata" Suárez Domínguez, R.I.P. 2019/07/24 // ****************************************************************************/ -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + const ushort APPLE_MAGIC = 0x4141; // "AA" + const ushort APPLE_MAGIC_OLD = 0x4241; // "BA" + + enum AppleId : byte { - const ushort APPLE_MAGIC = 0x4141; // "AA" - const ushort APPLE_MAGIC_OLD = 0x4241; // "BA" + ProDOS = 1, HFS = 2 + } - enum AppleId : byte - { - ProDOS = 1, HFS = 2 - } - - enum AppleOldId : byte - { - ProDOS = 1, TypeCreator = 2, TypeCreatorBundle = 3, - TypeCreatorIcon = 4, TypeCreatorIconBundle = 5, HFS = 6 - } + enum AppleOldId : byte + { + ProDOS = 1, TypeCreator = 2, TypeCreatorBundle = 3, + TypeCreatorIcon = 4, TypeCreatorIconBundle = 5, HFS = 6 } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/CDi.cs b/Aaru.Filesystems/ISO9660/Consts/CDi.cs index 168f24f27..3240d91c4 100644 --- a/Aaru.Filesystems/ISO9660/Consts/CDi.cs +++ b/Aaru.Filesystems/ISO9660/Consts/CDi.cs @@ -36,33 +36,32 @@ using Aaru.Helpers; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + const string CDI_MAGIC = "CD-I "; + static readonly int _cdiDirectoryRecordSize = Marshal.SizeOf(); + static readonly int _cdiSystemAreaSize = Marshal.SizeOf(); + + [Flags] + enum CdiVolumeFlags : byte { - const string CDI_MAGIC = "CD-I "; - static readonly int _cdiDirectoryRecordSize = Marshal.SizeOf(); - static readonly int _cdiSystemAreaSize = Marshal.SizeOf(); + // Escapes are not ISO 2375 but ISO 2022 + NotISO2375 = 1 + } - [Flags] - enum CdiVolumeFlags : byte - { - // Escapes are not ISO 2375 but ISO 2022 - NotISO2375 = 1 - } + [Flags] + enum CdiFileFlags : byte + { + Hidden = 0x01 + } - [Flags] - enum CdiFileFlags : byte - { - Hidden = 0x01 - } - - [Flags] - enum CdiAttributes : ushort - { - OwnerRead = 1 << 0, OwnerExecute = 1 << 2, GroupRead = 1 << 4, - GroupExecute = 1 << 6, OtherRead = 1 << 8, OtherExecute = 1 << 10, - DigitalAudio = 1 << 14, Directory = 1 << 15 - } + [Flags] + enum CdiAttributes : ushort + { + OwnerRead = 1 << 0, OwnerExecute = 1 << 2, GroupRead = 1 << 4, + GroupExecute = 1 << 6, OtherRead = 1 << 8, OtherExecute = 1 << 10, + DigitalAudio = 1 << 14, Directory = 1 << 15 } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/ElTorito.cs b/Aaru.Filesystems/ISO9660/Consts/ElTorito.cs index 8805c747c..a799e7b76 100644 --- a/Aaru.Filesystems/ISO9660/Consts/ElTorito.cs +++ b/Aaru.Filesystems/ISO9660/Consts/ElTorito.cs @@ -35,36 +35,35 @@ using System; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + const ushort EL_TORITO_MAGIC = 0xAA55; + const int EL_TORITO_ENTRY_SIZE = 32; + + enum ElToritoIndicator : byte { - const ushort EL_TORITO_MAGIC = 0xAA55; - const int EL_TORITO_ENTRY_SIZE = 32; + Header = 1, Extension = 0x44, Bootable = 0x88, + MoreHeaders = 0x90, LastHeader = 0x91 + } - enum ElToritoIndicator : byte - { - Header = 1, Extension = 0x44, Bootable = 0x88, - MoreHeaders = 0x90, LastHeader = 0x91 - } + enum ElToritoPlatform : byte + { + x86 = 0, PowerPC = 1, Macintosh = 2, + EFI = 0xef + } - enum ElToritoPlatform : byte - { - x86 = 0, PowerPC = 1, Macintosh = 2, - EFI = 0xef - } + enum ElToritoEmulation : byte + { + None = 0, Md2Hd = 1, Mf2Hd = 2, + Mf2Ed = 3, Hdd = 4 + } - enum ElToritoEmulation : byte - { - None = 0, Md2Hd = 1, Mf2Hd = 2, - Mf2Ed = 3, Hdd = 4 - } - - [Flags] - enum ElToritoFlags : byte - { - Reserved = 0x10, Continued = 0x20, ATAPI = 0x40, - SCSI = 0x08 - } + [Flags] + enum ElToritoFlags : byte + { + Reserved = 0x10, Continued = 0x20, ATAPI = 0x40, + SCSI = 0x08 } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/HighSierra.cs b/Aaru.Filesystems/ISO9660/Consts/HighSierra.cs index d26e72eb3..22e52b2df 100644 --- a/Aaru.Filesystems/ISO9660/Consts/HighSierra.cs +++ b/Aaru.Filesystems/ISO9660/Consts/HighSierra.cs @@ -33,11 +33,10 @@ using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 - { - const string HIGH_SIERRA_MAGIC = "CDROM"; - static readonly int _highSierraDirectoryRecordSize = Marshal.SizeOf(); - } + const string HIGH_SIERRA_MAGIC = "CDROM"; + static readonly int _highSierraDirectoryRecordSize = Marshal.SizeOf(); } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/ISO.cs b/Aaru.Filesystems/ISO9660/Consts/ISO.cs index eb3b89f11..6c28e055f 100644 --- a/Aaru.Filesystems/ISO9660/Consts/ISO.cs +++ b/Aaru.Filesystems/ISO9660/Consts/ISO.cs @@ -35,36 +35,35 @@ using System; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + const string ISO_MAGIC = "CD001"; + + [Flags] + enum FileFlags : byte { - const string ISO_MAGIC = "CD001"; + Hidden = 0x01, Directory = 0x02, Associated = 0x04, + Record = 0x08, Protected = 0x10, MultiExtent = 0x80 + } - [Flags] - enum FileFlags : byte - { - Hidden = 0x01, Directory = 0x02, Associated = 0x04, - Record = 0x08, Protected = 0x10, MultiExtent = 0x80 - } + [Flags] + enum Permissions : ushort + { + SystemRead = 0x01, SystemExecute = 0x04, OwnerRead = 0x10, + OwnerExecute = 0x40, GroupRead = 0x100, GroupExecute = 0x400, + OtherRead = 0x1000, OtherExecute = 0x4000 + } - [Flags] - enum Permissions : ushort - { - SystemRead = 0x01, SystemExecute = 0x04, OwnerRead = 0x10, - OwnerExecute = 0x40, GroupRead = 0x100, GroupExecute = 0x400, - OtherRead = 0x1000, OtherExecute = 0x4000 - } + enum RecordFormat : byte + { + Unspecified = 0, FixedLength = 1, VariableLength = 2, + VariableLengthAlternate = 3 + } - enum RecordFormat : byte - { - Unspecified = 0, FixedLength = 1, VariableLength = 2, - VariableLengthAlternate = 3 - } - - enum RecordAttribute : byte - { - LFCR = 0, ISO1539 = 1, ControlContained = 2 - } + enum RecordAttribute : byte + { + LFCR = 0, ISO1539 = 1, ControlContained = 2 } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/Internal.cs b/Aaru.Filesystems/ISO9660/Consts/Internal.cs index f1f017b74..1d5ba627b 100644 --- a/Aaru.Filesystems/ISO9660/Consts/Internal.cs +++ b/Aaru.Filesystems/ISO9660/Consts/Internal.cs @@ -34,18 +34,17 @@ using System.Diagnostics.CodeAnalysis; using Aaru.Helpers; -namespace Aaru.Filesystems -{ - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class ISO9660 - { - const byte MODE2_FORM2 = 0x20; - static readonly int _directoryRecordSize = Marshal.SizeOf(); +namespace Aaru.Filesystems; - enum Namespace - { - Normal, Vms, Joliet, - Rrip, Romeo - } +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class ISO9660 +{ + const byte MODE2_FORM2 = 0x20; + static readonly int _directoryRecordSize = Marshal.SizeOf(); + + enum Namespace + { + Normal, Vms, Joliet, + Rrip, Romeo } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/RRIP.cs b/Aaru.Filesystems/ISO9660/Consts/RRIP.cs index 40175e142..39dd98d40 100644 --- a/Aaru.Filesystems/ISO9660/Consts/RRIP.cs +++ b/Aaru.Filesystems/ISO9660/Consts/RRIP.cs @@ -35,59 +35,58 @@ using System; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + const ushort RRIP_MAGIC = 0x5252; // "RR" + const ushort RRIP_POSIX_ATTRIBUTES = 0x5058; // "PX" + const ushort RRIP_POSIX_DEV_NO = 0x504E; // "PN" + const ushort RRIP_SYMLINK = 0x534C; // "SL" + const ushort RRIP_NAME = 0x4E4D; // "NM" + const ushort RRIP_CHILDLINK = 0x434C; // "CL" + const ushort RRIP_PARENTLINK = 0x504C; // "PL" + const ushort RRIP_RELOCATED_DIR = 0x5245; // "RE" + const ushort RRIP_TIMESTAMPS = 0x5446; // "TF" + const ushort RRIP_SPARSE = 0x5346; // "SF" + + [Flags] + enum PosixMode : uint { - const ushort RRIP_MAGIC = 0x5252; // "RR" - const ushort RRIP_POSIX_ATTRIBUTES = 0x5058; // "PX" - const ushort RRIP_POSIX_DEV_NO = 0x504E; // "PN" - const ushort RRIP_SYMLINK = 0x534C; // "SL" - const ushort RRIP_NAME = 0x4E4D; // "NM" - const ushort RRIP_CHILDLINK = 0x434C; // "CL" - const ushort RRIP_PARENTLINK = 0x504C; // "PL" - const ushort RRIP_RELOCATED_DIR = 0x5245; // "RE" - const ushort RRIP_TIMESTAMPS = 0x5446; // "TF" - const ushort RRIP_SPARSE = 0x5346; // "SF" + OwnerRead = 0x0100, OwnerWrite = 0x0080, OwnerExecute = 0x0040, + GroupRead = 0x0020, GroupWrite = 0x0010, GroupExecute = 0x0008, + OtherRead = 0x0004, OtherWrite = 0x0002, OtherExecute = 0x0001, + SetUID = 0x0800, SetGid = 0x0400, IsVTX = 0x0200, + Socket = 0xC000, Symlink = 0xA000, Regular = 0x8000, + Block = 0x6000, Character = 0x2000, Directory = 0x4000, + Pipe = 0x1000 + } - [Flags] - enum PosixMode : uint - { - OwnerRead = 0x0100, OwnerWrite = 0x0080, OwnerExecute = 0x0040, - GroupRead = 0x0020, GroupWrite = 0x0010, GroupExecute = 0x0008, - OtherRead = 0x0004, OtherWrite = 0x0002, OtherExecute = 0x0001, - SetUID = 0x0800, SetGid = 0x0400, IsVTX = 0x0200, - Socket = 0xC000, Symlink = 0xA000, Regular = 0x8000, - Block = 0x6000, Character = 0x2000, Directory = 0x4000, - Pipe = 0x1000 - } + [Flags] + enum SymlinkFlags : byte + { + Continue = 1 + } - [Flags] - enum SymlinkFlags : byte - { - Continue = 1 - } + [Flags] + enum SymlinkComponentFlags : byte + { + Continue = 1, Current = 2, Parent = 4, + Root = 8, Mountpoint = 16, Networkname = 32 + } - [Flags] - enum SymlinkComponentFlags : byte - { - Continue = 1, Current = 2, Parent = 4, - Root = 8, Mountpoint = 16, Networkname = 32 - } + [Flags] + enum AlternateNameFlags : byte + { + Continue = 1, Current = 2, Parent = 4, + Networkname = 32 + } - [Flags] - enum AlternateNameFlags : byte - { - Continue = 1, Current = 2, Parent = 4, - Networkname = 32 - } - - [Flags] - enum TimestampFlags : byte - { - Creation = 1 << 0, Modification = 1 << 1, Access = 1 << 2, - AttributeChange = 1 << 3, Backup = 1 << 4, Expiration = 1 << 5, - Effective = 1 << 6, LongFormat = 1 << 7 - } + [Flags] + enum TimestampFlags : byte + { + Creation = 1 << 0, Modification = 1 << 1, Access = 1 << 2, + AttributeChange = 1 << 3, Backup = 1 << 4, Expiration = 1 << 5, + Effective = 1 << 6, LongFormat = 1 << 7 } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/SUSP.cs b/Aaru.Filesystems/ISO9660/Consts/SUSP.cs index 6734dc386..8ae5aa866 100644 --- a/Aaru.Filesystems/ISO9660/Consts/SUSP.cs +++ b/Aaru.Filesystems/ISO9660/Consts/SUSP.cs @@ -35,17 +35,16 @@ using System.Diagnostics.CodeAnalysis; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class ISO9660 { - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class ISO9660 - { - const ushort SUSP_CONTINUATION = 0x4345; // "CE" - const ushort SUSP_PADDING = 0x5044; // "PD" - const ushort SUSP_INDICATOR = 0x5350; // "SP" - const ushort SUSP_TERMINATOR = 0x5354; // "ST" - const ushort SUSP_REFERENCE = 0x4552; // "ER" - const ushort SUSP_SELECTOR = 0x4553; // "ES" - const ushort SUSP_MAGIC = 0xBEEF; - } + const ushort SUSP_CONTINUATION = 0x4345; // "CE" + const ushort SUSP_PADDING = 0x5044; // "PD" + const ushort SUSP_INDICATOR = 0x5350; // "SP" + const ushort SUSP_TERMINATOR = 0x5354; // "ST" + const ushort SUSP_REFERENCE = 0x4552; // "ER" + const ushort SUSP_SELECTOR = 0x4553; // "ES" + const ushort SUSP_MAGIC = 0xBEEF; } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/XA.cs b/Aaru.Filesystems/ISO9660/Consts/XA.cs index bec6ad5db..a289b3f37 100644 --- a/Aaru.Filesystems/ISO9660/Consts/XA.cs +++ b/Aaru.Filesystems/ISO9660/Consts/XA.cs @@ -35,27 +35,26 @@ using System; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + const ushort XA_MAGIC = 0x5841; // "XA" + + [Flags] + enum XaAttributes : ushort { - const ushort XA_MAGIC = 0x5841; // "XA" + SystemRead = 0x01, SystemExecute = 0x04, OwnerRead = 0x10, + OwnerExecute = 0x40, GroupRead = 0x100, GroupExecute = 0x400, + Mode2Form1 = 0x800, Mode2Form2 = 0x1000, Interleaved = 0x2000, + Cdda = 0x4000, Directory = 0x8000 + } - [Flags] - enum XaAttributes : ushort - { - SystemRead = 0x01, SystemExecute = 0x04, OwnerRead = 0x10, - OwnerExecute = 0x40, GroupRead = 0x100, GroupExecute = 0x400, - Mode2Form1 = 0x800, Mode2Form2 = 0x1000, Interleaved = 0x2000, - Cdda = 0x4000, Directory = 0x8000 - } - - [Flags] - enum Mode2Submode : byte - { - EndOfFile = 0x80, RealTime = 0x40, Form2 = 0x20, - Trigger = 0x10, Data = 0x08, Audio = 0x04, - Video = 0x02, EndOfRecord = 0x01 - } + [Flags] + enum Mode2Submode : byte + { + EndOfFile = 0x80, RealTime = 0x40, Form2 = 0x20, + Trigger = 0x10, Data = 0x08, Audio = 0x04, + Video = 0x02, EndOfRecord = 0x01 } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Consts/Ziso.cs b/Aaru.Filesystems/ISO9660/Consts/Ziso.cs index 88134e4ed..02699857a 100644 --- a/Aaru.Filesystems/ISO9660/Consts/Ziso.cs +++ b/Aaru.Filesystems/ISO9660/Consts/Ziso.cs @@ -33,14 +33,13 @@ using System.Diagnostics.CodeAnalysis; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class ISO9660 { - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class ISO9660 - { - const ulong ZISO_HEADER_MAGIC = 0x37E45396C9DBD607; - const ulong ZISO_HEADER_CIGAM = 0x07D6DBC99653E437; - const ushort ZISO_MAGIC = 0x5A46; // "ZF" - const ushort ZISO_PAGED_ZLIB = 0x707A; // "pz" - } + const ulong ZISO_HEADER_MAGIC = 0x37E45396C9DBD607; + const ulong ZISO_HEADER_CIGAM = 0x07D6DBC99653E437; + const ushort ZISO_MAGIC = 0x5A46; // "ZF" + const ushort ZISO_PAGED_ZLIB = 0x707A; // "pz" } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Date.cs b/Aaru.Filesystems/ISO9660/Date.cs index df4946c79..8ec326ca0 100644 --- a/Aaru.Filesystems/ISO9660/Date.cs +++ b/Aaru.Filesystems/ISO9660/Date.cs @@ -34,52 +34,51 @@ using System; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + static DateTime? DecodeIsoDateTime(byte[] timestamp) { - static DateTime? DecodeIsoDateTime(byte[] timestamp) + switch(timestamp?.Length) { - switch(timestamp?.Length) - { - case 7: return DecodeIsoDateTime(Marshal.ByteArrayToStructureLittleEndian(timestamp)); - case 17: return DateHandlers.Iso9660ToDateTime(timestamp); - default: return null; - } + case 7: return DecodeIsoDateTime(Marshal.ByteArrayToStructureLittleEndian(timestamp)); + case 17: return DateHandlers.Iso9660ToDateTime(timestamp); + default: return null; } + } - static DateTime? DecodeIsoDateTime(IsoTimestamp timestamp) + static DateTime? DecodeIsoDateTime(IsoTimestamp timestamp) + { + try { - try - { - var date = new DateTime(timestamp.Years + 1900, timestamp.Month, timestamp.Day, timestamp.Hour, - timestamp.Minute, timestamp.Second, DateTimeKind.Unspecified); + var date = new DateTime(timestamp.Years + 1900, timestamp.Month, timestamp.Day, timestamp.Hour, + timestamp.Minute, timestamp.Second, DateTimeKind.Unspecified); - date = date.AddMinutes(timestamp.GmtOffset * 15); + date = date.AddMinutes(timestamp.GmtOffset * 15); - return TimeZoneInfo.ConvertTimeToUtc(date, TimeZoneInfo.FindSystemTimeZoneById("GMT")); - } - catch(Exception) - { - // ISO says timestamp can be unspecified - return null; - } + return TimeZoneInfo.ConvertTimeToUtc(date, TimeZoneInfo.FindSystemTimeZoneById("GMT")); } - - static DateTime? DecodeHighSierraDateTime(HighSierraTimestamp timestamp) + catch(Exception) { - try - { - var date = new DateTime(timestamp.Years + 1900, timestamp.Month, timestamp.Day, timestamp.Hour, - timestamp.Minute, timestamp.Second, DateTimeKind.Unspecified); + // ISO says timestamp can be unspecified + return null; + } + } - return TimeZoneInfo.ConvertTimeToUtc(date, TimeZoneInfo.FindSystemTimeZoneById("GMT")); - } - catch(Exception) - { - // ISO says timestamp can be unspecified, suppose same for High Sierra - return null; - } + static DateTime? DecodeHighSierraDateTime(HighSierraTimestamp timestamp) + { + try + { + var date = new DateTime(timestamp.Years + 1900, timestamp.Month, timestamp.Day, timestamp.Hour, + timestamp.Minute, timestamp.Second, DateTimeKind.Unspecified); + + return TimeZoneInfo.ConvertTimeToUtc(date, TimeZoneInfo.FindSystemTimeZoneById("GMT")); + } + catch(Exception) + { + // ISO says timestamp can be unspecified, suppose same for High Sierra + return null; } } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Dir.cs b/Aaru.Filesystems/ISO9660/Dir.cs index 3666ad7d6..75c588dd4 100644 --- a/Aaru.Filesystems/ISO9660/Dir.cs +++ b/Aaru.Filesystems/ISO9660/Dir.cs @@ -40,46 +40,60 @@ using System.Text; using Aaru.CommonTypes.Enums; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + Dictionary> _directoryCache; + + /// + public ErrorNumber ReadDir(string path, out List contents) { - Dictionary> _directoryCache; + contents = null; - /// - public ErrorNumber ReadDir(string path, out List contents) + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(string.IsNullOrWhiteSpace(path) || + path == "/") { - contents = null; + contents = GetFilenames(_rootDirectoryCache); - if(!_mounted) - return ErrorNumber.AccessDenied; + return ErrorNumber.NoError; + } - if(string.IsNullOrWhiteSpace(path) || - path == "/") - { - contents = GetFilenames(_rootDirectoryCache); + string cutPath = path.StartsWith("/", StringComparison.Ordinal) + ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) + : path.ToLower(CultureInfo.CurrentUICulture); - return ErrorNumber.NoError; - } + if(_directoryCache.TryGetValue(cutPath, out Dictionary currentDirectory)) + { + contents = currentDirectory.Keys.ToList(); - string cutPath = path.StartsWith("/", StringComparison.Ordinal) - ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) - : path.ToLower(CultureInfo.CurrentUICulture); + return ErrorNumber.NoError; + } - if(_directoryCache.TryGetValue(cutPath, out Dictionary currentDirectory)) - { - contents = currentDirectory.Keys.ToList(); + string[] pieces = cutPath.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); - return ErrorNumber.NoError; - } + KeyValuePair entry = + _rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[0]); - string[] pieces = cutPath.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + if(string.IsNullOrEmpty(entry.Key)) + return ErrorNumber.NoSuchFile; - KeyValuePair entry = - _rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[0]); + if(!entry.Value.Flags.HasFlag(FileFlags.Directory)) + return ErrorNumber.NotDirectory; + + string currentPath = pieces[0]; + + currentDirectory = _rootDirectoryCache; + + for(int p = 0; p < pieces.Length; p++) + { + entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[p]); if(string.IsNullOrEmpty(entry.Key)) return ErrorNumber.NoSuchFile; @@ -87,1099 +101,1084 @@ namespace Aaru.Filesystems if(!entry.Value.Flags.HasFlag(FileFlags.Directory)) return ErrorNumber.NotDirectory; - string currentPath = pieces[0]; + currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; - currentDirectory = _rootDirectoryCache; + if(_directoryCache.TryGetValue(currentPath, out currentDirectory)) + continue; - for(int p = 0; p < pieces.Length; p++) - { - entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[p]); + if(entry.Value.Extents.Count == 0) + return ErrorNumber.InvalidArgument; - if(string.IsNullOrEmpty(entry.Key)) - return ErrorNumber.NoSuchFile; + currentDirectory = _cdi + ? DecodeCdiDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength, + entry.Value.Extents[0].size) + : _highSierra + ? DecodeHighSierraDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength, + entry.Value.Extents[0].size) + : DecodeIsoDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength, + entry.Value.Extents[0].size); - if(!entry.Value.Flags.HasFlag(FileFlags.Directory)) - return ErrorNumber.NotDirectory; + if(_usePathTable) + foreach(DecodedDirectoryEntry subDirectory in _cdi + ? GetSubdirsFromCdiPathTable(currentPath) + : _highSierra + ? GetSubdirsFromHighSierraPathTable(currentPath) + : GetSubdirsFromIsoPathTable(currentPath)) + currentDirectory[subDirectory.Filename] = subDirectory; - currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; - - if(_directoryCache.TryGetValue(currentPath, out currentDirectory)) - continue; - - if(entry.Value.Extents.Count == 0) - return ErrorNumber.InvalidArgument; - - currentDirectory = _cdi - ? DecodeCdiDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength, - entry.Value.Extents[0].size) - : _highSierra - ? DecodeHighSierraDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength, - entry.Value.Extents[0].size) - : DecodeIsoDirectory(entry.Value.Extents[0].extent + entry.Value.XattrLength, - entry.Value.Extents[0].size); - - if(_usePathTable) - foreach(DecodedDirectoryEntry subDirectory in _cdi - ? GetSubdirsFromCdiPathTable(currentPath) - : _highSierra - ? GetSubdirsFromHighSierraPathTable(currentPath) - : GetSubdirsFromIsoPathTable(currentPath)) - currentDirectory[subDirectory.Filename] = subDirectory; - - _directoryCache.Add(currentPath, currentDirectory); - } - - contents = GetFilenames(currentDirectory); - - return ErrorNumber.NoError; + _directoryCache.Add(currentPath, currentDirectory); } - List GetFilenames(Dictionary dirents) - { - List contents = new(); + contents = GetFilenames(currentDirectory); - foreach(DecodedDirectoryEntry entry in dirents.Values) - switch(_namespace) - { - case Namespace.Normal: - contents.Add(entry.Filename.EndsWith(";1", StringComparison.Ordinal) - ? entry.Filename.Substring(0, entry.Filename.Length - 2) : entry.Filename); + return ErrorNumber.NoError; + } - break; - case Namespace.Vms: - case Namespace.Joliet: - case Namespace.Rrip: - case Namespace.Romeo: - contents.Add(entry.Filename); + List GetFilenames(Dictionary dirents) + { + List contents = new(); - break; - default: throw new ArgumentOutOfRangeException(); - } - - return contents; - } - - Dictionary DecodeCdiDirectory(ulong start, uint size) - { - Dictionary entries = new(); - int entryOff = 0; - - byte[] data = ReadSingleExtent(size, (uint)start); - - while(entryOff + _cdiDirectoryRecordSize < data.Length) + foreach(DecodedDirectoryEntry entry in dirents.Values) + switch(_namespace) { - CdiDirectoryRecord record = - Marshal.ByteArrayToStructureBigEndian(data, entryOff, _cdiDirectoryRecordSize); - - if(record.length == 0) - { - // Skip to next sector - if(data.Length - (((entryOff / 2048) + 1) * 2048) > 0) - { - entryOff = ((entryOff / 2048) + 1) * 2048; - - continue; - } + case Namespace.Normal: + contents.Add(entry.Filename.EndsWith(";1", StringComparison.Ordinal) + ? entry.Filename.Substring(0, entry.Filename.Length - 2) : entry.Filename); break; - } + case Namespace.Vms: + case Namespace.Joliet: + case Namespace.Rrip: + case Namespace.Romeo: + contents.Add(entry.Filename); - // Special entries for current and parent directories, skip them - if(record.name_len == 1) - if(data[entryOff + _directoryRecordSize] == 0 || - data[entryOff + _directoryRecordSize] == 1) - { - entryOff += record.length; + break; + default: throw new ArgumentOutOfRangeException(); + } - continue; - } + return contents; + } - var entry = new DecodedDirectoryEntry + Dictionary DecodeCdiDirectory(ulong start, uint size) + { + Dictionary entries = new(); + int entryOff = 0; + + byte[] data = ReadSingleExtent(size, (uint)start); + + while(entryOff + _cdiDirectoryRecordSize < data.Length) + { + CdiDirectoryRecord record = + Marshal.ByteArrayToStructureBigEndian(data, entryOff, _cdiDirectoryRecordSize); + + if(record.length == 0) + { + // Skip to next sector + if(data.Length - (((entryOff / 2048) + 1) * 2048) > 0) { - Size = record.size, - Filename = Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len), - VolumeSequenceNumber = record.volume_sequence_number, - Timestamp = DecodeHighSierraDateTime(record.date), - XattrLength = record.xattr_len - }; - - entry.Extents = new List<(uint extent, uint size)>(); - - if(record.size != 0) - entry.Extents.Add((record.start_lbn, record.size)); - - if(record.flags.HasFlag(CdiFileFlags.Hidden)) - { - entry.Flags |= FileFlags.Hidden; + entryOff = ((entryOff / 2048) + 1) * 2048; continue; } - int systemAreaStart = entryOff + record.name_len + _cdiDirectoryRecordSize; + break; + } - if(systemAreaStart % 2 != 0) - systemAreaStart++; + // Special entries for current and parent directories, skip them + if(record.name_len == 1) + if(data[entryOff + _directoryRecordSize] == 0 || + data[entryOff + _directoryRecordSize] == 1) + { + entryOff += record.length; - entry.CdiSystemArea = - Marshal.ByteArrayToStructureBigEndian(data, systemAreaStart, _cdiSystemAreaSize); + continue; + } - if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory)) - entry.Flags |= FileFlags.Directory; + var entry = new DecodedDirectoryEntry + { + Size = record.size, + Filename = Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len), + VolumeSequenceNumber = record.volume_sequence_number, + Timestamp = DecodeHighSierraDateTime(record.date), + XattrLength = record.xattr_len + }; - if(!entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory) || - !_usePathTable) + entry.Extents = new List<(uint extent, uint size)>(); + + if(record.size != 0) + entry.Extents.Add((record.start_lbn, record.size)); + + if(record.flags.HasFlag(CdiFileFlags.Hidden)) + { + entry.Flags |= FileFlags.Hidden; + + continue; + } + + int systemAreaStart = entryOff + record.name_len + _cdiDirectoryRecordSize; + + if(systemAreaStart % 2 != 0) + systemAreaStart++; + + entry.CdiSystemArea = + Marshal.ByteArrayToStructureBigEndian(data, systemAreaStart, _cdiSystemAreaSize); + + if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory)) + entry.Flags |= FileFlags.Directory; + + if(!entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory) || + !_usePathTable) + entries[entry.Filename] = entry; + + entryOff += record.length; + } + + return entries; + } + + Dictionary DecodeHighSierraDirectory(ulong start, uint size) + { + Dictionary entries = new(); + int entryOff = 0; + + byte[] data = ReadSingleExtent(size, (uint)start); + + while(entryOff + _directoryRecordSize < data.Length) + { + HighSierraDirectoryRecord record = + Marshal.ByteArrayToStructureLittleEndian(data, entryOff, + _highSierraDirectoryRecordSize); + + if(record.length == 0) + { + // Skip to next sector + if(data.Length - (((entryOff / 2048) + 1) * 2048) > 0) + { + entryOff = ((entryOff / 2048) + 1) * 2048; + + continue; + } + + break; + } + + // Special entries for current and parent directories, skip them + if(record.name_len == 1) + if(data[entryOff + _directoryRecordSize] == 0 || + data[entryOff + _directoryRecordSize] == 1) + { + entryOff += record.length; + + continue; + } + + var entry = new DecodedDirectoryEntry + { + Size = record.size, + Flags = record.flags, + Interleave = record.interleave, + VolumeSequenceNumber = record.volume_sequence_number, + Filename = Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len), + Timestamp = DecodeHighSierraDateTime(record.date), + XattrLength = record.xattr_len + }; + + entry.Extents = new List<(uint extent, uint size)>(); + + if(record.size != 0) + entry.Extents.Add((record.extent, record.size)); + + if(entry.Flags.HasFlag(FileFlags.Directory) && _usePathTable) + { + entryOff += record.length; + + continue; + } + + if(!entries.ContainsKey(entry.Filename)) + entries.Add(entry.Filename, entry); + + entryOff += record.length; + } + + if(_useTransTbl) + DecodeTransTable(entries); + + return entries; + } + + Dictionary DecodeIsoDirectory(ulong start, uint size) + { + Dictionary entries = new(); + int entryOff = 0; + + byte[] data = ReadSingleExtent(size, (uint)start); + + while(entryOff + _directoryRecordSize < data.Length) + { + DirectoryRecord record = + Marshal.ByteArrayToStructureLittleEndian(data, entryOff, _directoryRecordSize); + + if(record.length == 0) + { + // Skip to next sector + if(data.Length - (((entryOff / 2048) + 1) * 2048) > 0) + { + entryOff = ((entryOff / 2048) + 1) * 2048; + + continue; + } + + break; + } + + // Special entries for current and parent directories, skip them + if(record.name_len == 1) + if(data[entryOff + _directoryRecordSize] == 0 || + data[entryOff + _directoryRecordSize] == 1) + { + entryOff += record.length; + + continue; + } + + var entry = new DecodedDirectoryEntry + { + Size = record.size, + Flags = record.flags, + Filename = + _joliet ? Encoding.BigEndianUnicode.GetString(data, entryOff + _directoryRecordSize, + record.name_len) + : Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len), + FileUnitSize = record.file_unit_size, + Interleave = record.interleave, + VolumeSequenceNumber = record.volume_sequence_number, + Timestamp = DecodeIsoDateTime(record.date), + XattrLength = record.xattr_len + }; + + entry.Extents = new List<(uint extent, uint size)>(); + + if(record.size != 0) + entry.Extents.Add((record.extent, record.size)); + + if(entry.Flags.HasFlag(FileFlags.Directory) && _usePathTable) + { + entryOff += record.length; + + continue; + } + + // Mac OS can use slashes, we cannot + entry.Filename = entry.Filename.Replace('/', '\u2215'); + + // Tailing '.' is only allowed on RRIP. If present it will be recreated below with the alternate name + if(entry.Filename.EndsWith(".", StringComparison.Ordinal)) + entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 1); + + if(entry.Filename.EndsWith(".;1", StringComparison.Ordinal)) + entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 3) + ";1"; + + // This is a legal Joliet name, different from VMS version fields, but Nero MAX incorrectly creates these filenames + if(_joliet && entry.Filename.EndsWith(";1", StringComparison.Ordinal)) + entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 2); + + int systemAreaStart = entryOff + record.name_len + _directoryRecordSize; + int systemAreaLength = record.length - record.name_len - _directoryRecordSize; + + if(systemAreaStart % 2 != 0) + { + systemAreaStart++; + systemAreaLength--; + } + + DecodeSystemArea(data, systemAreaStart, systemAreaStart + systemAreaLength, ref entry, + out bool hasResourceFork); + + if(entry.Flags.HasFlag(FileFlags.Associated)) + { + if(entries.ContainsKey(entry.Filename)) + { + if(hasResourceFork) + { + entries[entry.Filename].ResourceFork.Size += entry.Size; + entries[entry.Filename].ResourceFork.Extents.Add(entry.Extents[0]); + } + else + { + entries[entry.Filename].AssociatedFile.Size += entry.Size; + entries[entry.Filename].AssociatedFile.Extents.Add(entry.Extents[0]); + } + } + else + { + entries[entry.Filename] = new DecodedDirectoryEntry + { + Size = 0, + Flags = record.flags ^ FileFlags.Associated, + FileUnitSize = 0, + Interleave = 0, + VolumeSequenceNumber = record.volume_sequence_number, + Filename = entry.Filename, + Timestamp = DecodeIsoDateTime(record.date), + XattrLength = 0 + }; + + if(hasResourceFork) + entries[entry.Filename].ResourceFork = entry; + else + entries[entry.Filename].AssociatedFile = entry; + } + } + else + { + if(entries.ContainsKey(entry.Filename)) + { + entries[entry.Filename].Size += entry.Size; + + // Can appear after an associated file + if(entries[entry.Filename].Extents is null) + { + entries[entry.Filename].Extents = new List<(uint extent, uint size)>(); + entries[entry.Filename].Flags = entry.Flags; + entries[entry.Filename].FileUnitSize = entry.FileUnitSize; + entries[entry.Filename].Interleave = entry.Interleave; + entries[entry.Filename].VolumeSequenceNumber = entry.VolumeSequenceNumber; + entries[entry.Filename].Filename = entry.Filename; + entries[entry.Filename].Timestamp = entry.Timestamp; + entries[entry.Filename].XattrLength = entry.XattrLength; + } + + if(entry.Extents?.Count > 0) + entries[entry.Filename].Extents.Add(entry.Extents[0]); + } + else entries[entry.Filename] = entry; - - entryOff += record.length; } - return entries; + entryOff += record.length; } - Dictionary DecodeHighSierraDirectory(ulong start, uint size) + if(_useTransTbl) + DecodeTransTable(entries); + + // Relocated directories should be shown in correct place when using Rock Ridge namespace + return _namespace == Namespace.Rrip + ? entries.Where(e => !e.Value.RockRidgeRelocated).ToDictionary(x => x.Key, x => x.Value) + : entries; + } + + void DecodeTransTable(Dictionary entries) + { + KeyValuePair transTblEntry = + entries.FirstOrDefault(e => !e.Value.Flags.HasFlag(FileFlags.Directory) && + (e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == "trans.tbl" || + e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == "trans.tbl;1")); + + if(transTblEntry.Value == null) + return; + + byte[] transTbl = ReadWithExtents(0, (long)transTblEntry.Value.Size, transTblEntry.Value.Extents, + transTblEntry.Value.XA?.signature == XA_MAGIC && + transTblEntry.Value.XA?.attributes.HasFlag(XaAttributes.Interleaved) == + true, transTblEntry.Value.XA?.filenumber ?? 0); + + var mr = new MemoryStream(transTbl, 0, (int)transTblEntry.Value.Size, false); + var sr = new StreamReader(mr, Encoding); + + string line = sr.ReadLine(); + + while(line != null) { - Dictionary entries = new(); - int entryOff = 0; + // Skip the type field and the first space + string cutLine = line.Substring(2); + int spaceIndex = cutLine.IndexOf(' '); + string originalName = cutLine.Substring(0, spaceIndex); + string originalNameWithVersion; + string newName = cutLine.Substring(spaceIndex + 1).TrimStart(); - byte[] data = ReadSingleExtent(size, (uint)start); - - while(entryOff + _directoryRecordSize < data.Length) + if(originalName.EndsWith(";1", StringComparison.Ordinal)) { - HighSierraDirectoryRecord record = - Marshal.ByteArrayToStructureLittleEndian(data, entryOff, - _highSierraDirectoryRecordSize); - - if(record.length == 0) - { - // Skip to next sector - if(data.Length - (((entryOff / 2048) + 1) * 2048) > 0) - { - entryOff = ((entryOff / 2048) + 1) * 2048; - - continue; - } - - break; - } - - // Special entries for current and parent directories, skip them - if(record.name_len == 1) - if(data[entryOff + _directoryRecordSize] == 0 || - data[entryOff + _directoryRecordSize] == 1) - { - entryOff += record.length; - - continue; - } - - var entry = new DecodedDirectoryEntry - { - Size = record.size, - Flags = record.flags, - Interleave = record.interleave, - VolumeSequenceNumber = record.volume_sequence_number, - Filename = Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len), - Timestamp = DecodeHighSierraDateTime(record.date), - XattrLength = record.xattr_len - }; - - entry.Extents = new List<(uint extent, uint size)>(); - - if(record.size != 0) - entry.Extents.Add((record.extent, record.size)); - - if(entry.Flags.HasFlag(FileFlags.Directory) && _usePathTable) - { - entryOff += record.length; - - continue; - } - - if(!entries.ContainsKey(entry.Filename)) - entries.Add(entry.Filename, entry); - - entryOff += record.length; + originalNameWithVersion = originalName.ToLower(CultureInfo.CurrentUICulture); + originalName = originalNameWithVersion.Substring(0, originalName.Length - 2); } - - if(_useTransTbl) - DecodeTransTable(entries); - - return entries; - } - - Dictionary DecodeIsoDirectory(ulong start, uint size) - { - Dictionary entries = new(); - int entryOff = 0; - - byte[] data = ReadSingleExtent(size, (uint)start); - - while(entryOff + _directoryRecordSize < data.Length) + else { - DirectoryRecord record = - Marshal.ByteArrayToStructureLittleEndian(data, entryOff, _directoryRecordSize); - - if(record.length == 0) - { - // Skip to next sector - if(data.Length - (((entryOff / 2048) + 1) * 2048) > 0) - { - entryOff = ((entryOff / 2048) + 1) * 2048; - - continue; - } - - break; - } - - // Special entries for current and parent directories, skip them - if(record.name_len == 1) - if(data[entryOff + _directoryRecordSize] == 0 || - data[entryOff + _directoryRecordSize] == 1) - { - entryOff += record.length; - - continue; - } - - var entry = new DecodedDirectoryEntry - { - Size = record.size, - Flags = record.flags, - Filename = - _joliet ? Encoding.BigEndianUnicode.GetString(data, entryOff + _directoryRecordSize, - record.name_len) - : Encoding.GetString(data, entryOff + _directoryRecordSize, record.name_len), - FileUnitSize = record.file_unit_size, - Interleave = record.interleave, - VolumeSequenceNumber = record.volume_sequence_number, - Timestamp = DecodeIsoDateTime(record.date), - XattrLength = record.xattr_len - }; - - entry.Extents = new List<(uint extent, uint size)>(); - - if(record.size != 0) - entry.Extents.Add((record.extent, record.size)); - - if(entry.Flags.HasFlag(FileFlags.Directory) && _usePathTable) - { - entryOff += record.length; - - continue; - } - - // Mac OS can use slashes, we cannot - entry.Filename = entry.Filename.Replace('/', '\u2215'); - - // Tailing '.' is only allowed on RRIP. If present it will be recreated below with the alternate name - if(entry.Filename.EndsWith(".", StringComparison.Ordinal)) - entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 1); - - if(entry.Filename.EndsWith(".;1", StringComparison.Ordinal)) - entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 3) + ";1"; - - // This is a legal Joliet name, different from VMS version fields, but Nero MAX incorrectly creates these filenames - if(_joliet && entry.Filename.EndsWith(";1", StringComparison.Ordinal)) - entry.Filename = entry.Filename.Substring(0, entry.Filename.Length - 2); - - int systemAreaStart = entryOff + record.name_len + _directoryRecordSize; - int systemAreaLength = record.length - record.name_len - _directoryRecordSize; - - if(systemAreaStart % 2 != 0) - { - systemAreaStart++; - systemAreaLength--; - } - - DecodeSystemArea(data, systemAreaStart, systemAreaStart + systemAreaLength, ref entry, - out bool hasResourceFork); - - if(entry.Flags.HasFlag(FileFlags.Associated)) - { - if(entries.ContainsKey(entry.Filename)) - { - if(hasResourceFork) - { - entries[entry.Filename].ResourceFork.Size += entry.Size; - entries[entry.Filename].ResourceFork.Extents.Add(entry.Extents[0]); - } - else - { - entries[entry.Filename].AssociatedFile.Size += entry.Size; - entries[entry.Filename].AssociatedFile.Extents.Add(entry.Extents[0]); - } - } - else - { - entries[entry.Filename] = new DecodedDirectoryEntry - { - Size = 0, - Flags = record.flags ^ FileFlags.Associated, - FileUnitSize = 0, - Interleave = 0, - VolumeSequenceNumber = record.volume_sequence_number, - Filename = entry.Filename, - Timestamp = DecodeIsoDateTime(record.date), - XattrLength = 0 - }; - - if(hasResourceFork) - entries[entry.Filename].ResourceFork = entry; - else - entries[entry.Filename].AssociatedFile = entry; - } - } - else - { - if(entries.ContainsKey(entry.Filename)) - { - entries[entry.Filename].Size += entry.Size; - - // Can appear after an associated file - if(entries[entry.Filename].Extents is null) - { - entries[entry.Filename].Extents = new List<(uint extent, uint size)>(); - entries[entry.Filename].Flags = entry.Flags; - entries[entry.Filename].FileUnitSize = entry.FileUnitSize; - entries[entry.Filename].Interleave = entry.Interleave; - entries[entry.Filename].VolumeSequenceNumber = entry.VolumeSequenceNumber; - entries[entry.Filename].Filename = entry.Filename; - entries[entry.Filename].Timestamp = entry.Timestamp; - entries[entry.Filename].XattrLength = entry.XattrLength; - } - - if(entry.Extents?.Count > 0) - entries[entry.Filename].Extents.Add(entry.Extents[0]); - } - else - entries[entry.Filename] = entry; - } - - entryOff += record.length; + originalName = originalName.ToLower(CultureInfo.CurrentUICulture); + originalNameWithVersion = originalName + ";1"; } - if(_useTransTbl) - DecodeTransTable(entries); + // Pre-read next line + line = sr.ReadLine(); - // Relocated directories should be shown in correct place when using Rock Ridge namespace - return _namespace == Namespace.Rrip - ? entries.Where(e => !e.Value.RockRidgeRelocated).ToDictionary(x => x.Key, x => x.Value) - : entries; - } - - void DecodeTransTable(Dictionary entries) - { - KeyValuePair transTblEntry = + KeyValuePair originalEntry = entries.FirstOrDefault(e => !e.Value.Flags.HasFlag(FileFlags.Directory) && - (e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == "trans.tbl" || - e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == "trans.tbl;1")); + (e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == + originalName || + e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == + originalNameWithVersion)); - if(transTblEntry.Value == null) - return; - - byte[] transTbl = ReadWithExtents(0, (long)transTblEntry.Value.Size, transTblEntry.Value.Extents, - transTblEntry.Value.XA?.signature == XA_MAGIC && - transTblEntry.Value.XA?.attributes.HasFlag(XaAttributes.Interleaved) == - true, transTblEntry.Value.XA?.filenumber ?? 0); - - var mr = new MemoryStream(transTbl, 0, (int)transTblEntry.Value.Size, false); - var sr = new StreamReader(mr, Encoding); - - string line = sr.ReadLine(); - - while(line != null) - { - // Skip the type field and the first space - string cutLine = line.Substring(2); - int spaceIndex = cutLine.IndexOf(' '); - string originalName = cutLine.Substring(0, spaceIndex); - string originalNameWithVersion; - string newName = cutLine.Substring(spaceIndex + 1).TrimStart(); - - if(originalName.EndsWith(";1", StringComparison.Ordinal)) - { - originalNameWithVersion = originalName.ToLower(CultureInfo.CurrentUICulture); - originalName = originalNameWithVersion.Substring(0, originalName.Length - 2); - } - else - { - originalName = originalName.ToLower(CultureInfo.CurrentUICulture); - originalNameWithVersion = originalName + ";1"; - } - - // Pre-read next line - line = sr.ReadLine(); - - KeyValuePair originalEntry = - entries.FirstOrDefault(e => !e.Value.Flags.HasFlag(FileFlags.Directory) && - (e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == - originalName || - e.Value.Filename.ToLower(CultureInfo.CurrentUICulture) == - originalNameWithVersion)); - - originalEntry.Value.Filename = newName; - entries.Remove(originalEntry.Key); - entries[newName] = originalEntry.Value; - } - - entries.Remove(transTblEntry.Key); + originalEntry.Value.Filename = newName; + entries.Remove(originalEntry.Key); + entries[newName] = originalEntry.Value; } - void DecodeSystemArea(byte[] data, int start, int end, ref DecodedDirectoryEntry entry, - out bool hasResourceFork) + entries.Remove(transTblEntry.Key); + } + + void DecodeSystemArea(byte[] data, int start, int end, ref DecodedDirectoryEntry entry, + out bool hasResourceFork) + { + int systemAreaOff = start; + hasResourceFork = false; + bool continueSymlink = false; + bool continueSymlinkComponent = false; + AppleCommon.FInfo fInfo; + + while(systemAreaOff + 2 <= end) { - int systemAreaOff = start; - hasResourceFork = false; - bool continueSymlink = false; - bool continueSymlinkComponent = false; - AppleCommon.FInfo fInfo; + ushort systemAreaSignature = BigEndianBitConverter.ToUInt16(data, systemAreaOff); - while(systemAreaOff + 2 <= end) + if(BigEndianBitConverter.ToUInt16(data, systemAreaOff + 6) == XA_MAGIC) + systemAreaSignature = XA_MAGIC; + + switch(systemAreaSignature) { - ushort systemAreaSignature = BigEndianBitConverter.ToUInt16(data, systemAreaOff); + case APPLE_MAGIC: + byte appleLength = data[systemAreaOff + 2]; + var appleId = (AppleId)data[systemAreaOff + 3]; - if(BigEndianBitConverter.ToUInt16(data, systemAreaOff + 6) == XA_MAGIC) - systemAreaSignature = XA_MAGIC; + // Old AAIP + if(appleId == AppleId.ProDOS && + appleLength != 7) + goto case AAIP_MAGIC; - switch(systemAreaSignature) - { - case APPLE_MAGIC: - byte appleLength = data[systemAreaOff + 2]; - var appleId = (AppleId)data[systemAreaOff + 3]; + switch(appleId) + { + case AppleId.ProDOS: + AppleProDOSSystemUse appleProDosSystemUse = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); - // Old AAIP - if(appleId == AppleId.ProDOS && - appleLength != 7) - goto case AAIP_MAGIC; - - switch(appleId) - { - case AppleId.ProDOS: - AppleProDOSSystemUse appleProDosSystemUse = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); - - entry.AppleProDosType = appleProDosSystemUse.aux_type; - entry.AppleDosType = appleProDosSystemUse.type; - - break; - case AppleId.HFS: - AppleHFSSystemUse appleHfsSystemUse = - Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, - Marshal.SizeOf()); - - hasResourceFork = true; - - fInfo = new AppleCommon.FInfo(); - fInfo.fdCreator = appleHfsSystemUse.creator; - fInfo.fdFlags = appleHfsSystemUse.finder_flags; - fInfo.fdType = appleHfsSystemUse.type; - entry.FinderInfo = fInfo; - - break; - } - - systemAreaOff += appleLength; - - break; - case APPLE_MAGIC_OLD: - var appleOldId = (AppleOldId)data[systemAreaOff + 2]; - - switch(appleOldId) - { - case AppleOldId.ProDOS: - AppleProDOSOldSystemUse appleProDosOldSystemUse = - Marshal.ByteArrayToStructureLittleEndian(data, - systemAreaOff, Marshal.SizeOf()); - - entry.AppleProDosType = appleProDosOldSystemUse.aux_type; - entry.AppleDosType = appleProDosOldSystemUse.type; - - systemAreaOff += Marshal.SizeOf(); - - break; - case AppleOldId.TypeCreator: - case AppleOldId.TypeCreatorBundle: - AppleHFSTypeCreatorSystemUse appleHfsTypeCreatorSystemUse = - Marshal.ByteArrayToStructureBigEndian(data, - systemAreaOff, Marshal.SizeOf()); - - hasResourceFork = true; - - fInfo = new AppleCommon.FInfo(); - fInfo.fdCreator = appleHfsTypeCreatorSystemUse.creator; - fInfo.fdType = appleHfsTypeCreatorSystemUse.type; - entry.FinderInfo = fInfo; - - systemAreaOff += Marshal.SizeOf(); - - break; - case AppleOldId.TypeCreatorIcon: - case AppleOldId.TypeCreatorIconBundle: - AppleHFSIconSystemUse appleHfsIconSystemUse = - Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, - Marshal.SizeOf()); - - hasResourceFork = true; - - fInfo = new AppleCommon.FInfo(); - fInfo.fdCreator = appleHfsIconSystemUse.creator; - fInfo.fdType = appleHfsIconSystemUse.type; - entry.FinderInfo = fInfo; - entry.AppleIcon = appleHfsIconSystemUse.icon; - - systemAreaOff += Marshal.SizeOf(); - - break; - case AppleOldId.HFS: - AppleHFSOldSystemUse appleHfsSystemUse = - Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, - Marshal.SizeOf()); - - hasResourceFork = true; - - fInfo = new AppleCommon.FInfo(); - fInfo.fdCreator = appleHfsSystemUse.creator; - fInfo.fdFlags = (AppleCommon.FinderFlags)appleHfsSystemUse.finder_flags; - fInfo.fdType = appleHfsSystemUse.type; - entry.FinderInfo = fInfo; - - systemAreaOff += Marshal.SizeOf(); - - break; - default: - // Cannot continue as we don't know this structure size - systemAreaOff = end; - - break; - } - - break; - case XA_MAGIC: - entry.XA = Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, - Marshal.SizeOf()); - - systemAreaOff += Marshal.SizeOf(); - - break; - - // All of these follow the SUSP indication of 2 bytes for signature 1 byte for length - case AAIP_MAGIC: - case AMIGA_MAGIC: - AmigaEntry amiga = - Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, - Marshal.SizeOf()); - - int protectionLength = 0; - - if(amiga.flags.HasFlag(AmigaFlags.Protection)) - { - entry.AmigaProtection = - Marshal.ByteArrayToStructureBigEndian(data, - systemAreaOff + Marshal.SizeOf(), - Marshal.SizeOf()); - - protectionLength = Marshal.SizeOf(); - } - - if(amiga.flags.HasFlag(AmigaFlags.Comment)) - { - if(entry.AmigaComment is null) - entry.AmigaComment = Array.Empty(); - - byte[] newComment = new byte[entry.AmigaComment.Length + - data - [systemAreaOff + Marshal.SizeOf() + protectionLength] - - 1]; - - Array.Copy(entry.AmigaComment, 0, newComment, 0, entry.AmigaComment.Length); - - Array.Copy(data, systemAreaOff + Marshal.SizeOf() + protectionLength, - newComment, entry.AmigaComment.Length, - data[systemAreaOff + Marshal.SizeOf() + protectionLength] - 1); - - entry.AmigaComment = newComment; - } - - systemAreaOff += amiga.length; - - break; - - // This merely indicates the existence of RRIP extensions, we don't need it - case RRIP_MAGIC: - byte rripLength = data[systemAreaOff + 2]; - systemAreaOff += rripLength; - - break; - case RRIP_POSIX_ATTRIBUTES: - byte pxLength = data[systemAreaOff + 2]; - - if(pxLength == 36) - entry.PosixAttributesOld = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); - else if(pxLength >= 44) - entry.PosixAttributes = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); - - systemAreaOff += pxLength; - - break; - case RRIP_POSIX_DEV_NO: - byte pnLength = data[systemAreaOff + 2]; - - entry.PosixDeviceNumber = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); - - systemAreaOff += pnLength; - - break; - case RRIP_SYMLINK: - byte slLength = data[systemAreaOff + 2]; - - SymbolicLink sl = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); - - SymbolicLinkComponent slc = - Marshal.ByteArrayToStructureLittleEndian(data, - systemAreaOff + Marshal.SizeOf(), - Marshal.SizeOf()); - - if(!continueSymlink || - entry.SymbolicLink is null) - entry.SymbolicLink = ""; - - if(slc.flags.HasFlag(SymlinkComponentFlags.Root)) - entry.SymbolicLink = "/"; - - if(slc.flags.HasFlag(SymlinkComponentFlags.Current)) - entry.SymbolicLink += "."; - - if(slc.flags.HasFlag(SymlinkComponentFlags.Parent)) - entry.SymbolicLink += ".."; - - if(!continueSymlinkComponent && - !slc.flags.HasFlag(SymlinkComponentFlags.Root)) - entry.SymbolicLink += "/"; - - entry.SymbolicLink += slc.flags.HasFlag(SymlinkComponentFlags.Networkname) - ? Environment.MachineName - : _joliet - ? Encoding.BigEndianUnicode.GetString(data, - systemAreaOff + Marshal.SizeOf() + - Marshal.SizeOf(), slc.length) - : Encoding.GetString(data, - systemAreaOff + - Marshal.SizeOf() + - Marshal.SizeOf(), - slc.length); - - continueSymlink = sl.flags.HasFlag(SymlinkFlags.Continue); - continueSymlinkComponent = slc.flags.HasFlag(SymlinkComponentFlags.Continue); - - systemAreaOff += slLength; - - break; - case RRIP_NAME: - byte nmLength = data[systemAreaOff + 2]; - - if(_namespace != Namespace.Rrip) - { - systemAreaOff += nmLength; + entry.AppleProDosType = appleProDosSystemUse.aux_type; + entry.AppleDosType = appleProDosSystemUse.type; break; - } + case AppleId.HFS: + AppleHFSSystemUse appleHfsSystemUse = + Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, + Marshal.SizeOf()); - AlternateName alternateName = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); + hasResourceFork = true; - byte[] nm; + fInfo = new AppleCommon.FInfo(); + fInfo.fdCreator = appleHfsSystemUse.creator; + fInfo.fdFlags = appleHfsSystemUse.finder_flags; + fInfo.fdType = appleHfsSystemUse.type; + entry.FinderInfo = fInfo; - if(alternateName.flags.HasFlag(AlternateNameFlags.Networkname)) - nm = _joliet ? Encoding.BigEndianUnicode.GetBytes(Environment.MachineName) - : Encoding.GetBytes(Environment.MachineName); - else - { - nm = new byte[nmLength - Marshal.SizeOf()]; + break; + } - Array.Copy(data, systemAreaOff + Marshal.SizeOf(), nm, 0, nm.Length); - } + systemAreaOff += appleLength; - if(entry.RockRidgeAlternateName is null) - entry.RockRidgeAlternateName = Array.Empty(); + break; + case APPLE_MAGIC_OLD: + var appleOldId = (AppleOldId)data[systemAreaOff + 2]; - byte[] newNm = new byte[entry.RockRidgeAlternateName.Length + nm.Length]; - Array.Copy(entry.RockRidgeAlternateName, 0, newNm, 0, entry.RockRidgeAlternateName.Length); - Array.Copy(nm, 0, newNm, entry.RockRidgeAlternateName.Length, nm.Length); + switch(appleOldId) + { + case AppleOldId.ProDOS: + AppleProDOSOldSystemUse appleProDosOldSystemUse = + Marshal.ByteArrayToStructureLittleEndian(data, + systemAreaOff, Marshal.SizeOf()); - entry.RockRidgeAlternateName = newNm; + entry.AppleProDosType = appleProDosOldSystemUse.aux_type; + entry.AppleDosType = appleProDosOldSystemUse.type; - if(!alternateName.flags.HasFlag(AlternateNameFlags.Continue)) - { - entry.Filename = _joliet ? Encoding.BigEndianUnicode.GetString(entry.RockRidgeAlternateName) - : Encoding.GetString(entry.RockRidgeAlternateName); + systemAreaOff += Marshal.SizeOf(); - entry.RockRidgeAlternateName = null; - } + break; + case AppleOldId.TypeCreator: + case AppleOldId.TypeCreatorBundle: + AppleHFSTypeCreatorSystemUse appleHfsTypeCreatorSystemUse = + Marshal.ByteArrayToStructureBigEndian(data, + systemAreaOff, Marshal.SizeOf()); + hasResourceFork = true; + + fInfo = new AppleCommon.FInfo(); + fInfo.fdCreator = appleHfsTypeCreatorSystemUse.creator; + fInfo.fdType = appleHfsTypeCreatorSystemUse.type; + entry.FinderInfo = fInfo; + + systemAreaOff += Marshal.SizeOf(); + + break; + case AppleOldId.TypeCreatorIcon: + case AppleOldId.TypeCreatorIconBundle: + AppleHFSIconSystemUse appleHfsIconSystemUse = + Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, + Marshal.SizeOf()); + + hasResourceFork = true; + + fInfo = new AppleCommon.FInfo(); + fInfo.fdCreator = appleHfsIconSystemUse.creator; + fInfo.fdType = appleHfsIconSystemUse.type; + entry.FinderInfo = fInfo; + entry.AppleIcon = appleHfsIconSystemUse.icon; + + systemAreaOff += Marshal.SizeOf(); + + break; + case AppleOldId.HFS: + AppleHFSOldSystemUse appleHfsSystemUse = + Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, + Marshal.SizeOf()); + + hasResourceFork = true; + + fInfo = new AppleCommon.FInfo(); + fInfo.fdCreator = appleHfsSystemUse.creator; + fInfo.fdFlags = (AppleCommon.FinderFlags)appleHfsSystemUse.finder_flags; + fInfo.fdType = appleHfsSystemUse.type; + entry.FinderInfo = fInfo; + + systemAreaOff += Marshal.SizeOf(); + + break; + default: + // Cannot continue as we don't know this structure size + systemAreaOff = end; + + break; + } + + break; + case XA_MAGIC: + entry.XA = Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, + Marshal.SizeOf()); + + systemAreaOff += Marshal.SizeOf(); + + break; + + // All of these follow the SUSP indication of 2 bytes for signature 1 byte for length + case AAIP_MAGIC: + case AMIGA_MAGIC: + AmigaEntry amiga = + Marshal.ByteArrayToStructureBigEndian(data, systemAreaOff, + Marshal.SizeOf()); + + int protectionLength = 0; + + if(amiga.flags.HasFlag(AmigaFlags.Protection)) + { + entry.AmigaProtection = + Marshal.ByteArrayToStructureBigEndian(data, + systemAreaOff + Marshal.SizeOf(), + Marshal.SizeOf()); + + protectionLength = Marshal.SizeOf(); + } + + if(amiga.flags.HasFlag(AmigaFlags.Comment)) + { + if(entry.AmigaComment is null) + entry.AmigaComment = Array.Empty(); + + byte[] newComment = new byte[entry.AmigaComment.Length + + data + [systemAreaOff + Marshal.SizeOf() + protectionLength] - + 1]; + + Array.Copy(entry.AmigaComment, 0, newComment, 0, entry.AmigaComment.Length); + + Array.Copy(data, systemAreaOff + Marshal.SizeOf() + protectionLength, + newComment, entry.AmigaComment.Length, + data[systemAreaOff + Marshal.SizeOf() + protectionLength] - 1); + + entry.AmigaComment = newComment; + } + + systemAreaOff += amiga.length; + + break; + + // This merely indicates the existence of RRIP extensions, we don't need it + case RRIP_MAGIC: + byte rripLength = data[systemAreaOff + 2]; + systemAreaOff += rripLength; + + break; + case RRIP_POSIX_ATTRIBUTES: + byte pxLength = data[systemAreaOff + 2]; + + if(pxLength == 36) + entry.PosixAttributesOld = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); + else if(pxLength >= 44) + entry.PosixAttributes = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); + + systemAreaOff += pxLength; + + break; + case RRIP_POSIX_DEV_NO: + byte pnLength = data[systemAreaOff + 2]; + + entry.PosixDeviceNumber = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); + + systemAreaOff += pnLength; + + break; + case RRIP_SYMLINK: + byte slLength = data[systemAreaOff + 2]; + + SymbolicLink sl = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); + + SymbolicLinkComponent slc = + Marshal.ByteArrayToStructureLittleEndian(data, + systemAreaOff + Marshal.SizeOf(), + Marshal.SizeOf()); + + if(!continueSymlink || + entry.SymbolicLink is null) + entry.SymbolicLink = ""; + + if(slc.flags.HasFlag(SymlinkComponentFlags.Root)) + entry.SymbolicLink = "/"; + + if(slc.flags.HasFlag(SymlinkComponentFlags.Current)) + entry.SymbolicLink += "."; + + if(slc.flags.HasFlag(SymlinkComponentFlags.Parent)) + entry.SymbolicLink += ".."; + + if(!continueSymlinkComponent && + !slc.flags.HasFlag(SymlinkComponentFlags.Root)) + entry.SymbolicLink += "/"; + + entry.SymbolicLink += slc.flags.HasFlag(SymlinkComponentFlags.Networkname) + ? Environment.MachineName + : _joliet + ? Encoding.BigEndianUnicode.GetString(data, + systemAreaOff + Marshal.SizeOf() + + Marshal.SizeOf(), slc.length) + : Encoding.GetString(data, + systemAreaOff + + Marshal.SizeOf() + + Marshal.SizeOf(), + slc.length); + + continueSymlink = sl.flags.HasFlag(SymlinkFlags.Continue); + continueSymlinkComponent = slc.flags.HasFlag(SymlinkComponentFlags.Continue); + + systemAreaOff += slLength; + + break; + case RRIP_NAME: + byte nmLength = data[systemAreaOff + 2]; + + if(_namespace != Namespace.Rrip) + { systemAreaOff += nmLength; break; - case RRIP_CHILDLINK: - byte clLength = data[systemAreaOff + 2]; + } - // If we are not in Rock Ridge namespace, or we are using the Path Table, skip it - if(_namespace != Namespace.Rrip || _usePathTable) - { - systemAreaOff += clLength; + AlternateName alternateName = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); - break; - } + byte[] nm; - ChildLink cl = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); + if(alternateName.flags.HasFlag(AlternateNameFlags.Networkname)) + nm = _joliet ? Encoding.BigEndianUnicode.GetBytes(Environment.MachineName) + : Encoding.GetBytes(Environment.MachineName); + else + { + nm = new byte[nmLength - Marshal.SizeOf()]; - ErrorNumber errno = ReadSector(cl.child_dir_lba, out byte[] childSector); + Array.Copy(data, systemAreaOff + Marshal.SizeOf(), nm, 0, nm.Length); + } - DirectoryRecord childRecord = - Marshal.ByteArrayToStructureLittleEndian(childSector); + if(entry.RockRidgeAlternateName is null) + entry.RockRidgeAlternateName = Array.Empty(); - // As per RRIP 4.1.5.1, we leave name as in previous entry, substitute location with the one in - // the CL, and replace all other fields with the ones found in the first entry of the child - entry.Extents = new List<(uint extent, uint size)> - { - (cl.child_dir_lba, childRecord.size) - }; + byte[] newNm = new byte[entry.RockRidgeAlternateName.Length + nm.Length]; + Array.Copy(entry.RockRidgeAlternateName, 0, newNm, 0, entry.RockRidgeAlternateName.Length); + Array.Copy(nm, 0, newNm, entry.RockRidgeAlternateName.Length, nm.Length); - entry.Size = childRecord.size; - entry.Flags = childRecord.flags; - entry.FileUnitSize = childRecord.file_unit_size; - entry.Interleave = childRecord.interleave; - entry.VolumeSequenceNumber = childRecord.volume_sequence_number; - entry.Timestamp = DecodeIsoDateTime(childRecord.date); - entry.XattrLength = childRecord.xattr_len; + entry.RockRidgeAlternateName = newNm; + if(!alternateName.flags.HasFlag(AlternateNameFlags.Continue)) + { + entry.Filename = _joliet ? Encoding.BigEndianUnicode.GetString(entry.RockRidgeAlternateName) + : Encoding.GetString(entry.RockRidgeAlternateName); + + entry.RockRidgeAlternateName = null; + } + + systemAreaOff += nmLength; + + break; + case RRIP_CHILDLINK: + byte clLength = data[systemAreaOff + 2]; + + // If we are not in Rock Ridge namespace, or we are using the Path Table, skip it + if(_namespace != Namespace.Rrip || _usePathTable) + { systemAreaOff += clLength; break; - case RRIP_PARENTLINK: - // SKip, we don't need it - byte plLength = data[systemAreaOff + 2]; - systemAreaOff += plLength; + } - break; - case RRIP_RELOCATED_DIR: - byte reLength = data[systemAreaOff + 2]; - systemAreaOff += reLength; + ChildLink cl = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); - entry.RockRidgeRelocated = true; + ErrorNumber errno = ReadSector(cl.child_dir_lba, out byte[] childSector); - break; - case RRIP_TIMESTAMPS: - byte tfLength = data[systemAreaOff + 2]; + DirectoryRecord childRecord = + Marshal.ByteArrayToStructureLittleEndian(childSector); - Timestamps timestamps = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); + // As per RRIP 4.1.5.1, we leave name as in previous entry, substitute location with the one in + // the CL, and replace all other fields with the ones found in the first entry of the child + entry.Extents = new List<(uint extent, uint size)> + { + (cl.child_dir_lba, childRecord.size) + }; - int tfOff = systemAreaOff + Marshal.SizeOf(); - int tfLen = timestamps.flags.HasFlag(TimestampFlags.LongFormat) ? 17 : 7; + entry.Size = childRecord.size; + entry.Flags = childRecord.flags; + entry.FileUnitSize = childRecord.file_unit_size; + entry.Interleave = childRecord.interleave; + entry.VolumeSequenceNumber = childRecord.volume_sequence_number; + entry.Timestamp = DecodeIsoDateTime(childRecord.date); + entry.XattrLength = childRecord.xattr_len; - if(timestamps.flags.HasFlag(TimestampFlags.Creation)) - { - entry.RripCreation = new byte[tfLen]; - Array.Copy(data, tfOff, entry.RripCreation, 0, tfLen); - tfOff += tfLen; - } + systemAreaOff += clLength; - if(timestamps.flags.HasFlag(TimestampFlags.Modification)) - { - entry.RripModify = new byte[tfLen]; - Array.Copy(data, tfOff, entry.RripModify, 0, tfLen); - tfOff += tfLen; - } - - if(timestamps.flags.HasFlag(TimestampFlags.Access)) - { - entry.RripAccess = new byte[tfLen]; - Array.Copy(data, tfOff, entry.RripAccess, 0, tfLen); - tfOff += tfLen; - } - - if(timestamps.flags.HasFlag(TimestampFlags.AttributeChange)) - { - entry.RripAttributeChange = new byte[tfLen]; - Array.Copy(data, tfOff, entry.RripAttributeChange, 0, tfLen); - tfOff += tfLen; - } - - if(timestamps.flags.HasFlag(TimestampFlags.Backup)) - { - entry.RripBackup = new byte[tfLen]; - Array.Copy(data, tfOff, entry.RripBackup, 0, tfLen); - tfOff += tfLen; - } - - if(timestamps.flags.HasFlag(TimestampFlags.Expiration)) - { - entry.RripExpiration = new byte[tfLen]; - Array.Copy(data, tfOff, entry.RripExpiration, 0, tfLen); - tfOff += tfLen; - } - - if(timestamps.flags.HasFlag(TimestampFlags.Effective)) - { - entry.RripEffective = new byte[tfLen]; - Array.Copy(data, tfOff, entry.RripEffective, 0, tfLen); - } - - systemAreaOff += tfLength; - - break; - case RRIP_SPARSE: - // TODO - byte sfLength = data[systemAreaOff + 2]; - systemAreaOff += sfLength; - - break; - case SUSP_CONTINUATION: - byte ceLength = data[systemAreaOff + 2]; - - ContinuationArea ca = - Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, - Marshal.SizeOf()); - - byte[] caData = ReadSingleExtent(ca.offset, ca.ca_length, ca.block); - - DecodeSystemArea(caData, 0, (int)ca.ca_length, ref entry, out hasResourceFork); - - systemAreaOff += ceLength; - - break; - case SUSP_PADDING: - // Just padding, skip - byte pdLength = data[systemAreaOff + 2]; - systemAreaOff += pdLength; - - break; - case SUSP_INDICATOR: - // Only to be found on CURRENT entry of root directory - byte spLength = data[systemAreaOff + 2]; - systemAreaOff += spLength; - - break; - case SUSP_TERMINATOR: - // Not seen on the wild - byte stLength = data[systemAreaOff + 2]; - systemAreaOff += stLength; - - break; - case SUSP_REFERENCE: - // Only to be found on CURRENT entry of root directory - byte erLength = data[systemAreaOff + 2]; - systemAreaOff += erLength; - - break; - case SUSP_SELECTOR: - // Only to be found on CURRENT entry of root directory - byte esLength = data[systemAreaOff + 2]; - systemAreaOff += esLength; - - break; - case ZISO_MAGIC: - // TODO: Implement support for zisofs - byte zfLength = data[systemAreaOff + 2]; - systemAreaOff += zfLength; - - break; - default: - // Cannot continue as we don't know this structure size - systemAreaOff = end; - - break; - } - } - } - - PathTableEntryInternal[] GetPathTableEntries(string path) - { - IEnumerable tableEntries; - List pathTableList = new(_pathTable); - - if(path == "" || - path == "/") - tableEntries = _pathTable.Where(p => p.Parent == 1 && p != _pathTable[0]); - else - { - string cutPath = path.StartsWith("/", StringComparison.Ordinal) - ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) - : path.ToLower(CultureInfo.CurrentUICulture); - - string[] pieces = cutPath.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - int currentParent = 1; - int currentPiece = 0; - - while(currentPiece < pieces.Length) - { - PathTableEntryInternal currentEntry = _pathTable.FirstOrDefault(p => p.Parent == currentParent && - p.Name.ToLower(CultureInfo.CurrentUICulture) == pieces[currentPiece]); - - if(currentEntry is null) - break; - - currentPiece++; - currentParent = pathTableList.IndexOf(currentEntry) + 1; - } - - tableEntries = _pathTable.Where(p => p.Parent == currentParent); - } - - return tableEntries.ToArray(); - } - - DecodedDirectoryEntry[] GetSubdirsFromCdiPathTable(string path) - { - PathTableEntryInternal[] tableEntries = GetPathTableEntries(path); - List entries = new(); - - foreach(PathTableEntryInternal tEntry in tableEntries) - { - ErrorNumber errno = ReadSector(tEntry.Extent, out byte[] sector); - - CdiDirectoryRecord record = - Marshal.ByteArrayToStructureBigEndian(sector, tEntry.XattrLength, - _cdiDirectoryRecordSize); - - if(record.length == 0) break; + case RRIP_PARENTLINK: + // SKip, we don't need it + byte plLength = data[systemAreaOff + 2]; + systemAreaOff += plLength; - var entry = new DecodedDirectoryEntry - { - Size = record.size, - Filename = tEntry.Name, - VolumeSequenceNumber = record.volume_sequence_number, - Timestamp = DecodeHighSierraDateTime(record.date), - XattrLength = tEntry.XattrLength - }; - - entry.Extents = new List<(uint extent, uint size)>(); - - if(record.size != 0) - entry.Extents.Add((record.start_lbn, record.size)); - - if(record.flags.HasFlag(CdiFileFlags.Hidden)) - entry.Flags |= FileFlags.Hidden; - - int systemAreaStart = record.name_len + _cdiDirectoryRecordSize; - - if(systemAreaStart % 2 != 0) - systemAreaStart++; - - entry.CdiSystemArea = - Marshal.ByteArrayToStructureBigEndian(sector, systemAreaStart, _cdiSystemAreaSize); - - if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory)) - entry.Flags |= FileFlags.Directory; - - entries.Add(entry); - } - - return entries.ToArray(); - } - - DecodedDirectoryEntry[] GetSubdirsFromIsoPathTable(string path) - { - PathTableEntryInternal[] tableEntries = GetPathTableEntries(path); - List entries = new(); - - foreach(PathTableEntryInternal tEntry in tableEntries) - { - ErrorNumber errno = ReadSector(tEntry.Extent, out byte[] sector); - - DirectoryRecord record = - Marshal.ByteArrayToStructureLittleEndian(sector, tEntry.XattrLength, - _directoryRecordSize); - - if(record.length == 0) break; + case RRIP_RELOCATED_DIR: + byte reLength = data[systemAreaOff + 2]; + systemAreaOff += reLength; - var entry = new DecodedDirectoryEntry - { - Size = record.size, - Flags = record.flags, - Filename = tEntry.Name, - FileUnitSize = record.file_unit_size, - Interleave = record.interleave, - VolumeSequenceNumber = record.volume_sequence_number, - Timestamp = DecodeIsoDateTime(record.date), - XattrLength = tEntry.XattrLength - }; + entry.RockRidgeRelocated = true; - entry.Extents = new List<(uint extent, uint size)>(); + break; + case RRIP_TIMESTAMPS: + byte tfLength = data[systemAreaOff + 2]; - if(record.size != 0) - entry.Extents.Add((record.extent, record.size)); + Timestamps timestamps = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); - int systemAreaStart = record.name_len + _directoryRecordSize; - int systemAreaLength = record.length - record.name_len - _directoryRecordSize; + int tfOff = systemAreaOff + Marshal.SizeOf(); + int tfLen = timestamps.flags.HasFlag(TimestampFlags.LongFormat) ? 17 : 7; - if(systemAreaStart % 2 != 0) - { - systemAreaStart++; - systemAreaLength--; - } + if(timestamps.flags.HasFlag(TimestampFlags.Creation)) + { + entry.RripCreation = new byte[tfLen]; + Array.Copy(data, tfOff, entry.RripCreation, 0, tfLen); + tfOff += tfLen; + } - DecodeSystemArea(sector, systemAreaStart, systemAreaStart + systemAreaLength, ref entry, out _); + if(timestamps.flags.HasFlag(TimestampFlags.Modification)) + { + entry.RripModify = new byte[tfLen]; + Array.Copy(data, tfOff, entry.RripModify, 0, tfLen); + tfOff += tfLen; + } - entries.Add(entry); + if(timestamps.flags.HasFlag(TimestampFlags.Access)) + { + entry.RripAccess = new byte[tfLen]; + Array.Copy(data, tfOff, entry.RripAccess, 0, tfLen); + tfOff += tfLen; + } + + if(timestamps.flags.HasFlag(TimestampFlags.AttributeChange)) + { + entry.RripAttributeChange = new byte[tfLen]; + Array.Copy(data, tfOff, entry.RripAttributeChange, 0, tfLen); + tfOff += tfLen; + } + + if(timestamps.flags.HasFlag(TimestampFlags.Backup)) + { + entry.RripBackup = new byte[tfLen]; + Array.Copy(data, tfOff, entry.RripBackup, 0, tfLen); + tfOff += tfLen; + } + + if(timestamps.flags.HasFlag(TimestampFlags.Expiration)) + { + entry.RripExpiration = new byte[tfLen]; + Array.Copy(data, tfOff, entry.RripExpiration, 0, tfLen); + tfOff += tfLen; + } + + if(timestamps.flags.HasFlag(TimestampFlags.Effective)) + { + entry.RripEffective = new byte[tfLen]; + Array.Copy(data, tfOff, entry.RripEffective, 0, tfLen); + } + + systemAreaOff += tfLength; + + break; + case RRIP_SPARSE: + // TODO + byte sfLength = data[systemAreaOff + 2]; + systemAreaOff += sfLength; + + break; + case SUSP_CONTINUATION: + byte ceLength = data[systemAreaOff + 2]; + + ContinuationArea ca = + Marshal.ByteArrayToStructureLittleEndian(data, systemAreaOff, + Marshal.SizeOf()); + + byte[] caData = ReadSingleExtent(ca.offset, ca.ca_length, ca.block); + + DecodeSystemArea(caData, 0, (int)ca.ca_length, ref entry, out hasResourceFork); + + systemAreaOff += ceLength; + + break; + case SUSP_PADDING: + // Just padding, skip + byte pdLength = data[systemAreaOff + 2]; + systemAreaOff += pdLength; + + break; + case SUSP_INDICATOR: + // Only to be found on CURRENT entry of root directory + byte spLength = data[systemAreaOff + 2]; + systemAreaOff += spLength; + + break; + case SUSP_TERMINATOR: + // Not seen on the wild + byte stLength = data[systemAreaOff + 2]; + systemAreaOff += stLength; + + break; + case SUSP_REFERENCE: + // Only to be found on CURRENT entry of root directory + byte erLength = data[systemAreaOff + 2]; + systemAreaOff += erLength; + + break; + case SUSP_SELECTOR: + // Only to be found on CURRENT entry of root directory + byte esLength = data[systemAreaOff + 2]; + systemAreaOff += esLength; + + break; + case ZISO_MAGIC: + // TODO: Implement support for zisofs + byte zfLength = data[systemAreaOff + 2]; + systemAreaOff += zfLength; + + break; + default: + // Cannot continue as we don't know this structure size + systemAreaOff = end; + + break; } - - return entries.ToArray(); - } - - DecodedDirectoryEntry[] GetSubdirsFromHighSierraPathTable(string path) - { - PathTableEntryInternal[] tableEntries = GetPathTableEntries(path); - List entries = new(); - - foreach(PathTableEntryInternal tEntry in tableEntries) - { - ErrorNumber errno = ReadSector(tEntry.Extent, out byte[] sector); - - HighSierraDirectoryRecord record = - Marshal.ByteArrayToStructureLittleEndian(sector, tEntry.XattrLength, - _highSierraDirectoryRecordSize); - - var entry = new DecodedDirectoryEntry - { - Size = record.size, - Flags = record.flags, - Filename = tEntry.Name, - Interleave = record.interleave, - VolumeSequenceNumber = record.volume_sequence_number, - Timestamp = DecodeHighSierraDateTime(record.date), - XattrLength = tEntry.XattrLength - }; - - entry.Extents = new List<(uint extent, uint size)>(); - - if(record.size != 0) - entry.Extents.Add((record.extent, record.size)); - - entries.Add(entry); - } - - return entries.ToArray(); } } + + PathTableEntryInternal[] GetPathTableEntries(string path) + { + IEnumerable tableEntries; + List pathTableList = new(_pathTable); + + if(path == "" || + path == "/") + tableEntries = _pathTable.Where(p => p.Parent == 1 && p != _pathTable[0]); + else + { + string cutPath = path.StartsWith("/", StringComparison.Ordinal) + ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) + : path.ToLower(CultureInfo.CurrentUICulture); + + string[] pieces = cutPath.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + int currentParent = 1; + int currentPiece = 0; + + while(currentPiece < pieces.Length) + { + PathTableEntryInternal currentEntry = _pathTable.FirstOrDefault(p => p.Parent == currentParent && + p.Name.ToLower(CultureInfo.CurrentUICulture) == pieces[currentPiece]); + + if(currentEntry is null) + break; + + currentPiece++; + currentParent = pathTableList.IndexOf(currentEntry) + 1; + } + + tableEntries = _pathTable.Where(p => p.Parent == currentParent); + } + + return tableEntries.ToArray(); + } + + DecodedDirectoryEntry[] GetSubdirsFromCdiPathTable(string path) + { + PathTableEntryInternal[] tableEntries = GetPathTableEntries(path); + List entries = new(); + + foreach(PathTableEntryInternal tEntry in tableEntries) + { + ErrorNumber errno = ReadSector(tEntry.Extent, out byte[] sector); + + CdiDirectoryRecord record = + Marshal.ByteArrayToStructureBigEndian(sector, tEntry.XattrLength, + _cdiDirectoryRecordSize); + + if(record.length == 0) + break; + + var entry = new DecodedDirectoryEntry + { + Size = record.size, + Filename = tEntry.Name, + VolumeSequenceNumber = record.volume_sequence_number, + Timestamp = DecodeHighSierraDateTime(record.date), + XattrLength = tEntry.XattrLength + }; + + entry.Extents = new List<(uint extent, uint size)>(); + + if(record.size != 0) + entry.Extents.Add((record.start_lbn, record.size)); + + if(record.flags.HasFlag(CdiFileFlags.Hidden)) + entry.Flags |= FileFlags.Hidden; + + int systemAreaStart = record.name_len + _cdiDirectoryRecordSize; + + if(systemAreaStart % 2 != 0) + systemAreaStart++; + + entry.CdiSystemArea = + Marshal.ByteArrayToStructureBigEndian(sector, systemAreaStart, _cdiSystemAreaSize); + + if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.Directory)) + entry.Flags |= FileFlags.Directory; + + entries.Add(entry); + } + + return entries.ToArray(); + } + + DecodedDirectoryEntry[] GetSubdirsFromIsoPathTable(string path) + { + PathTableEntryInternal[] tableEntries = GetPathTableEntries(path); + List entries = new(); + + foreach(PathTableEntryInternal tEntry in tableEntries) + { + ErrorNumber errno = ReadSector(tEntry.Extent, out byte[] sector); + + DirectoryRecord record = + Marshal.ByteArrayToStructureLittleEndian(sector, tEntry.XattrLength, + _directoryRecordSize); + + if(record.length == 0) + break; + + var entry = new DecodedDirectoryEntry + { + Size = record.size, + Flags = record.flags, + Filename = tEntry.Name, + FileUnitSize = record.file_unit_size, + Interleave = record.interleave, + VolumeSequenceNumber = record.volume_sequence_number, + Timestamp = DecodeIsoDateTime(record.date), + XattrLength = tEntry.XattrLength + }; + + entry.Extents = new List<(uint extent, uint size)>(); + + if(record.size != 0) + entry.Extents.Add((record.extent, record.size)); + + int systemAreaStart = record.name_len + _directoryRecordSize; + int systemAreaLength = record.length - record.name_len - _directoryRecordSize; + + if(systemAreaStart % 2 != 0) + { + systemAreaStart++; + systemAreaLength--; + } + + DecodeSystemArea(sector, systemAreaStart, systemAreaStart + systemAreaLength, ref entry, out _); + + entries.Add(entry); + } + + return entries.ToArray(); + } + + DecodedDirectoryEntry[] GetSubdirsFromHighSierraPathTable(string path) + { + PathTableEntryInternal[] tableEntries = GetPathTableEntries(path); + List entries = new(); + + foreach(PathTableEntryInternal tEntry in tableEntries) + { + ErrorNumber errno = ReadSector(tEntry.Extent, out byte[] sector); + + HighSierraDirectoryRecord record = + Marshal.ByteArrayToStructureLittleEndian(sector, tEntry.XattrLength, + _highSierraDirectoryRecordSize); + + var entry = new DecodedDirectoryEntry + { + Size = record.size, + Flags = record.flags, + Filename = tEntry.Name, + Interleave = record.interleave, + VolumeSequenceNumber = record.volume_sequence_number, + Timestamp = DecodeHighSierraDateTime(record.date), + XattrLength = tEntry.XattrLength + }; + + entry.Extents = new List<(uint extent, uint size)>(); + + if(record.size != 0) + entry.Extents.Add((record.extent, record.size)); + + entries.Add(entry); + } + + return entries.ToArray(); + } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/File.cs b/Aaru.Filesystems/ISO9660/File.cs index 158afb994..d10caf081 100644 --- a/Aaru.Filesystems/ISO9660/File.cs +++ b/Aaru.Filesystems/ISO9660/File.cs @@ -43,561 +43,560 @@ using Aaru.Console; using Aaru.Helpers; using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) { - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + deviceBlock = 0; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + + if(err != ErrorNumber.NoError) + return err; + + if(entry.Flags.HasFlag(FileFlags.Directory) && + !_debug) + return ErrorNumber.IsDirectory; + + // TODO: Multi-extents + if(entry.Extents.Count > 1) + return ErrorNumber.NotImplemented; + + deviceBlock = entry.Extents[0].extent + fileBlock; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + { + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = Stat(path, out FileEntryInfo stat); + + if(err != ErrorNumber.NoError) + return err; + + attributes = stat.Attributes; + + return ErrorNumber.NoError; + } + + // TODO: Resolve symbolic link + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + buf = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + + if(err != ErrorNumber.NoError) + return err; + + if(entry.Flags.HasFlag(FileFlags.Directory) && + !_debug) + return ErrorNumber.IsDirectory; + + if(entry.Extents is null) + return ErrorNumber.InvalidArgument; + + if(entry.Size == 0) { - deviceBlock = 0; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); - - if(err != ErrorNumber.NoError) - return err; - - if(entry.Flags.HasFlag(FileFlags.Directory) && - !_debug) - return ErrorNumber.IsDirectory; - - // TODO: Multi-extents - if(entry.Extents.Count > 1) - return ErrorNumber.NotImplemented; - - deviceBlock = entry.Extents[0].extent + fileBlock; + buf = Array.Empty(); return ErrorNumber.NoError; } - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + if(offset >= (long)entry.Size) + return ErrorNumber.InvalidArgument; + + if(size + offset >= (long)entry.Size) + size = (long)entry.Size - offset; + + offset += entry.XattrLength * _blockSize; + + if(entry.CdiSystemArea?.attributes.HasFlag(CdiAttributes.DigitalAudio) == true && + entry.Extents.Count == 1) { - attributes = new FileAttributes(); - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = Stat(path, out FileEntryInfo stat); - - if(err != ErrorNumber.NoError) - return err; - - attributes = stat.Attributes; - - return ErrorNumber.NoError; - } - - // TODO: Resolve symbolic link - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) - { - buf = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); - - if(err != ErrorNumber.NoError) - return err; - - if(entry.Flags.HasFlag(FileFlags.Directory) && - !_debug) - return ErrorNumber.IsDirectory; - - if(entry.Extents is null) - return ErrorNumber.InvalidArgument; - - if(entry.Size == 0) + try { - buf = Array.Empty(); + long firstSector = offset / 2352; + long offsetInSector = offset % 2352; + long sizeInSectors = (size + offsetInSector) / 2352; + + if((size + offsetInSector) % 2352 > 0) + sizeInSectors++; + + ErrorNumber errno = _image.ReadSectorsLong((ulong)(entry.Extents[0].extent + firstSector), + (uint)sizeInSectors, out byte[] buffer); + + if(errno != ErrorNumber.NoError) + return errno; + + buf = new byte[size]; + Array.Copy(buffer, offsetInSector, buf, 0, size); return ErrorNumber.NoError; } - - if(offset >= (long)entry.Size) - return ErrorNumber.InvalidArgument; - - if(size + offset >= (long)entry.Size) - size = (long)entry.Size - offset; - - offset += entry.XattrLength * _blockSize; - - if(entry.CdiSystemArea?.attributes.HasFlag(CdiAttributes.DigitalAudio) == true && - entry.Extents.Count == 1) + catch(Exception e) { - try - { - long firstSector = offset / 2352; - long offsetInSector = offset % 2352; - long sizeInSectors = (size + offsetInSector) / 2352; + AaruConsole.DebugWriteLine("ISO9660 plugin", "Exception reading CD-i audio file"); + AaruConsole.DebugWriteLine("ISO9660 plugin", "{0}", e); - if((size + offsetInSector) % 2352 > 0) - sizeInSectors++; - - ErrorNumber errno = _image.ReadSectorsLong((ulong)(entry.Extents[0].extent + firstSector), - (uint)sizeInSectors, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - buf = new byte[size]; - Array.Copy(buffer, offsetInSector, buf, 0, size); - - return ErrorNumber.NoError; - } - catch(Exception e) - { - AaruConsole.DebugWriteLine("ISO9660 plugin", "Exception reading CD-i audio file"); - AaruConsole.DebugWriteLine("ISO9660 plugin", "{0}", e); - - return ErrorNumber.UnexpectedException; - } + return ErrorNumber.UnexpectedException; } - - buf = ReadWithExtents(offset, size, entry.Extents, - entry.XA?.signature == XA_MAGIC && - entry.XA?.attributes.HasFlag(XaAttributes.Interleaved) == true, - entry.XA?.filenumber ?? 0); - - return ErrorNumber.NoError; } - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) + buf = ReadWithExtents(offset, size, entry.Extents, + entry.XA?.signature == XA_MAGIC && + entry.XA?.attributes.HasFlag(XaAttributes.Interleaved) == true, + entry.XA?.filenumber ?? 0); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + + if(err != ErrorNumber.NoError) + return err; + + stat = new FileEntryInfo { - stat = null; + Attributes = new FileAttributes(), + Blocks = (long)(entry.Size / 2048), // TODO: XA + BlockSize = 2048, + Length = (long)entry.Size, + Links = 1, + LastWriteTimeUtc = entry.Timestamp + }; - if(!_mounted) - return ErrorNumber.AccessDenied; + if(entry.Extents?.Count > 0) + stat.Inode = entry.Extents[0].extent; - ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + if(entry.Size % 2048 > 0) + stat.Blocks++; - if(err != ErrorNumber.NoError) - return err; + if(entry.Flags.HasFlag(FileFlags.Directory)) + stat.Attributes |= FileAttributes.Directory; - stat = new FileEntryInfo - { - Attributes = new FileAttributes(), - Blocks = (long)(entry.Size / 2048), // TODO: XA - BlockSize = 2048, - Length = (long)entry.Size, - Links = 1, - LastWriteTimeUtc = entry.Timestamp - }; + if(entry.Flags.HasFlag(FileFlags.Hidden)) + stat.Attributes |= FileAttributes.Hidden; - if(entry.Extents?.Count > 0) - stat.Inode = entry.Extents[0].extent; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsAlias) == true) + stat.Attributes |= FileAttributes.Alias; - if(entry.Size % 2048 > 0) - stat.Blocks++; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsInvisible) == true) + stat.Attributes |= FileAttributes.Hidden; - if(entry.Flags.HasFlag(FileFlags.Directory)) - stat.Attributes |= FileAttributes.Directory; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBeenInited) == true) + stat.Attributes |= FileAttributes.HasBeenInited; - if(entry.Flags.HasFlag(FileFlags.Hidden)) - stat.Attributes |= FileAttributes.Hidden; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasCustomIcon) == true) + stat.Attributes |= FileAttributes.HasCustomIcon; - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsAlias) == true) - stat.Attributes |= FileAttributes.Alias; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasNoINITs) == true) + stat.Attributes |= FileAttributes.HasNoINITs; - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsInvisible) == true) - stat.Attributes |= FileAttributes.Hidden; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsOnDesk) == true) + stat.Attributes |= FileAttributes.IsOnDesk; - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBeenInited) == true) - stat.Attributes |= FileAttributes.HasBeenInited; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsShared) == true) + stat.Attributes |= FileAttributes.Shared; - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasCustomIcon) == true) - stat.Attributes |= FileAttributes.HasCustomIcon; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsStationery) == true) + stat.Attributes |= FileAttributes.Stationery; - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasNoINITs) == true) - stat.Attributes |= FileAttributes.HasNoINITs; + if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBundle) == true) + stat.Attributes |= FileAttributes.Bundle; - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsOnDesk) == true) - stat.Attributes |= FileAttributes.IsOnDesk; + if(entry.AppleIcon != null) + stat.Attributes |= FileAttributes.HasCustomIcon; - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsShared) == true) - stat.Attributes |= FileAttributes.Shared; - - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kIsStationery) == true) - stat.Attributes |= FileAttributes.Stationery; - - if(entry.FinderInfo?.fdFlags.HasFlag(AppleCommon.FinderFlags.kHasBundle) == true) - stat.Attributes |= FileAttributes.Bundle; - - if(entry.AppleIcon != null) - stat.Attributes |= FileAttributes.HasCustomIcon; - - if(entry.XA != null) - { - if(entry.XA.Value.attributes.HasFlag(XaAttributes.GroupExecute)) - stat.Mode |= 8; - - if(entry.XA.Value.attributes.HasFlag(XaAttributes.GroupRead)) - stat.Mode |= 32; - - if(entry.XA.Value.attributes.HasFlag(XaAttributes.OwnerExecute)) - stat.Mode |= 64; - - if(entry.XA.Value.attributes.HasFlag(XaAttributes.OwnerRead)) - stat.Mode |= 256; - - if(entry.XA.Value.attributes.HasFlag(XaAttributes.SystemExecute)) - stat.Mode |= 1; - - if(entry.XA.Value.attributes.HasFlag(XaAttributes.SystemRead)) - stat.Mode |= 4; - - stat.UID = entry.XA.Value.user; - stat.GID = entry.XA.Value.group; - stat.Inode = entry.XA.Value.filenumber; - } - - if(entry.PosixAttributes != null) - { - stat.Mode = (uint?)entry.PosixAttributes.Value.st_mode & 0x0FFF; - - if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Block)) - stat.Attributes |= FileAttributes.BlockDevice; - - if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Character)) - stat.Attributes |= FileAttributes.CharDevice; - - if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Pipe)) - stat.Attributes |= FileAttributes.Pipe; - - if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Socket)) - stat.Attributes |= FileAttributes.Socket; - - if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Symlink)) - stat.Attributes |= FileAttributes.Symlink; - - stat.Links = entry.PosixAttributes.Value.st_nlink; - stat.UID = entry.PosixAttributes.Value.st_uid; - stat.GID = entry.PosixAttributes.Value.st_gid; - stat.Inode = entry.PosixAttributes.Value.st_ino; - } - else if(entry.PosixAttributesOld != null) - { - stat.Mode = (uint?)entry.PosixAttributesOld.Value.st_mode & 0x0FFF; - - if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Block)) - stat.Attributes |= FileAttributes.BlockDevice; - - if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Character)) - stat.Attributes |= FileAttributes.CharDevice; - - if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Pipe)) - stat.Attributes |= FileAttributes.Pipe; - - if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Socket)) - stat.Attributes |= FileAttributes.Socket; - - if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Symlink)) - stat.Attributes |= FileAttributes.Symlink; - - stat.Links = entry.PosixAttributesOld.Value.st_nlink; - stat.UID = entry.PosixAttributesOld.Value.st_uid; - stat.GID = entry.PosixAttributesOld.Value.st_gid; - } - - if(entry.AmigaProtection != null) - { - if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupExec)) - stat.Mode |= 8; - - if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupRead)) - stat.Mode |= 32; - - if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupWrite)) - stat.Mode |= 16; - - if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherExec)) - stat.Mode |= 1; - - if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherRead)) - stat.Mode |= 4; - - if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherWrite)) - stat.Mode |= 2; - - if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerExec)) - stat.Mode |= 64; - - if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerRead)) - stat.Mode |= 256; - - if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerWrite)) - stat.Mode |= 128; - - if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.Archive)) - stat.Attributes |= FileAttributes.Archive; - } - - if(entry.PosixDeviceNumber != null) - stat.DeviceNo = ((ulong)entry.PosixDeviceNumber.Value.dev_t_high << 32) + - entry.PosixDeviceNumber.Value.dev_t_low; - - if(entry.RripModify != null) - stat.LastWriteTimeUtc = DecodeIsoDateTime(entry.RripModify); - - if(entry.RripAccess != null) - stat.AccessTimeUtc = DecodeIsoDateTime(entry.RripAccess); - - if(entry.RripAttributeChange != null) - stat.StatusChangeTimeUtc = DecodeIsoDateTime(entry.RripAttributeChange); - - if(entry.RripBackup != null) - stat.BackupTimeUtc = DecodeIsoDateTime(entry.RripBackup); - - if(entry.SymbolicLink != null) - stat.Attributes |= FileAttributes.Symlink; - - if(entry.XattrLength == 0 || - _cdi || - _highSierra) - return ErrorNumber.NoError; - - if(entry.CdiSystemArea != null) - { - stat.UID = entry.CdiSystemArea.Value.owner; - stat.GID = entry.CdiSystemArea.Value.group; - - if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.GroupExecute)) - stat.Mode |= 8; - - if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.GroupRead)) - stat.Mode |= 32; - - if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OtherExecute)) - stat.Mode |= 1; - - if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OtherRead)) - stat.Mode |= 4; - - if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OwnerExecute)) - stat.Mode |= 64; - - if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OwnerRead)) - stat.Mode |= 256; - } - - byte[] ea = ReadSingleExtent(entry.XattrLength * _blockSize, entry.Extents[0].extent); - - ExtendedAttributeRecord ear = Marshal.ByteArrayToStructureLittleEndian(ea); - - stat.UID = ear.owner; - stat.GID = ear.group; - - stat.Mode = 0; - - if(ear.permissions.HasFlag(Permissions.GroupExecute)) + if(entry.XA != null) + { + if(entry.XA.Value.attributes.HasFlag(XaAttributes.GroupExecute)) stat.Mode |= 8; - if(ear.permissions.HasFlag(Permissions.GroupRead)) + if(entry.XA.Value.attributes.HasFlag(XaAttributes.GroupRead)) stat.Mode |= 32; - if(ear.permissions.HasFlag(Permissions.OwnerExecute)) + if(entry.XA.Value.attributes.HasFlag(XaAttributes.OwnerExecute)) stat.Mode |= 64; - if(ear.permissions.HasFlag(Permissions.OwnerRead)) + if(entry.XA.Value.attributes.HasFlag(XaAttributes.OwnerRead)) stat.Mode |= 256; - if(ear.permissions.HasFlag(Permissions.OtherExecute)) + if(entry.XA.Value.attributes.HasFlag(XaAttributes.SystemExecute)) stat.Mode |= 1; - if(ear.permissions.HasFlag(Permissions.OtherRead)) + if(entry.XA.Value.attributes.HasFlag(XaAttributes.SystemRead)) stat.Mode |= 4; - stat.CreationTimeUtc = DateHandlers.Iso9660ToDateTime(ear.creation_date); - stat.LastWriteTimeUtc = DateHandlers.Iso9660ToDateTime(ear.modification_date); - - return ErrorNumber.NoError; + stat.UID = entry.XA.Value.user; + stat.GID = entry.XA.Value.group; + stat.Inode = entry.XA.Value.filenumber; } - /// - public ErrorNumber ReadLink(string path, out string dest) + if(entry.PosixAttributes != null) { - dest = null; + stat.Mode = (uint?)entry.PosixAttributes.Value.st_mode & 0x0FFF; - ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Block)) + stat.Attributes |= FileAttributes.BlockDevice; + + if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Character)) + stat.Attributes |= FileAttributes.CharDevice; + + if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Pipe)) + stat.Attributes |= FileAttributes.Pipe; + + if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Socket)) + stat.Attributes |= FileAttributes.Socket; + + if(entry.PosixAttributes.Value.st_mode.HasFlag(PosixMode.Symlink)) + stat.Attributes |= FileAttributes.Symlink; + + stat.Links = entry.PosixAttributes.Value.st_nlink; + stat.UID = entry.PosixAttributes.Value.st_uid; + stat.GID = entry.PosixAttributes.Value.st_gid; + stat.Inode = entry.PosixAttributes.Value.st_ino; + } + else if(entry.PosixAttributesOld != null) + { + stat.Mode = (uint?)entry.PosixAttributesOld.Value.st_mode & 0x0FFF; + + if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Block)) + stat.Attributes |= FileAttributes.BlockDevice; + + if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Character)) + stat.Attributes |= FileAttributes.CharDevice; + + if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Pipe)) + stat.Attributes |= FileAttributes.Pipe; + + if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Socket)) + stat.Attributes |= FileAttributes.Socket; + + if(entry.PosixAttributesOld.Value.st_mode.HasFlag(PosixMode.Symlink)) + stat.Attributes |= FileAttributes.Symlink; + + stat.Links = entry.PosixAttributesOld.Value.st_nlink; + stat.UID = entry.PosixAttributesOld.Value.st_uid; + stat.GID = entry.PosixAttributesOld.Value.st_gid; + } + + if(entry.AmigaProtection != null) + { + if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupExec)) + stat.Mode |= 8; + + if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupRead)) + stat.Mode |= 32; + + if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.GroupWrite)) + stat.Mode |= 16; + + if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherExec)) + stat.Mode |= 1; + + if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherRead)) + stat.Mode |= 4; + + if(entry.AmigaProtection.Value.Multiuser.HasFlag(AmigaMultiuser.OtherWrite)) + stat.Mode |= 2; + + if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerExec)) + stat.Mode |= 64; + + if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerRead)) + stat.Mode |= 256; + + if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.OwnerWrite)) + stat.Mode |= 128; + + if(entry.AmigaProtection.Value.Protection.HasFlag(AmigaAttributes.Archive)) + stat.Attributes |= FileAttributes.Archive; + } + + if(entry.PosixDeviceNumber != null) + stat.DeviceNo = ((ulong)entry.PosixDeviceNumber.Value.dev_t_high << 32) + + entry.PosixDeviceNumber.Value.dev_t_low; + + if(entry.RripModify != null) + stat.LastWriteTimeUtc = DecodeIsoDateTime(entry.RripModify); + + if(entry.RripAccess != null) + stat.AccessTimeUtc = DecodeIsoDateTime(entry.RripAccess); + + if(entry.RripAttributeChange != null) + stat.StatusChangeTimeUtc = DecodeIsoDateTime(entry.RripAttributeChange); + + if(entry.RripBackup != null) + stat.BackupTimeUtc = DecodeIsoDateTime(entry.RripBackup); + + if(entry.SymbolicLink != null) + stat.Attributes |= FileAttributes.Symlink; + + if(entry.XattrLength == 0 || + _cdi || + _highSierra) + return ErrorNumber.NoError; + + if(entry.CdiSystemArea != null) + { + stat.UID = entry.CdiSystemArea.Value.owner; + stat.GID = entry.CdiSystemArea.Value.group; + + if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.GroupExecute)) + stat.Mode |= 8; + + if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.GroupRead)) + stat.Mode |= 32; + + if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OtherExecute)) + stat.Mode |= 1; + + if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OtherRead)) + stat.Mode |= 4; + + if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OwnerExecute)) + stat.Mode |= 64; + + if(entry.CdiSystemArea.Value.attributes.HasFlag(CdiAttributes.OwnerRead)) + stat.Mode |= 256; + } + + byte[] ea = ReadSingleExtent(entry.XattrLength * _blockSize, entry.Extents[0].extent); + + ExtendedAttributeRecord ear = Marshal.ByteArrayToStructureLittleEndian(ea); + + stat.UID = ear.owner; + stat.GID = ear.group; + + stat.Mode = 0; + + if(ear.permissions.HasFlag(Permissions.GroupExecute)) + stat.Mode |= 8; + + if(ear.permissions.HasFlag(Permissions.GroupRead)) + stat.Mode |= 32; + + if(ear.permissions.HasFlag(Permissions.OwnerExecute)) + stat.Mode |= 64; + + if(ear.permissions.HasFlag(Permissions.OwnerRead)) + stat.Mode |= 256; + + if(ear.permissions.HasFlag(Permissions.OtherExecute)) + stat.Mode |= 1; + + if(ear.permissions.HasFlag(Permissions.OtherRead)) + stat.Mode |= 4; + + stat.CreationTimeUtc = DateHandlers.Iso9660ToDateTime(ear.creation_date); + stat.LastWriteTimeUtc = DateHandlers.Iso9660ToDateTime(ear.modification_date); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber ReadLink(string path, out string dest) + { + dest = null; + + ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + + if(err != ErrorNumber.NoError) + return err; + + if(entry.SymbolicLink is null) + return ErrorNumber.InvalidArgument; + + dest = entry.SymbolicLink; + + return ErrorNumber.NoError; + } + + ErrorNumber GetFileEntry(string path, out DecodedDirectoryEntry entry) + { + entry = null; + + string cutPath = path.StartsWith("/", StringComparison.Ordinal) + ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) + : path.ToLower(CultureInfo.CurrentUICulture); + + string[] pieces = cutPath.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + if(pieces.Length == 0) + return ErrorNumber.InvalidArgument; + + string parentPath = string.Join("/", pieces, 0, pieces.Length - 1); + + if(!_directoryCache.TryGetValue(parentPath, out _)) + { + ErrorNumber err = ReadDir(parentPath, out _); if(err != ErrorNumber.NoError) return err; - - if(entry.SymbolicLink is null) - return ErrorNumber.InvalidArgument; - - dest = entry.SymbolicLink; - - return ErrorNumber.NoError; } - ErrorNumber GetFileEntry(string path, out DecodedDirectoryEntry entry) + Dictionary parent; + + if(pieces.Length == 1) + parent = _rootDirectoryCache; + else if(!_directoryCache.TryGetValue(parentPath, out parent)) + return ErrorNumber.InvalidArgument; + + KeyValuePair dirent = + parent.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[^1]); + + if(string.IsNullOrEmpty(dirent.Key)) { - entry = null; - - string cutPath = path.StartsWith("/", StringComparison.Ordinal) - ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) - : path.ToLower(CultureInfo.CurrentUICulture); - - string[] pieces = cutPath.Split(new[] + if(!_joliet && + !pieces[^1].EndsWith(";1", StringComparison.Ordinal)) { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + dirent = parent.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == + pieces[^1] + ";1"); - if(pieces.Length == 0) - return ErrorNumber.InvalidArgument; - - string parentPath = string.Join("/", pieces, 0, pieces.Length - 1); - - if(!_directoryCache.TryGetValue(parentPath, out _)) - { - ErrorNumber err = ReadDir(parentPath, out _); - - if(err != ErrorNumber.NoError) - return err; - } - - Dictionary parent; - - if(pieces.Length == 1) - parent = _rootDirectoryCache; - else if(!_directoryCache.TryGetValue(parentPath, out parent)) - return ErrorNumber.InvalidArgument; - - KeyValuePair dirent = - parent.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[^1]); - - if(string.IsNullOrEmpty(dirent.Key)) - { - if(!_joliet && - !pieces[^1].EndsWith(";1", StringComparison.Ordinal)) - { - dirent = parent.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == - pieces[^1] + ";1"); - - if(string.IsNullOrEmpty(dirent.Key)) - return ErrorNumber.NoSuchFile; - } - else + if(string.IsNullOrEmpty(dirent.Key)) return ErrorNumber.NoSuchFile; } - - entry = dirent.Value; - - return ErrorNumber.NoError; + else + return ErrorNumber.NoSuchFile; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - byte[] ReadSingleExtent(long size, uint startingSector, bool interleaved = false, byte fileNumber = 0) => - ReadWithExtents(0, size, new List<(uint extent, uint size)> - { - (startingSector, (uint)size) - }, interleaved, fileNumber); + entry = dirent.Value; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - byte[] ReadSingleExtent(long offset, long size, uint startingSector, bool interleaved = false, - byte fileNumber = 0) => ReadWithExtents(offset, size, new List<(uint extent, uint size)> + return ErrorNumber.NoError; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + byte[] ReadSingleExtent(long size, uint startingSector, bool interleaved = false, byte fileNumber = 0) => + ReadWithExtents(0, size, new List<(uint extent, uint size)> { (startingSector, (uint)size) }, interleaved, fileNumber); - // Cannot think how to make this faster, as we don't know the mode sector until it is read, but we have size in bytes - byte[] ReadWithExtents(long offset, long size, List<(uint extent, uint size)> extents, bool interleaved, - byte fileNumber) - { - var ms = new MemoryStream(); - long currentFilePos = 0; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + byte[] ReadSingleExtent(long offset, long size, uint startingSector, bool interleaved = false, + byte fileNumber = 0) => ReadWithExtents(offset, size, new List<(uint extent, uint size)> + { + (startingSector, (uint)size) + }, interleaved, fileNumber); - for(int i = 0; i < extents.Count; i++) + // Cannot think how to make this faster, as we don't know the mode sector until it is read, but we have size in bytes + byte[] ReadWithExtents(long offset, long size, List<(uint extent, uint size)> extents, bool interleaved, + byte fileNumber) + { + var ms = new MemoryStream(); + long currentFilePos = 0; + + for(int i = 0; i < extents.Count; i++) + { + if(offset - currentFilePos >= extents[i].size) { - if(offset - currentFilePos >= extents[i].size) + currentFilePos += extents[i].size; + + continue; + } + + long leftExtentSize = extents[i].size; + uint currentExtentSector = 0; + + while(leftExtentSize > 0) + { + ErrorNumber errno = ReadSector(extents[i].extent + currentExtentSector, out byte[] sector, + interleaved, fileNumber); + + if(errno != ErrorNumber.NoError || + sector is null) { - currentFilePos += extents[i].size; + currentExtentSector++; continue; } - long leftExtentSize = extents[i].size; - uint currentExtentSector = 0; - - while(leftExtentSize > 0) + if(offset - currentFilePos > sector.Length) { - ErrorNumber errno = ReadSector(extents[i].extent + currentExtentSector, out byte[] sector, - interleaved, fileNumber); - - if(errno != ErrorNumber.NoError || - sector is null) - { - currentExtentSector++; - - continue; - } - - if(offset - currentFilePos > sector.Length) - { - currentExtentSector++; - leftExtentSize -= sector.Length; - currentFilePos += sector.Length; - - continue; - } - - if(offset - currentFilePos > 0) - ms.Write(sector, (int)(offset - currentFilePos), - (int)(sector.Length - (offset - currentFilePos))); - else - ms.Write(sector, 0, sector.Length); - currentExtentSector++; leftExtentSize -= sector.Length; currentFilePos += sector.Length; - if(ms.Length >= size) - break; + continue; } + if(offset - currentFilePos > 0) + ms.Write(sector, (int)(offset - currentFilePos), + (int)(sector.Length - (offset - currentFilePos))); + else + ms.Write(sector, 0, sector.Length); + + currentExtentSector++; + leftExtentSize -= sector.Length; + currentFilePos += sector.Length; + if(ms.Length >= size) break; } if(ms.Length >= size) - ms.SetLength(size); - - return ms.ToArray(); + break; } - byte[] ReadSubheaderWithExtents(List<(uint extent, uint size)> extents, bool copy) + if(ms.Length >= size) + ms.SetLength(size); + + return ms.ToArray(); + } + + byte[] ReadSubheaderWithExtents(List<(uint extent, uint size)> extents, bool copy) + { + var ms = new MemoryStream(); + + for(int i = 0; i < extents.Count; i++) { - var ms = new MemoryStream(); + long leftExtentSize = extents[i].size; + uint currentExtentSector = 0; - for(int i = 0; i < extents.Count; i++) + while(leftExtentSize > 0) { - long leftExtentSize = extents[i].size; - uint currentExtentSector = 0; + ErrorNumber errno = + _image.ReadSectorTag((extents[i].extent + currentExtentSector) * _blockSize / 2048, + SectorTagType.CdSectorSubHeader, out byte[] fullSector); - while(leftExtentSize > 0) - { - ErrorNumber errno = - _image.ReadSectorTag((extents[i].extent + currentExtentSector) * _blockSize / 2048, - SectorTagType.CdSectorSubHeader, out byte[] fullSector); + if(errno != ErrorNumber.NoError) + return null; - if(errno != ErrorNumber.NoError) - return null; + ms.Write(fullSector, copy ? 0 : 4, 4); - ms.Write(fullSector, copy ? 0 : 4, 4); - - currentExtentSector++; - leftExtentSize -= 2048; - } + currentExtentSector++; + leftExtentSize -= 2048; } - - return ms.ToArray(); } + + return ms.ToArray(); } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/ISO9660.cs b/Aaru.Filesystems/ISO9660/ISO9660.cs index 7484ca5bb..c53680b94 100644 --- a/Aaru.Filesystems/ISO9660/ISO9660.cs +++ b/Aaru.Filesystems/ISO9660/ISO9660.cs @@ -39,75 +39,74 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// This is coded following ECMA-119. +/// +/// Implements the High Sierra, ISO9660 and CD-i filesystems +[SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed partial class ISO9660 : IReadOnlyFilesystem { - // This is coded following ECMA-119. + bool _cdi; + bool _debug; + bool _highSierra; + IMediaImage _image; + bool _joliet; + bool _mounted; + Namespace _namespace; + PathTableEntryInternal[] _pathTable; + Dictionary _rootDirectoryCache; + FileSystemInfo _statfs; + bool _useEvd; + bool _usePathTable; + bool _useTransTbl; + ushort _blockSize; + /// - /// Implements the High Sierra, ISO9660 and CD-i filesystems - [SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed partial class ISO9660 : IReadOnlyFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "ISO9660 Filesystem"; + /// + public Guid Id => new("d812f4d3-c357-400d-90fd-3b22ef786aa8"); + /// + public string Author => "Natalia Portillo"; + + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + { + ("use_path_table", typeof(bool), "Use path table for directory traversal"), + ("use_trans_tbl", typeof(bool), "Use TRANS.TBL for filenames"), + ("use_evd", typeof(bool), + "If present, use Enhanced Volume Descriptor with specified encoding (overrides namespace)") + }; + + /// + public Dictionary Namespaces => new() { - bool _cdi; - bool _debug; - bool _highSierra; - IMediaImage _image; - bool _joliet; - bool _mounted; - Namespace _namespace; - PathTableEntryInternal[] _pathTable; - Dictionary _rootDirectoryCache; - FileSystemInfo _statfs; - bool _useEvd; - bool _usePathTable; - bool _useTransTbl; - ushort _blockSize; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "ISO9660 Filesystem"; - /// - public Guid Id => new("d812f4d3-c357-400d-90fd-3b22ef786aa8"); - /// - public string Author => "Natalia Portillo"; - - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - { - ("use_path_table", typeof(bool), "Use path table for directory traversal"), - ("use_trans_tbl", typeof(bool), "Use TRANS.TBL for filenames"), - ("use_evd", typeof(bool), - "If present, use Enhanced Volume Descriptor with specified encoding (overrides namespace)") - }; - - /// - public Dictionary Namespaces => new() { - { - "normal", "Primary Volume Descriptor, ignoring ;1 suffixes" - }, - { - "vms", "Primary Volume Descriptor, showing version suffixes" - }, - { - "joliet", "Joliet Volume Descriptor (default)" - }, - { - "rrip", "Rock Ridge" - }, - { - "romeo", "Primary Volume Descriptor using the specified encoding codepage" - } - }; - - static Dictionary GetDefaultOptions() => new() + "normal", "Primary Volume Descriptor, ignoring ;1 suffixes" + }, { - { - "debug", false.ToString() - } - }; - } + "vms", "Primary Volume Descriptor, showing version suffixes" + }, + { + "joliet", "Joliet Volume Descriptor (default)" + }, + { + "rrip", "Rock Ridge" + }, + { + "romeo", "Primary Volume Descriptor using the specified encoding codepage" + } + }; + + static Dictionary GetDefaultOptions() => new() + { + { + "debug", false.ToString() + } + }; } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Info.cs b/Aaru.Filesystems/ISO9660/Info.cs index fc55ed638..8001d4837 100644 --- a/Aaru.Filesystems/ISO9660/Info.cs +++ b/Aaru.Filesystems/ISO9660/Info.cs @@ -43,972 +43,971 @@ using Aaru.Decoders.Sega; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + // ISO9660 is designed for 2048 bytes/sector devices + if(imagePlugin.Info.SectorSize < 2048) + return false; + + // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. + if(partition.End <= 16 + partition.Start) + return false; + + // Read to Volume Descriptor + ErrorNumber errno = imagePlugin.ReadSector(16 + partition.Start, out byte[] vdSector); + + if(errno != ErrorNumber.NoError) + return false; + + int xaOff = 0; + + if(vdSector.Length == 2336) + xaOff = 8; + + byte vdType = vdSector[0 + xaOff]; + byte[] vdMagic = new byte[5]; + byte[] hsMagic = new byte[5]; + + // This indicates the end of a volume descriptor. HighSierra here would have 16 so no problem + if(vdType == 255) + return false; + + Array.Copy(vdSector, 0x001 + xaOff, vdMagic, 0, 5); + Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); + + AaruConsole.DebugWriteLine("ISO9660 plugin", "VDMagic = {0}", Encoding.ASCII.GetString(vdMagic)); + AaruConsole.DebugWriteLine("ISO9660 plugin", "HSMagic = {0}", Encoding.ASCII.GetString(hsMagic)); + + return Encoding.ASCII.GetString(vdMagic) == ISO_MAGIC || + Encoding.ASCII.GetString(hsMagic) == HIGH_SIERRA_MAGIC || + Encoding.ASCII.GetString(vdMagic) == CDI_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.ASCII; + information = ""; + var isoMetadata = new StringBuilder(); + byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" + byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" + + string bootSpec = ""; + + PrimaryVolumeDescriptor? pvd = null; + PrimaryVolumeDescriptor? jolietvd = null; + BootRecord? bvd = null; + HighSierraPrimaryVolumeDescriptor? hsvd = null; + FileStructureVolumeDescriptor? fsvd = null; + ElToritoBootRecord? torito = null; + + // ISO9660 is designed for 2048 bytes/sector devices + if(imagePlugin.Info.SectorSize < 2048) + return; + + // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. + if(partition.End < 16) + return; + + ulong counter = 0; + + ErrorNumber errno = imagePlugin.ReadSector(16 + counter + partition.Start, out byte[] vdSector); + + if(errno != ErrorNumber.NoError) + return; + + int xaOff = vdSector.Length == 2336 ? 8 : 0; + Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); + bool highSierraInfo = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; + int hsOff = 0; + + if(highSierraInfo) + hsOff = 8; + + bool cdiInfo = false; + bool evd = false; + bool vpd = false; + + while(true) { - // ISO9660 is designed for 2048 bytes/sector devices - if(imagePlugin.Info.SectorSize < 2048) - return false; + AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); - // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. - if(partition.End <= 16 + partition.Start) - return false; - - // Read to Volume Descriptor - ErrorNumber errno = imagePlugin.ReadSector(16 + partition.Start, out byte[] vdSector); + // Seek to Volume Descriptor + AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); + errno = imagePlugin.ReadSector(16 + counter + partition.Start, out byte[] vdSectorTmp); if(errno != ErrorNumber.NoError) - return false; + return; - int xaOff = 0; + vdSector = new byte[vdSectorTmp.Length - xaOff]; + Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); - if(vdSector.Length == 2336) - xaOff = 8; + byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. + AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); - byte vdType = vdSector[0 + xaOff]; - byte[] vdMagic = new byte[5]; - byte[] hsMagic = new byte[5]; + if(vdType == 255) // Supposedly we are in the PVD. + { + if(counter == 0) + return; - // This indicates the end of a volume descriptor. HighSierra here would have 16 so no problem - if(vdType == 255) - return false; + break; + } - Array.Copy(vdSector, 0x001 + xaOff, vdMagic, 0, 5); - Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); + Array.Copy(vdSector, 0x001, vdMagic, 0, 5); + Array.Copy(vdSector, 0x009, hsMagic, 0, 5); - AaruConsole.DebugWriteLine("ISO9660 plugin", "VDMagic = {0}", Encoding.ASCII.GetString(vdMagic)); - AaruConsole.DebugWriteLine("ISO9660 plugin", "HSMagic = {0}", Encoding.ASCII.GetString(hsMagic)); + if(Encoding.GetString(vdMagic) != ISO_MAGIC && + Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && + Encoding.GetString(vdMagic) != + CDI_MAGIC) // Recognized, it is an ISO9660, now check for rest of data. + { + if(counter == 0) + return; - return Encoding.ASCII.GetString(vdMagic) == ISO_MAGIC || - Encoding.ASCII.GetString(hsMagic) == HIGH_SIERRA_MAGIC || - Encoding.ASCII.GetString(vdMagic) == CDI_MAGIC; + break; + } + + cdiInfo |= Encoding.GetString(vdMagic) == CDI_MAGIC; + + switch(vdType) + { + case 0: + { + bvd = Marshal.ByteArrayToStructureLittleEndian(vdSector, hsOff, 2048 - hsOff); + + bootSpec = "Unknown"; + + if(Encoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION") + { + bootSpec = "El Torito"; + + torito = + Marshal.ByteArrayToStructureLittleEndian(vdSector, hsOff, + 2048 - hsOff); + } + + break; + } + + case 1: + { + if(highSierraInfo) + hsvd = Marshal. + ByteArrayToStructureLittleEndian(vdSector); + else if(cdiInfo) + fsvd = Marshal.ByteArrayToStructureBigEndian(vdSector); + else + pvd = Marshal.ByteArrayToStructureLittleEndian(vdSector); + + break; + } + + case 2: + { + PrimaryVolumeDescriptor svd = + Marshal.ByteArrayToStructureLittleEndian(vdSector); + + // Check if this is Joliet + if(svd.version == 1) + { + if(svd.escape_sequences[0] == '%' && + svd.escape_sequences[1] == '/') + if(svd.escape_sequences[2] == '@' || + svd.escape_sequences[2] == 'C' || + svd.escape_sequences[2] == 'E') + jolietvd = svd; + else + AaruConsole.WriteLine("ISO9660 plugin", + "Found unknown supplementary volume descriptor"); + } + else + evd = true; + + break; + } + + case 3: + { + vpd = true; + + break; + } + } + + counter++; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + DecodedVolumeDescriptor decodedVd; + var decodedJolietVd = new DecodedVolumeDescriptor(); + + XmlFsType = new FileSystemType(); + + if(pvd == null && + hsvd == null && + fsvd == null) { - Encoding = encoding ?? Encoding.ASCII; - information = ""; - var isoMetadata = new StringBuilder(); - byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" - byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" + information = "ERROR: Could not find primary volume descriptor"; - string bootSpec = ""; + return; + } - PrimaryVolumeDescriptor? pvd = null; - PrimaryVolumeDescriptor? jolietvd = null; - BootRecord? bvd = null; - HighSierraPrimaryVolumeDescriptor? hsvd = null; - FileStructureVolumeDescriptor? fsvd = null; - ElToritoBootRecord? torito = null; + if(highSierraInfo) + decodedVd = DecodeVolumeDescriptor(hsvd.Value); + else if(cdiInfo) + decodedVd = DecodeVolumeDescriptor(fsvd.Value); + else + decodedVd = DecodeVolumeDescriptor(pvd.Value); - // ISO9660 is designed for 2048 bytes/sector devices - if(imagePlugin.Info.SectorSize < 2048) - return; + if(jolietvd != null) + decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); - // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. - if(partition.End < 16) - return; + uint rootLocation = 0; + uint rootSize = 0; - ulong counter = 0; + // No need to read root on CD-i, as extensions are not supported... + if(!cdiInfo) + { + rootLocation = highSierraInfo ? hsvd.Value.root_directory_record.extent + : pvd.Value.root_directory_record.extent; - ErrorNumber errno = imagePlugin.ReadSector(16 + counter + partition.Start, out byte[] vdSector); + if(highSierraInfo) + { + rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size; + + if(hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0) + rootSize++; + } + else + { + rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size; + + if(pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0) + rootSize++; + } + } + + byte[] rootDir = Array.Empty(); + int rootOff = 0; + bool xaExtensions = false; + bool apple = false; + bool susp = false; + bool rrip = false; + bool ziso = false; + bool amiga = false; + bool aaip = false; + List contareas = new(); + List refareas = new(); + var suspInformation = new StringBuilder(); + + if(rootLocation + rootSize < imagePlugin.Info.Sectors) + { + errno = imagePlugin.ReadSectors(rootLocation, rootSize, out rootDir); if(errno != ErrorNumber.NoError) return; + } - int xaOff = vdSector.Length == 2336 ? 8 : 0; - Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); - bool highSierraInfo = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; - int hsOff = 0; + // Walk thru root directory to see system area extensions in use + while(rootOff + Marshal.SizeOf() < rootDir.Length && + !cdiInfo) + { + DirectoryRecord record = + Marshal.ByteArrayToStructureLittleEndian(rootDir, rootOff, + Marshal.SizeOf()); - if(highSierraInfo) - hsOff = 8; + int saOff = Marshal.SizeOf() + record.name_len; + saOff += saOff % 2; + int saLen = record.length - saOff; - bool cdiInfo = false; - bool evd = false; - bool vpd = false; - - while(true) + if(saLen > 0 && + rootOff + saOff + saLen <= rootDir.Length) { - AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); + byte[] sa = new byte[saLen]; + Array.Copy(rootDir, rootOff + saOff, sa, 0, saLen); + saOff = 0; - // Seek to Volume Descriptor - AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); - errno = imagePlugin.ReadSector(16 + counter + partition.Start, out byte[] vdSectorTmp); - - if(errno != ErrorNumber.NoError) - return; - - vdSector = new byte[vdSectorTmp.Length - xaOff]; - Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); - - byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. - AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); - - if(vdType == 255) // Supposedly we are in the PVD. + while(saOff < saLen) { - if(counter == 0) - return; + bool noneFound = true; - break; - } - - Array.Copy(vdSector, 0x001, vdMagic, 0, 5); - Array.Copy(vdSector, 0x009, hsMagic, 0, 5); - - if(Encoding.GetString(vdMagic) != ISO_MAGIC && - Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && - Encoding.GetString(vdMagic) != - CDI_MAGIC) // Recognized, it is an ISO9660, now check for rest of data. - { - if(counter == 0) - return; - - break; - } - - cdiInfo |= Encoding.GetString(vdMagic) == CDI_MAGIC; - - switch(vdType) - { - case 0: + if(Marshal.SizeOf() + saOff <= saLen) { - bvd = Marshal.ByteArrayToStructureLittleEndian(vdSector, hsOff, 2048 - hsOff); + CdromXa xa = Marshal.ByteArrayToStructureBigEndian(sa); - bootSpec = "Unknown"; - - if(Encoding.GetString(bvd.Value.system_id).Substring(0, 23) == "EL TORITO SPECIFICATION") + if(xa.signature == XA_MAGIC) { - bootSpec = "El Torito"; - - torito = - Marshal.ByteArrayToStructureLittleEndian(vdSector, hsOff, - 2048 - hsOff); + xaExtensions = true; + saOff += Marshal.SizeOf(); + noneFound = false; } + } + if(saOff + 2 >= saLen) break; - } - case 1: - { - if(highSierraInfo) - hsvd = Marshal. - ByteArrayToStructureLittleEndian(vdSector); - else if(cdiInfo) - fsvd = Marshal.ByteArrayToStructureBigEndian(vdSector); - else - pvd = Marshal.ByteArrayToStructureLittleEndian(vdSector); - - break; - } - - case 2: - { - PrimaryVolumeDescriptor svd = - Marshal.ByteArrayToStructureLittleEndian(vdSector); - - // Check if this is Joliet - if(svd.version == 1) - { - if(svd.escape_sequences[0] == '%' && - svd.escape_sequences[1] == '/') - if(svd.escape_sequences[2] == '@' || - svd.escape_sequences[2] == 'C' || - svd.escape_sequences[2] == 'E') - jolietvd = svd; - else - AaruConsole.WriteLine("ISO9660 plugin", - "Found unknown supplementary volume descriptor"); - } - else - evd = true; - - break; - } - - case 3: - { - vpd = true; - - break; - } - } - - counter++; - } - - DecodedVolumeDescriptor decodedVd; - var decodedJolietVd = new DecodedVolumeDescriptor(); - - XmlFsType = new FileSystemType(); - - if(pvd == null && - hsvd == null && - fsvd == null) - { - information = "ERROR: Could not find primary volume descriptor"; - - return; - } - - if(highSierraInfo) - decodedVd = DecodeVolumeDescriptor(hsvd.Value); - else if(cdiInfo) - decodedVd = DecodeVolumeDescriptor(fsvd.Value); - else - decodedVd = DecodeVolumeDescriptor(pvd.Value); - - if(jolietvd != null) - decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); - - uint rootLocation = 0; - uint rootSize = 0; - - // No need to read root on CD-i, as extensions are not supported... - if(!cdiInfo) - { - rootLocation = highSierraInfo ? hsvd.Value.root_directory_record.extent - : pvd.Value.root_directory_record.extent; - - if(highSierraInfo) - { - rootSize = hsvd.Value.root_directory_record.size / hsvd.Value.logical_block_size; - - if(hsvd.Value.root_directory_record.size % hsvd.Value.logical_block_size > 0) - rootSize++; - } - else - { - rootSize = pvd.Value.root_directory_record.size / pvd.Value.logical_block_size; - - if(pvd.Value.root_directory_record.size % pvd.Value.logical_block_size > 0) - rootSize++; - } - } - - byte[] rootDir = Array.Empty(); - int rootOff = 0; - bool xaExtensions = false; - bool apple = false; - bool susp = false; - bool rrip = false; - bool ziso = false; - bool amiga = false; - bool aaip = false; - List contareas = new(); - List refareas = new(); - var suspInformation = new StringBuilder(); - - if(rootLocation + rootSize < imagePlugin.Info.Sectors) - { - errno = imagePlugin.ReadSectors(rootLocation, rootSize, out rootDir); - - if(errno != ErrorNumber.NoError) - return; - } - - // Walk thru root directory to see system area extensions in use - while(rootOff + Marshal.SizeOf() < rootDir.Length && - !cdiInfo) - { - DirectoryRecord record = - Marshal.ByteArrayToStructureLittleEndian(rootDir, rootOff, - Marshal.SizeOf()); - - int saOff = Marshal.SizeOf() + record.name_len; - saOff += saOff % 2; - int saLen = record.length - saOff; - - if(saLen > 0 && - rootOff + saOff + saLen <= rootDir.Length) - { - byte[] sa = new byte[saLen]; - Array.Copy(rootDir, rootOff + saOff, sa, 0, saLen); - saOff = 0; - - while(saOff < saLen) - { - bool noneFound = true; - - if(Marshal.SizeOf() + saOff <= saLen) - { - CdromXa xa = Marshal.ByteArrayToStructureBigEndian(sa); - - if(xa.signature == XA_MAGIC) - { - xaExtensions = true; - saOff += Marshal.SizeOf(); - noneFound = false; - } - } - - if(saOff + 2 >= saLen) - break; - - ushort nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); - - switch(nextSignature) - { - // Easy, contains size field - case APPLE_MAGIC: - apple = true; - saOff += sa[saOff + 2]; - noneFound = false; - - break; - - // Not easy, contains size field - case APPLE_MAGIC_OLD: - apple = true; - var appleId = (AppleOldId)sa[saOff + 2]; - noneFound = false; - - switch(appleId) - { - case AppleOldId.ProDOS: - saOff += Marshal.SizeOf(); - - break; - case AppleOldId.TypeCreator: - case AppleOldId.TypeCreatorBundle: - saOff += Marshal.SizeOf(); - - break; - case AppleOldId.TypeCreatorIcon: - case AppleOldId.TypeCreatorIconBundle: - saOff += Marshal.SizeOf(); - - break; - case AppleOldId.HFS: - saOff += Marshal.SizeOf(); - - break; - } - - break; - - // IEEE-P1281 aka SUSP 1.12 - case SUSP_INDICATOR: - susp = true; - saOff += sa[saOff + 2]; - noneFound = false; - - while(saOff + 2 < saLen) - { - nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); - - switch(nextSignature) - { - case APPLE_MAGIC: - if(sa[saOff + 3] == 1 && - sa[saOff + 2] == 7) - apple = true; - else - apple |= sa[saOff + 3] != 1; - - break; - case SUSP_CONTINUATION when saOff + sa[saOff + 2] <= saLen: - byte[] ce = new byte[sa[saOff + 2]]; - Array.Copy(sa, saOff, ce, 0, ce.Length); - - ContinuationArea ca = - Marshal.ByteArrayToStructureBigEndian(ce); - - contareas.Add(ca); - - break; - case SUSP_REFERENCE when saOff + sa[saOff + 2] <= saLen: - byte[] er = new byte[sa[saOff + 2]]; - Array.Copy(sa, saOff, er, 0, er.Length); - refareas.Add(er); - - break; - } - - rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || - nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || - nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || - nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || - nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; - - ziso |= nextSignature == ZISO_MAGIC; - amiga |= nextSignature == AMIGA_MAGIC; - - aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && - sa[saOff + 3] == 1 && sa[saOff + 2] >= 9); - - saOff += sa[saOff + 2]; - - if(nextSignature == SUSP_TERMINATOR) - break; - } - - break; - } - - if(noneFound) - break; - } - } - - rootOff += record.length; - - if(record.length == 0) - break; - } - - foreach(ContinuationArea ca in contareas) - { - uint caLen = (ca.ca_length_be + ca.offset_be) / - (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size); - - if((ca.ca_length_be + ca.offset_be) % - (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size) > 0) - caLen++; - - errno = imagePlugin.ReadSectors(ca.block_be, caLen, out byte[] caSectors); - - if(errno != ErrorNumber.NoError) - return; - - byte[] caData = new byte[ca.ca_length_be]; - Array.Copy(caSectors, ca.offset_be, caData, 0, ca.ca_length_be); - int caOff = 0; - - while(caOff < ca.ca_length_be) - { - ushort nextSignature = BigEndianBitConverter.ToUInt16(caData, caOff); + ushort nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); switch(nextSignature) { - // Apple never said to include its extensions inside a continuation area, but just in case + // Easy, contains size field case APPLE_MAGIC: - if(caData[caOff + 3] == 1 && - caData[caOff + 2] == 7) - apple = true; - else - apple |= caData[caOff + 3] != 1; + apple = true; + saOff += sa[saOff + 2]; + noneFound = false; break; - case SUSP_REFERENCE when caOff + caData[caOff + 2] <= ca.ca_length_be: - byte[] er = new byte[caData[caOff + 2]]; - Array.Copy(caData, caOff, er, 0, er.Length); - refareas.Add(er); + + // Not easy, contains size field + case APPLE_MAGIC_OLD: + apple = true; + var appleId = (AppleOldId)sa[saOff + 2]; + noneFound = false; + + switch(appleId) + { + case AppleOldId.ProDOS: + saOff += Marshal.SizeOf(); + + break; + case AppleOldId.TypeCreator: + case AppleOldId.TypeCreatorBundle: + saOff += Marshal.SizeOf(); + + break; + case AppleOldId.TypeCreatorIcon: + case AppleOldId.TypeCreatorIconBundle: + saOff += Marshal.SizeOf(); + + break; + case AppleOldId.HFS: + saOff += Marshal.SizeOf(); + + break; + } + + break; + + // IEEE-P1281 aka SUSP 1.12 + case SUSP_INDICATOR: + susp = true; + saOff += sa[saOff + 2]; + noneFound = false; + + while(saOff + 2 < saLen) + { + nextSignature = BigEndianBitConverter.ToUInt16(sa, saOff); + + switch(nextSignature) + { + case APPLE_MAGIC: + if(sa[saOff + 3] == 1 && + sa[saOff + 2] == 7) + apple = true; + else + apple |= sa[saOff + 3] != 1; + + break; + case SUSP_CONTINUATION when saOff + sa[saOff + 2] <= saLen: + byte[] ce = new byte[sa[saOff + 2]]; + Array.Copy(sa, saOff, ce, 0, ce.Length); + + ContinuationArea ca = + Marshal.ByteArrayToStructureBigEndian(ce); + + contareas.Add(ca); + + break; + case SUSP_REFERENCE when saOff + sa[saOff + 2] <= saLen: + byte[] er = new byte[sa[saOff + 2]]; + Array.Copy(sa, saOff, er, 0, er.Length); + refareas.Add(er); + + break; + } + + rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || + nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || + nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || + nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || + nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; + + ziso |= nextSignature == ZISO_MAGIC; + amiga |= nextSignature == AMIGA_MAGIC; + + aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && + sa[saOff + 3] == 1 && sa[saOff + 2] >= 9); + + saOff += sa[saOff + 2]; + + if(nextSignature == SUSP_TERMINATOR) + break; + } break; } - rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || - nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || - nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || - nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || - nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; - - ziso |= nextSignature == ZISO_MAGIC; - amiga |= nextSignature == AMIGA_MAGIC; - - aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && caData[caOff + 3] == 1 && - caData[caOff + 2] >= 9); - - caOff += caData[caOff + 2]; + if(noneFound) + break; } } - if(refareas.Count > 0) - { - suspInformation.AppendLine("----------------------------------------"); - suspInformation.AppendLine("SYSTEM USE SHARING PROTOCOL INFORMATION:"); - suspInformation.AppendLine("----------------------------------------"); + rootOff += record.length; - counter = 1; + if(record.length == 0) + break; + } - foreach(byte[] erb in refareas) - { - ReferenceArea er = Marshal.ByteArrayToStructureBigEndian(erb); - string extId = Encoding.GetString(erb, Marshal.SizeOf(), er.id_len); + foreach(ContinuationArea ca in contareas) + { + uint caLen = (ca.ca_length_be + ca.offset_be) / + (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size); - string extDes = Encoding.GetString(erb, Marshal.SizeOf() + er.id_len, er.des_len); + if((ca.ca_length_be + ca.offset_be) % + (highSierraInfo ? hsvd.Value.logical_block_size : pvd.Value.logical_block_size) > 0) + caLen++; - string extSrc = Encoding.GetString(erb, Marshal.SizeOf() + er.id_len + er.des_len, - er.src_len); - - suspInformation.AppendFormat("Extension: {0}", counter).AppendLine(); - suspInformation.AppendFormat("\tID: {0}, version {1}", extId, er.ext_ver).AppendLine(); - suspInformation.AppendFormat("\tDescription: {0}", extDes).AppendLine(); - suspInformation.AppendFormat("\tSource: {0}", extSrc).AppendLine(); - counter++; - } - } - - errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] ipbinSector); + errno = imagePlugin.ReadSectors(ca.block_be, caLen, out byte[] caSectors); if(errno != ErrorNumber.NoError) return; - CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); - Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); - Dreamcast.IPBin? dreamcast = Dreamcast.DecodeIPBin(ipbinSector); + byte[] caData = new byte[ca.ca_length_be]; + Array.Copy(caSectors, ca.offset_be, caData, 0, ca.ca_length_be); + int caOff = 0; - string fsFormat; - - if(highSierraInfo) - fsFormat = "High Sierra Format"; - else if(cdiInfo) - fsFormat = "CD-i"; - else - fsFormat = "ISO9660"; - - isoMetadata.AppendFormat("{0} file system", fsFormat).AppendLine(); - - if(xaExtensions) - isoMetadata.AppendLine("CD-ROM XA extensions present."); - - if(amiga) - isoMetadata.AppendLine("Amiga extensions present."); - - if(apple) - isoMetadata.AppendLine("Apple extensions present."); - - if(jolietvd != null) - isoMetadata.AppendLine("Joliet extensions present."); - - if(susp) - isoMetadata.AppendLine("System Use Sharing Protocol present."); - - if(rrip) - isoMetadata.AppendLine("Rock Ridge Interchange Protocol present."); - - if(aaip) - isoMetadata.AppendLine("Arbitrary Attribute Interchange Protocol present."); - - if(ziso) - isoMetadata.AppendLine("zisofs compression present."); - - if(evd) - isoMetadata.AppendLine("Contains Enhanved Volume Descriptor."); - - if(vpd) - isoMetadata.AppendLine("Contains Volume Partition Descriptor."); - - if(bvd != null) - isoMetadata.AppendFormat("Disc bootable following {0} specifications.", bootSpec).AppendLine(); - - if(segaCd != null) + while(caOff < ca.ca_length_be) { - isoMetadata.AppendLine("This is a SegaCD / MegaCD disc."); - isoMetadata.AppendLine(CD.Prettify(segaCd)); - } + ushort nextSignature = BigEndianBitConverter.ToUInt16(caData, caOff); - if(saturn != null) + switch(nextSignature) + { + // Apple never said to include its extensions inside a continuation area, but just in case + case APPLE_MAGIC: + if(caData[caOff + 3] == 1 && + caData[caOff + 2] == 7) + apple = true; + else + apple |= caData[caOff + 3] != 1; + + break; + case SUSP_REFERENCE when caOff + caData[caOff + 2] <= ca.ca_length_be: + byte[] er = new byte[caData[caOff + 2]]; + Array.Copy(caData, caOff, er, 0, er.Length); + refareas.Add(er); + + break; + } + + rrip |= nextSignature == RRIP_MAGIC || nextSignature == RRIP_POSIX_ATTRIBUTES || + nextSignature == RRIP_POSIX_DEV_NO || nextSignature == RRIP_SYMLINK || + nextSignature == RRIP_NAME || nextSignature == RRIP_CHILDLINK || + nextSignature == RRIP_PARENTLINK || nextSignature == RRIP_RELOCATED_DIR || + nextSignature == RRIP_TIMESTAMPS || nextSignature == RRIP_SPARSE; + + ziso |= nextSignature == ZISO_MAGIC; + amiga |= nextSignature == AMIGA_MAGIC; + + aaip |= nextSignature == AAIP_MAGIC || (nextSignature == AAIP_MAGIC_OLD && caData[caOff + 3] == 1 && + caData[caOff + 2] >= 9); + + caOff += caData[caOff + 2]; + } + } + + if(refareas.Count > 0) + { + suspInformation.AppendLine("----------------------------------------"); + suspInformation.AppendLine("SYSTEM USE SHARING PROTOCOL INFORMATION:"); + suspInformation.AppendLine("----------------------------------------"); + + counter = 1; + + foreach(byte[] erb in refareas) { - isoMetadata.AppendLine("This is a Sega Saturn disc."); - isoMetadata.AppendLine(Saturn.Prettify(saturn)); - } + ReferenceArea er = Marshal.ByteArrayToStructureBigEndian(erb); + string extId = Encoding.GetString(erb, Marshal.SizeOf(), er.id_len); - if(dreamcast != null) - { - isoMetadata.AppendLine("This is a Sega Dreamcast disc."); - isoMetadata.AppendLine(Dreamcast.Prettify(dreamcast)); - } + string extDes = Encoding.GetString(erb, Marshal.SizeOf() + er.id_len, er.des_len); - isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : ""). + string extSrc = Encoding.GetString(erb, Marshal.SizeOf() + er.id_len + er.des_len, + er.src_len); + + suspInformation.AppendFormat("Extension: {0}", counter).AppendLine(); + suspInformation.AppendFormat("\tID: {0}, version {1}", extId, er.ext_ver).AppendLine(); + suspInformation.AppendFormat("\tDescription: {0}", extDes).AppendLine(); + suspInformation.AppendFormat("\tSource: {0}", extSrc).AppendLine(); + counter++; + } + } + + errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] ipbinSector); + + if(errno != ErrorNumber.NoError) + return; + + CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); + Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); + Dreamcast.IPBin? dreamcast = Dreamcast.DecodeIPBin(ipbinSector); + + string fsFormat; + + if(highSierraInfo) + fsFormat = "High Sierra Format"; + else if(cdiInfo) + fsFormat = "CD-i"; + else + fsFormat = "ISO9660"; + + isoMetadata.AppendFormat("{0} file system", fsFormat).AppendLine(); + + if(xaExtensions) + isoMetadata.AppendLine("CD-ROM XA extensions present."); + + if(amiga) + isoMetadata.AppendLine("Amiga extensions present."); + + if(apple) + isoMetadata.AppendLine("Apple extensions present."); + + if(jolietvd != null) + isoMetadata.AppendLine("Joliet extensions present."); + + if(susp) + isoMetadata.AppendLine("System Use Sharing Protocol present."); + + if(rrip) + isoMetadata.AppendLine("Rock Ridge Interchange Protocol present."); + + if(aaip) + isoMetadata.AppendLine("Arbitrary Attribute Interchange Protocol present."); + + if(ziso) + isoMetadata.AppendLine("zisofs compression present."); + + if(evd) + isoMetadata.AppendLine("Contains Enhanved Volume Descriptor."); + + if(vpd) + isoMetadata.AppendLine("Contains Volume Partition Descriptor."); + + if(bvd != null) + isoMetadata.AppendFormat("Disc bootable following {0} specifications.", bootSpec).AppendLine(); + + if(segaCd != null) + { + isoMetadata.AppendLine("This is a SegaCD / MegaCD disc."); + isoMetadata.AppendLine(CD.Prettify(segaCd)); + } + + if(saturn != null) + { + isoMetadata.AppendLine("This is a Sega Saturn disc."); + isoMetadata.AppendLine(Saturn.Prettify(saturn)); + } + + if(dreamcast != null) + { + isoMetadata.AppendLine("This is a Sega Dreamcast disc."); + isoMetadata.AppendLine(Dreamcast.Prettify(dreamcast)); + } + + isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : ""). + AppendLine(); + + isoMetadata.AppendFormat("{0}VOLUME DESCRIPTOR INFORMATION:", cdiInfo ? "FILE STRUCTURE " : ""). + AppendLine(); + + isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : ""). + AppendLine(); + + isoMetadata.AppendFormat("System identifier: {0}", decodedVd.SystemIdentifier).AppendLine(); + isoMetadata.AppendFormat("Volume identifier: {0}", decodedVd.VolumeIdentifier).AppendLine(); + isoMetadata.AppendFormat("Volume set identifier: {0}", decodedVd.VolumeSetIdentifier).AppendLine(); + isoMetadata.AppendFormat("Publisher identifier: {0}", decodedVd.PublisherIdentifier).AppendLine(); + isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedVd.DataPreparerIdentifier).AppendLine(); + isoMetadata.AppendFormat("Application identifier: {0}", decodedVd.ApplicationIdentifier).AppendLine(); + isoMetadata.AppendFormat("Volume creation date: {0}", decodedVd.CreationTime).AppendLine(); + + if(decodedVd.HasModificationTime) + isoMetadata.AppendFormat("Volume modification date: {0}", decodedVd.ModificationTime).AppendLine(); + else + isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); + + if(decodedVd.HasExpirationTime) + isoMetadata.AppendFormat("Volume expiration date: {0}", decodedVd.ExpirationTime).AppendLine(); + else + isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); + + if(decodedVd.HasEffectiveTime) + isoMetadata.AppendFormat("Volume effective date: {0}", decodedVd.EffectiveTime).AppendLine(); + else + isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); + + isoMetadata.AppendFormat("Volume has {0} blocks of {1} bytes each", decodedVd.Blocks, decodedVd.BlockSize). + AppendLine(); + + if(jolietvd != null) + { + isoMetadata.AppendLine("-------------------------------------"); + isoMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:"); + isoMetadata.AppendLine("-------------------------------------"); + isoMetadata.AppendFormat("System identifier: {0}", decodedJolietVd.SystemIdentifier).AppendLine(); + isoMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVd.VolumeIdentifier).AppendLine(); + + isoMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVd.VolumeSetIdentifier). AppendLine(); - isoMetadata.AppendFormat("{0}VOLUME DESCRIPTOR INFORMATION:", cdiInfo ? "FILE STRUCTURE " : ""). + isoMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVd.PublisherIdentifier).AppendLine(); + + isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVd.DataPreparerIdentifier). AppendLine(); - isoMetadata.AppendFormat("{0}------------------------------", cdiInfo ? "---------------" : ""). + isoMetadata.AppendFormat("Application identifier: {0}", decodedJolietVd.ApplicationIdentifier). AppendLine(); - isoMetadata.AppendFormat("System identifier: {0}", decodedVd.SystemIdentifier).AppendLine(); - isoMetadata.AppendFormat("Volume identifier: {0}", decodedVd.VolumeIdentifier).AppendLine(); - isoMetadata.AppendFormat("Volume set identifier: {0}", decodedVd.VolumeSetIdentifier).AppendLine(); - isoMetadata.AppendFormat("Publisher identifier: {0}", decodedVd.PublisherIdentifier).AppendLine(); - isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedVd.DataPreparerIdentifier).AppendLine(); - isoMetadata.AppendFormat("Application identifier: {0}", decodedVd.ApplicationIdentifier).AppendLine(); - isoMetadata.AppendFormat("Volume creation date: {0}", decodedVd.CreationTime).AppendLine(); + isoMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVd.CreationTime).AppendLine(); - if(decodedVd.HasModificationTime) - isoMetadata.AppendFormat("Volume modification date: {0}", decodedVd.ModificationTime).AppendLine(); + if(decodedJolietVd.HasModificationTime) + isoMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVd.ModificationTime). + AppendLine(); else isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); - if(decodedVd.HasExpirationTime) - isoMetadata.AppendFormat("Volume expiration date: {0}", decodedVd.ExpirationTime).AppendLine(); + if(decodedJolietVd.HasExpirationTime) + isoMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVd.ExpirationTime). + AppendLine(); else isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); - if(decodedVd.HasEffectiveTime) - isoMetadata.AppendFormat("Volume effective date: {0}", decodedVd.EffectiveTime).AppendLine(); + if(decodedJolietVd.HasEffectiveTime) + isoMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVd.EffectiveTime).AppendLine(); else isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); + } - isoMetadata.AppendFormat("Volume has {0} blocks of {1} bytes each", decodedVd.Blocks, decodedVd.BlockSize). - AppendLine(); + if(torito != null) + { + errno = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start, out vdSector); - if(jolietvd != null) + if(errno != ErrorNumber.NoError) + return; + + int toritoOff = 0; + + if(vdSector[toritoOff] != 1) + goto exit_torito; + + ElToritoValidationEntry valentry = + Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, + EL_TORITO_ENTRY_SIZE); + + if(valentry.signature != EL_TORITO_MAGIC) + goto exit_torito; + + toritoOff = EL_TORITO_ENTRY_SIZE; + + ElToritoInitialEntry initialEntry = + Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, + EL_TORITO_ENTRY_SIZE); + + initialEntry.boot_type = (ElToritoEmulation)((byte)initialEntry.boot_type & 0xF); + + AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.load_rba = {0}", + initialEntry.load_rba); + + AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.sector_count = {0}", + initialEntry.sector_count); + + byte[] bootImage = null; + + if(initialEntry.load_rba + partition.Start + initialEntry.sector_count - 1 <= partition.End) + imagePlugin.ReadSectors(initialEntry.load_rba + partition.Start, initialEntry.sector_count, + out bootImage); + + isoMetadata.AppendLine("----------------------"); + isoMetadata.AppendLine("EL TORITO INFORMATION:"); + isoMetadata.AppendLine("----------------------"); + + isoMetadata.AppendLine("Initial entry:"); + isoMetadata.AppendFormat("\tDeveloper ID: {0}", Encoding.GetString(valentry.developer_id)).AppendLine(); + + if(initialEntry.bootable == ElToritoIndicator.Bootable) { - isoMetadata.AppendLine("-------------------------------------"); - isoMetadata.AppendLine("JOLIET VOLUME DESCRIPTOR INFORMATION:"); - isoMetadata.AppendLine("-------------------------------------"); - isoMetadata.AppendFormat("System identifier: {0}", decodedJolietVd.SystemIdentifier).AppendLine(); - isoMetadata.AppendFormat("Volume identifier: {0}", decodedJolietVd.VolumeIdentifier).AppendLine(); + isoMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine(); - isoMetadata.AppendFormat("Volume set identifier: {0}", decodedJolietVd.VolumeSetIdentifier). - AppendLine(); + isoMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors", + initialEntry.load_rba, initialEntry.sector_count).AppendLine(); - isoMetadata.AppendFormat("Publisher identifier: {0}", decodedJolietVd.PublisherIdentifier).AppendLine(); - - isoMetadata.AppendFormat("Data preparer identifier: {0}", decodedJolietVd.DataPreparerIdentifier). - AppendLine(); - - isoMetadata.AppendFormat("Application identifier: {0}", decodedJolietVd.ApplicationIdentifier). - AppendLine(); - - isoMetadata.AppendFormat("Volume creation date: {0}", decodedJolietVd.CreationTime).AppendLine(); - - if(decodedJolietVd.HasModificationTime) - isoMetadata.AppendFormat("Volume modification date: {0}", decodedJolietVd.ModificationTime). + if(valentry.platform_id == ElToritoPlatform.x86) + isoMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h", + initialEntry.load_seg == 0 ? 0x7C0 : initialEntry.load_seg). AppendLine(); else - isoMetadata.AppendFormat("Volume has not been modified.").AppendLine(); + isoMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}", + (uint)initialEntry.load_seg * 10).AppendLine(); - if(decodedJolietVd.HasExpirationTime) - isoMetadata.AppendFormat("Volume expiration date: {0}", decodedJolietVd.ExpirationTime). - AppendLine(); - else - isoMetadata.AppendFormat("Volume does not expire.").AppendLine(); - - if(decodedJolietVd.HasEffectiveTime) - isoMetadata.AppendFormat("Volume effective date: {0}", decodedJolietVd.EffectiveTime).AppendLine(); - else - isoMetadata.AppendFormat("Volume has always been effective.").AppendLine(); - } - - if(torito != null) - { - errno = imagePlugin.ReadSector(torito.Value.catalog_sector + partition.Start, out vdSector); - - if(errno != ErrorNumber.NoError) - return; - - int toritoOff = 0; - - if(vdSector[toritoOff] != 1) - goto exit_torito; - - ElToritoValidationEntry valentry = - Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, - EL_TORITO_ENTRY_SIZE); - - if(valentry.signature != EL_TORITO_MAGIC) - goto exit_torito; - - toritoOff = EL_TORITO_ENTRY_SIZE; - - ElToritoInitialEntry initialEntry = - Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, - EL_TORITO_ENTRY_SIZE); - - initialEntry.boot_type = (ElToritoEmulation)((byte)initialEntry.boot_type & 0xF); - - AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.load_rba = {0}", - initialEntry.load_rba); - - AaruConsole.DebugWriteLine("DEBUG (ISO9660 plugin)", "initialEntry.sector_count = {0}", - initialEntry.sector_count); - - byte[] bootImage = null; - - if(initialEntry.load_rba + partition.Start + initialEntry.sector_count - 1 <= partition.End) - imagePlugin.ReadSectors(initialEntry.load_rba + partition.Start, initialEntry.sector_count, - out bootImage); - - isoMetadata.AppendLine("----------------------"); - isoMetadata.AppendLine("EL TORITO INFORMATION:"); - isoMetadata.AppendLine("----------------------"); - - isoMetadata.AppendLine("Initial entry:"); - isoMetadata.AppendFormat("\tDeveloper ID: {0}", Encoding.GetString(valentry.developer_id)).AppendLine(); - - if(initialEntry.bootable == ElToritoIndicator.Bootable) + switch(initialEntry.boot_type) { - isoMetadata.AppendFormat("\tBootable on {0}", valentry.platform_id).AppendLine(); + case ElToritoEmulation.None: + isoMetadata.AppendLine("\tImage uses no emulation"); - isoMetadata.AppendFormat("\tBootable image starts at sector {0} and runs for {1} sectors", - initialEntry.load_rba, initialEntry.sector_count).AppendLine(); + break; + case ElToritoEmulation.Md2Hd: + isoMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); - if(valentry.platform_id == ElToritoPlatform.x86) - isoMetadata.AppendFormat("\tBootable image will be loaded at segment {0:X4}h", - initialEntry.load_seg == 0 ? 0x7C0 : initialEntry.load_seg). - AppendLine(); - else - isoMetadata.AppendFormat("\tBootable image will be loaded at 0x{0:X8}", - (uint)initialEntry.load_seg * 10).AppendLine(); + break; + case ElToritoEmulation.Mf2Hd: + isoMetadata.AppendLine("\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); - switch(initialEntry.boot_type) - { - case ElToritoEmulation.None: - isoMetadata.AppendLine("\tImage uses no emulation"); + break; + case ElToritoEmulation.Mf2Ed: + isoMetadata.AppendLine("\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); - break; - case ElToritoEmulation.Md2Hd: - isoMetadata.AppendLine("\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); + break; + default: + isoMetadata.AppendFormat("\tImage uses unknown emulation type {0}", + (byte)initialEntry.boot_type).AppendLine(); - break; - case ElToritoEmulation.Mf2Hd: - isoMetadata.AppendLine("\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); - - break; - case ElToritoEmulation.Mf2Ed: - isoMetadata.AppendLine("\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); - - break; - default: - isoMetadata.AppendFormat("\tImage uses unknown emulation type {0}", - (byte)initialEntry.boot_type).AppendLine(); - - break; - } - - isoMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initialEntry.system_type).AppendLine(); - - if(bootImage != null) - isoMetadata.AppendFormat("\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)). - AppendLine(); + break; } - else - isoMetadata.AppendLine("\tNot bootable"); + + isoMetadata.AppendFormat("\tSystem type: 0x{0:X2}", initialEntry.system_type).AppendLine(); + + if(bootImage != null) + isoMetadata.AppendFormat("\tBootable image's SHA1: {0}", Sha1Context.Data(bootImage, out _)). + AppendLine(); + } + else + isoMetadata.AppendLine("\tNot bootable"); + + toritoOff += EL_TORITO_ENTRY_SIZE; + + const int sectionCounter = 2; + + while(toritoOff < vdSector.Length && + (vdSector[toritoOff] == (byte)ElToritoIndicator.Header || + vdSector[toritoOff] == (byte)ElToritoIndicator.LastHeader)) + { + ElToritoSectionHeaderEntry sectionHeader = + Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, + EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; - const int sectionCounter = 2; + isoMetadata.AppendFormat("Boot section {0}:", sectionCounter); - while(toritoOff < vdSector.Length && - (vdSector[toritoOff] == (byte)ElToritoIndicator.Header || - vdSector[toritoOff] == (byte)ElToritoIndicator.LastHeader)) + isoMetadata.AppendFormat("\tSection ID: {0}", Encoding.GetString(sectionHeader.identifier)). + AppendLine(); + + for(int entryCounter = 1; entryCounter <= sectionHeader.entries && toritoOff < vdSector.Length; + entryCounter++) { - ElToritoSectionHeaderEntry sectionHeader = - Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, + ElToritoSectionEntry sectionEntry = + Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; - isoMetadata.AppendFormat("Boot section {0}:", sectionCounter); + isoMetadata.AppendFormat("\tEntry {0}:", entryCounter); - isoMetadata.AppendFormat("\tSection ID: {0}", Encoding.GetString(sectionHeader.identifier)). - AppendLine(); - - for(int entryCounter = 1; entryCounter <= sectionHeader.entries && toritoOff < vdSector.Length; - entryCounter++) + if(sectionEntry.bootable == ElToritoIndicator.Bootable) { - ElToritoSectionEntry sectionEntry = - Marshal.ByteArrayToStructureLittleEndian(vdSector, toritoOff, - EL_TORITO_ENTRY_SIZE); + bootImage = null; + + if(sectionEntry.load_rba + partition.Start + sectionEntry.sector_count - 1 <= partition.End) + imagePlugin.ReadSectors(sectionEntry.load_rba + partition.Start, + sectionEntry.sector_count, out bootImage); + + isoMetadata.AppendFormat("\t\tBootable on {0}", sectionHeader.platform_id).AppendLine(); + + isoMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors", + sectionEntry.load_rba, sectionEntry.sector_count).AppendLine(); + + if(valentry.platform_id == ElToritoPlatform.x86) + isoMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h", + sectionEntry.load_seg == 0 ? 0x7C0 : sectionEntry.load_seg). + AppendLine(); + else + isoMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}", + (uint)sectionEntry.load_seg * 10).AppendLine(); + + switch((ElToritoEmulation)((byte)sectionEntry.boot_type & 0xF)) + { + case ElToritoEmulation.None: + isoMetadata.AppendLine("\t\tImage uses no emulation"); + + break; + case ElToritoEmulation.Md2Hd: + isoMetadata. + AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); + + break; + case ElToritoEmulation.Mf2Hd: + isoMetadata. + AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); + + break; + case ElToritoEmulation.Mf2Ed: + isoMetadata. + AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); + + break; + default: + isoMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}", + (byte)initialEntry.boot_type).AppendLine(); + + break; + } + + isoMetadata.AppendFormat("\t\tSelection criteria type: {0}", + sectionEntry.selection_criteria_type).AppendLine(); + + isoMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", sectionEntry.system_type). + AppendLine(); + + if(bootImage != null) + isoMetadata.AppendFormat("\t\tBootable image's SHA1: {0}", + Sha1Context.Data(bootImage, out _)).AppendLine(); + } + else + isoMetadata.AppendLine("\t\tNot bootable"); + + var flags = (ElToritoFlags)((byte)sectionEntry.boot_type & 0xF0); + + if(flags.HasFlag(ElToritoFlags.ATAPI)) + isoMetadata.AppendLine("\t\tImage contains ATAPI drivers"); + + if(flags.HasFlag(ElToritoFlags.SCSI)) + isoMetadata.AppendLine("\t\tImage contains SCSI drivers"); + + if(!flags.HasFlag(ElToritoFlags.Continued)) + continue; + + while(toritoOff < vdSector.Length) + { + ElToritoSectionEntryExtension sectionExtension = + Marshal.ByteArrayToStructureLittleEndian(vdSector, + toritoOff, EL_TORITO_ENTRY_SIZE); toritoOff += EL_TORITO_ENTRY_SIZE; - isoMetadata.AppendFormat("\tEntry {0}:", entryCounter); - - if(sectionEntry.bootable == ElToritoIndicator.Bootable) - { - bootImage = null; - - if(sectionEntry.load_rba + partition.Start + sectionEntry.sector_count - 1 <= partition.End) - imagePlugin.ReadSectors(sectionEntry.load_rba + partition.Start, - sectionEntry.sector_count, out bootImage); - - isoMetadata.AppendFormat("\t\tBootable on {0}", sectionHeader.platform_id).AppendLine(); - - isoMetadata.AppendFormat("\t\tBootable image starts at sector {0} and runs for {1} sectors", - sectionEntry.load_rba, sectionEntry.sector_count).AppendLine(); - - if(valentry.platform_id == ElToritoPlatform.x86) - isoMetadata.AppendFormat("\t\tBootable image will be loaded at segment {0:X4}h", - sectionEntry.load_seg == 0 ? 0x7C0 : sectionEntry.load_seg). - AppendLine(); - else - isoMetadata.AppendFormat("\t\tBootable image will be loaded at 0x{0:X8}", - (uint)sectionEntry.load_seg * 10).AppendLine(); - - switch((ElToritoEmulation)((byte)sectionEntry.boot_type & 0xF)) - { - case ElToritoEmulation.None: - isoMetadata.AppendLine("\t\tImage uses no emulation"); - - break; - case ElToritoEmulation.Md2Hd: - isoMetadata. - AppendLine("\t\tImage emulates a 5.25\" high-density (MD2HD, 1.2Mb) floppy"); - - break; - case ElToritoEmulation.Mf2Hd: - isoMetadata. - AppendLine("\t\tImage emulates a 3.5\" high-density (MF2HD, 1.44Mb) floppy"); - - break; - case ElToritoEmulation.Mf2Ed: - isoMetadata. - AppendLine("\t\tImage emulates a 3.5\" extra-density (MF2ED, 2.88Mb) floppy"); - - break; - default: - isoMetadata.AppendFormat("\t\tImage uses unknown emulation type {0}", - (byte)initialEntry.boot_type).AppendLine(); - - break; - } - - isoMetadata.AppendFormat("\t\tSelection criteria type: {0}", - sectionEntry.selection_criteria_type).AppendLine(); - - isoMetadata.AppendFormat("\t\tSystem type: 0x{0:X2}", sectionEntry.system_type). - AppendLine(); - - if(bootImage != null) - isoMetadata.AppendFormat("\t\tBootable image's SHA1: {0}", - Sha1Context.Data(bootImage, out _)).AppendLine(); - } - else - isoMetadata.AppendLine("\t\tNot bootable"); - - var flags = (ElToritoFlags)((byte)sectionEntry.boot_type & 0xF0); - - if(flags.HasFlag(ElToritoFlags.ATAPI)) - isoMetadata.AppendLine("\t\tImage contains ATAPI drivers"); - - if(flags.HasFlag(ElToritoFlags.SCSI)) - isoMetadata.AppendLine("\t\tImage contains SCSI drivers"); - - if(!flags.HasFlag(ElToritoFlags.Continued)) - continue; - - while(toritoOff < vdSector.Length) - { - ElToritoSectionEntryExtension sectionExtension = - Marshal.ByteArrayToStructureLittleEndian(vdSector, - toritoOff, EL_TORITO_ENTRY_SIZE); - - toritoOff += EL_TORITO_ENTRY_SIZE; - - if(!sectionExtension.extension_flags.HasFlag(ElToritoFlags.Continued)) - break; - } + if(!sectionExtension.extension_flags.HasFlag(ElToritoFlags.Continued)) + break; } - - if(sectionHeader.header_id == ElToritoIndicator.LastHeader) - break; } + + if(sectionHeader.header_id == ElToritoIndicator.LastHeader) + break; } - - exit_torito: - - if(refareas.Count > 0) - isoMetadata.Append(suspInformation); - - XmlFsType.Type = fsFormat; - - if(jolietvd != null) - { - XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || - decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) - XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; - else - XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null - : decodedJolietVd.SystemIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || - decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) - XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; - else - XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null - : decodedJolietVd.VolumeSetIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || - decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) - XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; - else - XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null - : decodedJolietVd.PublisherIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || - decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) - XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; - else - XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) - ? null : decodedJolietVd.DataPreparerIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || - decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) - XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; - else - XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null - : decodedJolietVd.ApplicationIdentifier; - - XmlFsType.CreationDate = decodedJolietVd.CreationTime; - XmlFsType.CreationDateSpecified = true; - - if(decodedJolietVd.HasModificationTime) - { - XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; - XmlFsType.ModificationDateSpecified = true; - } - - if(decodedJolietVd.HasExpirationTime) - { - XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; - XmlFsType.ExpirationDateSpecified = true; - } - - if(decodedJolietVd.HasEffectiveTime) - { - XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; - XmlFsType.EffectiveDateSpecified = true; - } - } - else - { - XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; - XmlFsType.VolumeName = decodedVd.VolumeIdentifier; - XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; - XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; - XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; - XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; - XmlFsType.CreationDate = decodedVd.CreationTime; - XmlFsType.CreationDateSpecified = true; - - if(decodedVd.HasModificationTime) - { - XmlFsType.ModificationDate = decodedVd.ModificationTime; - XmlFsType.ModificationDateSpecified = true; - } - - if(decodedVd.HasExpirationTime) - { - XmlFsType.ExpirationDate = decodedVd.ExpirationTime; - XmlFsType.ExpirationDateSpecified = true; - } - - if(decodedVd.HasEffectiveTime) - { - XmlFsType.EffectiveDate = decodedVd.EffectiveTime; - XmlFsType.EffectiveDateSpecified = true; - } - } - - XmlFsType.Bootable |= bvd != null || segaCd != null || saturn != null || dreamcast != null; - XmlFsType.Clusters = decodedVd.Blocks; - XmlFsType.ClusterSize = decodedVd.BlockSize; - - information = isoMetadata.ToString(); } + + exit_torito: + + if(refareas.Count > 0) + isoMetadata.Append(suspInformation); + + XmlFsType.Type = fsFormat; + + if(jolietvd != null) + { + XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || + decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) + XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; + else + XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null + : decodedJolietVd.SystemIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || + decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) + XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; + else + XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null + : decodedJolietVd.VolumeSetIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || + decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) + XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; + else + XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null + : decodedJolietVd.PublisherIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || + decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) + XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; + else + XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) + ? null : decodedJolietVd.DataPreparerIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || + decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) + XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; + else + XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null + : decodedJolietVd.ApplicationIdentifier; + + XmlFsType.CreationDate = decodedJolietVd.CreationTime; + XmlFsType.CreationDateSpecified = true; + + if(decodedJolietVd.HasModificationTime) + { + XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; + XmlFsType.ModificationDateSpecified = true; + } + + if(decodedJolietVd.HasExpirationTime) + { + XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; + XmlFsType.ExpirationDateSpecified = true; + } + + if(decodedJolietVd.HasEffectiveTime) + { + XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; + XmlFsType.EffectiveDateSpecified = true; + } + } + else + { + XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; + XmlFsType.VolumeName = decodedVd.VolumeIdentifier; + XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; + XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; + XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; + XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; + XmlFsType.CreationDate = decodedVd.CreationTime; + XmlFsType.CreationDateSpecified = true; + + if(decodedVd.HasModificationTime) + { + XmlFsType.ModificationDate = decodedVd.ModificationTime; + XmlFsType.ModificationDateSpecified = true; + } + + if(decodedVd.HasExpirationTime) + { + XmlFsType.ExpirationDate = decodedVd.ExpirationTime; + XmlFsType.ExpirationDateSpecified = true; + } + + if(decodedVd.HasEffectiveTime) + { + XmlFsType.EffectiveDate = decodedVd.EffectiveTime; + XmlFsType.EffectiveDateSpecified = true; + } + } + + XmlFsType.Bootable |= bvd != null || segaCd != null || saturn != null || dreamcast != null; + XmlFsType.Clusters = decodedVd.Blocks; + XmlFsType.ClusterSize = decodedVd.BlockSize; + + information = isoMetadata.ToString(); } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Mode2.cs b/Aaru.Filesystems/ISO9660/Mode2.cs index 5fd96a531..cae145911 100644 --- a/Aaru.Filesystems/ISO9660/Mode2.cs +++ b/Aaru.Filesystems/ISO9660/Mode2.cs @@ -37,34 +37,109 @@ using Aaru.CommonTypes.Enums; using Aaru.Console; using Aaru.Decoders.CD; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + ErrorNumber ReadSector(ulong sector, out byte[] buffer, bool interleaved = false, byte fileNumber = 0) { - ErrorNumber ReadSector(ulong sector, out byte[] buffer, bool interleaved = false, byte fileNumber = 0) + ulong realSector; + uint sectorCount; + ErrorNumber errno; + buffer = null; + + sectorCount = (uint)_blockSize / 2048; + + if(_blockSize % 2048 > 0) + sectorCount++; + + realSector = sector * _blockSize / 2048; + + ulong offset = sector * _blockSize % 2048; + + byte[] data; + + if(sectorCount == 1) { - ulong realSector; - uint sectorCount; - ErrorNumber errno; - buffer = null; + errno = _image.ReadSectorLong(realSector, out data); - sectorCount = (uint)_blockSize / 2048; + if(errno != ErrorNumber.NoError) + errno = _image.ReadSector(realSector, out data); - if(_blockSize % 2048 > 0) - sectorCount++; + if(errno != ErrorNumber.NoError) + return errno; - realSector = sector * _blockSize / 2048; - - ulong offset = sector * _blockSize % 2048; - - byte[] data; - - if(sectorCount == 1) + if(_debug) { - errno = _image.ReadSectorLong(realSector, out data); + switch(data.Length) + { + case 2048: + AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Cooked, Mode 0/1 / Mode 2 Form 1", + realSector); + + break; + case 2324: + AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Cooked, Mode 2 Form 2", + realSector); + + break; + case 2336: + AaruConsole.DebugWriteLine("ISO9660 Plugin", + "Sector {0}, Cooked, Mode 2 Form {1}, File Number {2}, Channel Number {3}, Submode {4}, Coding Information {5}", + realSector, + ((Mode2Submode)data[2]).HasFlag(Mode2Submode.Form2) ? 2 : 1, + data[0], data[1], (Mode2Submode)data[2], data[3]); + + break; + case 2352 when data[0] != 0x00 || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || + data[4] != 0xFF || data[5] != 0xFF || data[6] != 0xFF || data[7] != 0xFF || + data[8] != 0xFF || data[9] != 0xFF || data[10] != 0xFF || data[11] != 0x00: + AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Raw, Audio", realSector); + + break; + case 2352 when data[15] != 2: + AaruConsole.DebugWriteLine("ISO9660 Plugin", + "Sector {0} ({1:X2}:{2:X2}:{3:X2}), Raw, Mode {4}", realSector, + data[12], data[13], data[14], data[15]); + + break; + case 2352: + AaruConsole.DebugWriteLine("ISO9660 Plugin", + "Sector {0} ({1:X2}:{2:X2}:{3:X2}), Raw, Mode 2 Form {4}, File Number {5}, Channel Number {6}, Submode {7}, Coding Information {8}", + realSector, data[12], data[13], data[14], + ((Mode2Submode)data[18]).HasFlag(Mode2Submode.Form2) ? 2 : 1, + data[16], data[17], (Mode2Submode)data[18], data[19]); + + break; + } + } + + if(_blockSize == 2048) + { + buffer = Sector.GetUserData(data, interleaved, fileNumber); + + return ErrorNumber.NoError; + } + + byte[] tmp = new byte[_blockSize]; + Array.Copy(Sector.GetUserData(data, interleaved, fileNumber), (int)offset, tmp, 0, _blockSize); + + buffer = tmp; + + return errno; + } + else + { + var ms = new MemoryStream(); + + for(uint i = 0; i < sectorCount; i++) + { + ulong dstSector = realSector + 1; + + errno = _image.ReadSectorLong(dstSector, out data); if(errno != ErrorNumber.NoError) - errno = _image.ReadSector(realSector, out data); + errno = _image.ReadSector(dstSector, out data); if(errno != ErrorNumber.NoError) return errno; @@ -74,19 +149,19 @@ namespace Aaru.Filesystems switch(data.Length) { case 2048: - AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Cooked, Mode 0/1 / Mode 2 Form 1", - realSector); + AaruConsole.DebugWriteLine("ISO9660 Plugin", + "Sector {0}, Cooked, Mode 0/1 / Mode 2 Form 1", dstSector); break; case 2324: AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Cooked, Mode 2 Form 2", - realSector); + dstSector); break; case 2336: AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Cooked, Mode 2 Form {1}, File Number {2}, Channel Number {3}, Submode {4}, Coding Information {5}", - realSector, + dstSector, ((Mode2Submode)data[2]).HasFlag(Mode2Submode.Form2) ? 2 : 1, data[0], data[1], (Mode2Submode)data[2], data[3]); @@ -94,19 +169,19 @@ namespace Aaru.Filesystems case 2352 when data[0] != 0x00 || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || data[4] != 0xFF || data[5] != 0xFF || data[6] != 0xFF || data[7] != 0xFF || data[8] != 0xFF || data[9] != 0xFF || data[10] != 0xFF || data[11] != 0x00: - AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Raw, Audio", realSector); + AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Raw, Audio", dstSector); break; case 2352 when data[15] != 2: AaruConsole.DebugWriteLine("ISO9660 Plugin", - "Sector {0} ({1:X2}:{2:X2}:{3:X2}), Raw, Mode {4}", realSector, - data[12], data[13], data[14], data[15]); + "Sector {0} ({1:X2}:{2:X2}:{3:X2}), Raw, Mode {4}", + dstSector, data[12], data[13], data[14], data[15]); break; case 2352: AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0} ({1:X2}:{2:X2}:{3:X2}), Raw, Mode 2 Form {4}, File Number {5}, Channel Number {6}, Submode {7}, Coding Information {8}", - realSector, data[12], data[13], data[14], + dstSector, data[12], data[13], data[14], ((Mode2Submode)data[18]).HasFlag(Mode2Submode.Form2) ? 2 : 1, data[16], data[17], (Mode2Submode)data[18], data[19]); @@ -114,92 +189,16 @@ namespace Aaru.Filesystems } } - if(_blockSize == 2048) - { - buffer = Sector.GetUserData(data, interleaved, fileNumber); + byte[] sectorData = Sector.GetUserData(data, interleaved, fileNumber); - return ErrorNumber.NoError; - } - - byte[] tmp = new byte[_blockSize]; - Array.Copy(Sector.GetUserData(data, interleaved, fileNumber), (int)offset, tmp, 0, _blockSize); - - buffer = tmp; - - return errno; + ms.Write(sectorData, 0, sectorData.Length); } - else - { - var ms = new MemoryStream(); - for(uint i = 0; i < sectorCount; i++) - { - ulong dstSector = realSector + 1; + byte[] tmp = new byte[_blockSize]; + Array.Copy(Sector.GetUserData(ms.ToArray(), interleaved, fileNumber), 0, tmp, 0, _blockSize); + buffer = tmp; - errno = _image.ReadSectorLong(dstSector, out data); - - if(errno != ErrorNumber.NoError) - errno = _image.ReadSector(dstSector, out data); - - if(errno != ErrorNumber.NoError) - return errno; - - if(_debug) - { - switch(data.Length) - { - case 2048: - AaruConsole.DebugWriteLine("ISO9660 Plugin", - "Sector {0}, Cooked, Mode 0/1 / Mode 2 Form 1", dstSector); - - break; - case 2324: - AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Cooked, Mode 2 Form 2", - dstSector); - - break; - case 2336: - AaruConsole.DebugWriteLine("ISO9660 Plugin", - "Sector {0}, Cooked, Mode 2 Form {1}, File Number {2}, Channel Number {3}, Submode {4}, Coding Information {5}", - dstSector, - ((Mode2Submode)data[2]).HasFlag(Mode2Submode.Form2) ? 2 : 1, - data[0], data[1], (Mode2Submode)data[2], data[3]); - - break; - case 2352 when data[0] != 0x00 || data[1] != 0xFF || data[2] != 0xFF || data[3] != 0xFF || - data[4] != 0xFF || data[5] != 0xFF || data[6] != 0xFF || data[7] != 0xFF || - data[8] != 0xFF || data[9] != 0xFF || data[10] != 0xFF || data[11] != 0x00: - AaruConsole.DebugWriteLine("ISO9660 Plugin", "Sector {0}, Raw, Audio", dstSector); - - break; - case 2352 when data[15] != 2: - AaruConsole.DebugWriteLine("ISO9660 Plugin", - "Sector {0} ({1:X2}:{2:X2}:{3:X2}), Raw, Mode {4}", - dstSector, data[12], data[13], data[14], data[15]); - - break; - case 2352: - AaruConsole.DebugWriteLine("ISO9660 Plugin", - "Sector {0} ({1:X2}:{2:X2}:{3:X2}), Raw, Mode 2 Form {4}, File Number {5}, Channel Number {6}, Submode {7}, Coding Information {8}", - dstSector, data[12], data[13], data[14], - ((Mode2Submode)data[18]).HasFlag(Mode2Submode.Form2) ? 2 : 1, - data[16], data[17], (Mode2Submode)data[18], data[19]); - - break; - } - } - - byte[] sectorData = Sector.GetUserData(data, interleaved, fileNumber); - - ms.Write(sectorData, 0, sectorData.Length); - } - - byte[] tmp = new byte[_blockSize]; - Array.Copy(Sector.GetUserData(ms.ToArray(), interleaved, fileNumber), 0, tmp, 0, _blockSize); - buffer = tmp; - - return ErrorNumber.NoError; - } + return ErrorNumber.NoError; } } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/PathTable.cs b/Aaru.Filesystems/ISO9660/PathTable.cs index 95c443fcf..1f864506d 100644 --- a/Aaru.Filesystems/ISO9660/PathTable.cs +++ b/Aaru.Filesystems/ISO9660/PathTable.cs @@ -34,94 +34,93 @@ using System.Collections.Generic; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + PathTableEntryInternal[] DecodePathTable(byte[] data) { - PathTableEntryInternal[] DecodePathTable(byte[] data) + if(data is null || data.Length == 0) + return null; + + List table = new List(); + + int off = 0; + + PathTableEntry entry = + Marshal.ByteArrayToStructureBigEndian(data, off, Marshal.SizeOf()); + + if(entry.name_len != 1 || + entry.parent_dirno != 1 || + data.Length <= Marshal.SizeOf() || + data[Marshal.SizeOf()] != 0x00) + return null; + + while(off < data.Length) { - if(data is null || data.Length == 0) - return null; - - List table = new List(); - - int off = 0; - - PathTableEntry entry = + entry = Marshal.ByteArrayToStructureBigEndian(data, off, Marshal.SizeOf()); - if(entry.name_len != 1 || - entry.parent_dirno != 1 || - data.Length <= Marshal.SizeOf() || - data[Marshal.SizeOf()] != 0x00) - return null; + if(entry.name_len == 0) + break; - while(off < data.Length) + off += Marshal.SizeOf(); + + string name = Encoding.GetString(data, off, entry.name_len); + + table.Add(new PathTableEntryInternal { - entry = - Marshal.ByteArrayToStructureBigEndian(data, off, Marshal.SizeOf()); + Extent = entry.start_lbn, + Name = name, + Parent = entry.parent_dirno, + XattrLength = entry.xattr_len + }); - if(entry.name_len == 0) - break; + off += entry.name_len; - off += Marshal.SizeOf(); - - string name = Encoding.GetString(data, off, entry.name_len); - - table.Add(new PathTableEntryInternal - { - Extent = entry.start_lbn, - Name = name, - Parent = entry.parent_dirno, - XattrLength = entry.xattr_len - }); - - off += entry.name_len; - - if(entry.name_len % 2 != 0) - off++; - } - - return table.ToArray(); + if(entry.name_len % 2 != 0) + off++; } - PathTableEntryInternal[] DecodeHighSierraPathTable(byte[] data) + return table.ToArray(); + } + + PathTableEntryInternal[] DecodeHighSierraPathTable(byte[] data) + { + if(data is null) + return null; + + List table = new List(); + + int off = 0; + + while(off < data.Length) { - if(data is null) - return null; + HighSierraPathTableEntry entry = + Marshal.ByteArrayToStructureBigEndian(data, off, + Marshal.SizeOf()); - List table = new List(); + if(entry.name_len == 0) + break; - int off = 0; + off += Marshal.SizeOf(); - while(off < data.Length) + string name = Encoding.GetString(data, off, entry.name_len); + + table.Add(new PathTableEntryInternal { - HighSierraPathTableEntry entry = - Marshal.ByteArrayToStructureBigEndian(data, off, - Marshal.SizeOf()); + Extent = entry.start_lbn, + Name = name, + Parent = entry.parent_dirno, + XattrLength = entry.xattr_len + }); - if(entry.name_len == 0) - break; + off += entry.name_len; - off += Marshal.SizeOf(); - - string name = Encoding.GetString(data, off, entry.name_len); - - table.Add(new PathTableEntryInternal - { - Extent = entry.start_lbn, - Name = name, - Parent = entry.parent_dirno, - XattrLength = entry.xattr_len - }); - - off += entry.name_len; - - if(entry.name_len % 2 != 0) - off++; - } - - return table.ToArray(); + if(entry.name_len % 2 != 0) + off++; } + + return table.ToArray(); } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/Amiga.cs b/Aaru.Filesystems/ISO9660/Structs/Amiga.cs index 52070169c..dd79add7c 100644 --- a/Aaru.Filesystems/ISO9660/Structs/Amiga.cs +++ b/Aaru.Filesystems/ISO9660/Structs/Amiga.cs @@ -33,29 +33,28 @@ using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AmigaEntry { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AmigaEntry - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly AmigaFlags flags; + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly AmigaFlags flags; - // Followed by AmigaProtection if present - // Followed by length-prefixed string for comment if present - } + // Followed by AmigaProtection if present + // Followed by length-prefixed string for comment if present + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AmigaProtection - { - public readonly byte User; - public readonly byte Reserved; - public readonly AmigaMultiuser Multiuser; - public readonly AmigaAttributes Protection; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AmigaProtection + { + public readonly byte User; + public readonly byte Reserved; + public readonly AmigaMultiuser Multiuser; + public readonly AmigaAttributes Protection; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/Apple.cs b/Aaru.Filesystems/ISO9660/Structs/Apple.cs index d9b3d0657..b41b345e0 100644 --- a/Aaru.Filesystems/ISO9660/Structs/Apple.cs +++ b/Aaru.Filesystems/ISO9660/Structs/Apple.cs @@ -33,74 +33,73 @@ using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + // Little-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AppleProDOSSystemUse { - // Little-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AppleProDOSSystemUse - { - public readonly ushort signature; - public readonly byte length; - public readonly AppleId id; - public readonly byte type; - public readonly ushort aux_type; - } + public readonly ushort signature; + public readonly byte length; + public readonly AppleId id; + public readonly byte type; + public readonly ushort aux_type; + } - // Big-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AppleHFSSystemUse - { - public readonly ushort signature; - public readonly byte length; - public readonly AppleId id; - public readonly uint type; - public readonly uint creator; - public readonly AppleCommon.FinderFlags finder_flags; - } + // Big-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AppleHFSSystemUse + { + public readonly ushort signature; + public readonly byte length; + public readonly AppleId id; + public readonly uint type; + public readonly uint creator; + public readonly AppleCommon.FinderFlags finder_flags; + } - // Little-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AppleProDOSOldSystemUse - { - public readonly ushort signature; - public readonly AppleOldId id; - public readonly byte type; - public readonly ushort aux_type; - } + // Little-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AppleProDOSOldSystemUse + { + public readonly ushort signature; + public readonly AppleOldId id; + public readonly byte type; + public readonly ushort aux_type; + } - // Big-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AppleHFSTypeCreatorSystemUse - { - public readonly ushort signature; - public readonly AppleOldId id; - public readonly uint type; - public readonly uint creator; - } + // Big-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AppleHFSTypeCreatorSystemUse + { + public readonly ushort signature; + public readonly AppleOldId id; + public readonly uint type; + public readonly uint creator; + } - // Big-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AppleHFSIconSystemUse - { - public readonly ushort signature; - public readonly AppleOldId id; - public readonly uint type; - public readonly uint creator; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] icon; - } + // Big-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AppleHFSIconSystemUse + { + public readonly ushort signature; + public readonly AppleOldId id; + public readonly uint type; + public readonly uint creator; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] icon; + } - // Big-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AppleHFSOldSystemUse - { - public readonly ushort signature; - public readonly AppleOldId id; - public readonly uint type; - public readonly uint creator; - public readonly ushort finder_flags; - } + // Big-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AppleHFSOldSystemUse + { + public readonly ushort signature; + public readonly AppleOldId id; + public readonly uint type; + public readonly uint creator; + public readonly ushort finder_flags; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/CDi.cs b/Aaru.Filesystems/ISO9660/Structs/CDi.cs index 87c8da9ae..041286f1f 100644 --- a/Aaru.Filesystems/ISO9660/Structs/CDi.cs +++ b/Aaru.Filesystems/ISO9660/Structs/CDi.cs @@ -35,162 +35,161 @@ using System; using System.Runtime.InteropServices; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + static DecodedVolumeDescriptor DecodeVolumeDescriptor(FileStructureVolumeDescriptor pvd) { - static DecodedVolumeDescriptor DecodeVolumeDescriptor(FileStructureVolumeDescriptor pvd) + var decodedVd = new DecodedVolumeDescriptor { - var decodedVd = new DecodedVolumeDescriptor - { - SystemIdentifier = StringHandlers.CToString(pvd.system_id).TrimEnd(), - VolumeIdentifier = StringHandlers.CToString(pvd.volume_id).TrimEnd(), - VolumeSetIdentifier = StringHandlers.CToString(pvd.volume_set_id).TrimEnd(), - PublisherIdentifier = StringHandlers.CToString(pvd.publisher_id).TrimEnd(), - DataPreparerIdentifier = StringHandlers.CToString(pvd.preparer_id).TrimEnd(), - ApplicationIdentifier = StringHandlers.CToString(pvd.application_data).TrimEnd() - }; + SystemIdentifier = StringHandlers.CToString(pvd.system_id).TrimEnd(), + VolumeIdentifier = StringHandlers.CToString(pvd.volume_id).TrimEnd(), + VolumeSetIdentifier = StringHandlers.CToString(pvd.volume_set_id).TrimEnd(), + PublisherIdentifier = StringHandlers.CToString(pvd.publisher_id).TrimEnd(), + DataPreparerIdentifier = StringHandlers.CToString(pvd.preparer_id).TrimEnd(), + ApplicationIdentifier = StringHandlers.CToString(pvd.application_data).TrimEnd() + }; - if(pvd.creation_date[0] == '0' || - pvd.creation_date[0] == 0x00) - decodedVd.CreationTime = DateTime.MinValue; - else - decodedVd.CreationTime = DateHandlers.HighSierraToDateTime(pvd.creation_date); + if(pvd.creation_date[0] == '0' || + pvd.creation_date[0] == 0x00) + decodedVd.CreationTime = DateTime.MinValue; + else + decodedVd.CreationTime = DateHandlers.HighSierraToDateTime(pvd.creation_date); - if(pvd.modification_date[0] == '0' || - pvd.modification_date[0] == 0x00) - decodedVd.HasModificationTime = false; - else - { - decodedVd.HasModificationTime = true; - decodedVd.ModificationTime = DateHandlers.HighSierraToDateTime(pvd.modification_date); - } - - if(pvd.expiration_date[0] == '0' || - pvd.expiration_date[0] == 0x00) - decodedVd.HasExpirationTime = false; - else - { - decodedVd.HasExpirationTime = true; - decodedVd.ExpirationTime = DateHandlers.HighSierraToDateTime(pvd.expiration_date); - } - - if(pvd.effective_date[0] == '0' || - pvd.effective_date[0] == 0x00) - decodedVd.HasEffectiveTime = false; - else - { - decodedVd.HasEffectiveTime = true; - decodedVd.EffectiveTime = DateHandlers.HighSierraToDateTime(pvd.effective_date); - } - - decodedVd.Blocks = pvd.volume_space_size; - decodedVd.BlockSize = pvd.logical_block_size; - - return decodedVd; + if(pvd.modification_date[0] == '0' || + pvd.modification_date[0] == 0x00) + decodedVd.HasModificationTime = false; + else + { + decodedVd.HasModificationTime = true; + decodedVd.ModificationTime = DateHandlers.HighSierraToDateTime(pvd.modification_date); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct FileStructureVolumeDescriptor + if(pvd.expiration_date[0] == '0' || + pvd.expiration_date[0] == 0x00) + decodedVd.HasExpirationTime = false; + else { - public readonly byte type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] id; - public readonly byte version; - public readonly CdiVolumeFlags flags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] system_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] volume_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] reserved1; - public readonly uint volume_space_size; - - // Only used in SVDs - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] escape_sequences; - public readonly ushort reserved2; - public readonly ushort volume_set_size; - public readonly ushort reserved3; - public readonly ushort volume_sequence_number; - public readonly ushort reserved4; - public readonly ushort logical_block_size; - public readonly uint reserved5; - public readonly uint path_table_size; - public readonly ulong reserved6; - public readonly uint path_table_addr; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 38)] - public readonly byte[] reserved7; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] volume_set_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] publisher_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] preparer_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] application_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] copyright_file_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] reserved8; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] abstract_file_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] reserved9; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] bibliographic_file_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] reserved10; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] creation_date; - public readonly byte reserved11; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] modification_date; - public readonly byte reserved12; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] expiration_date; - public readonly byte reserved13; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] effective_date; - public readonly byte reserved14; - public readonly byte file_structure_version; - public readonly byte reserved15; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] - public readonly byte[] application_data; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 653)] - public readonly byte[] reserved16; + decodedVd.HasExpirationTime = true; + decodedVd.ExpirationTime = DateHandlers.HighSierraToDateTime(pvd.expiration_date); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CdiDirectoryRecord + if(pvd.effective_date[0] == '0' || + pvd.effective_date[0] == 0x00) + decodedVd.HasEffectiveTime = false; + else { - public readonly byte length; - public readonly byte xattr_len; - public readonly uint reserved1; - public readonly uint start_lbn; - public readonly uint reserved2; - public readonly uint size; - public readonly HighSierraTimestamp date; - public readonly byte reserved3; - public readonly CdiFileFlags flags; - public readonly ushort file_unit_size; - public readonly ushort reserved4; - public readonly ushort volume_sequence_number; - public readonly byte name_len; - - // Followed by name[name_len] and then CdiSystemArea until length arrives + decodedVd.HasEffectiveTime = true; + decodedVd.EffectiveTime = DateHandlers.HighSierraToDateTime(pvd.effective_date); } - // Follows filename on directory record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CdiSystemArea - { - public readonly ushort group; - public readonly ushort owner; - public readonly CdiAttributes attributes; - public readonly ushort reserved1; - public readonly byte file_no; - public readonly byte reserved2; - } + decodedVd.Blocks = pvd.volume_space_size; + decodedVd.BlockSize = pvd.logical_block_size; + + return decodedVd; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct FileStructureVolumeDescriptor + { + public readonly byte type; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] id; + public readonly byte version; + public readonly CdiVolumeFlags flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] system_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] volume_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] reserved1; + public readonly uint volume_space_size; + + // Only used in SVDs + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] escape_sequences; + public readonly ushort reserved2; + public readonly ushort volume_set_size; + public readonly ushort reserved3; + public readonly ushort volume_sequence_number; + public readonly ushort reserved4; + public readonly ushort logical_block_size; + public readonly uint reserved5; + public readonly uint path_table_size; + public readonly ulong reserved6; + public readonly uint path_table_addr; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 38)] + public readonly byte[] reserved7; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] volume_set_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] publisher_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] preparer_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] application_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] copyright_file_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] reserved8; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] abstract_file_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] reserved9; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] bibliographic_file_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] reserved10; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] creation_date; + public readonly byte reserved11; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] modification_date; + public readonly byte reserved12; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] expiration_date; + public readonly byte reserved13; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] effective_date; + public readonly byte reserved14; + public readonly byte file_structure_version; + public readonly byte reserved15; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] + public readonly byte[] application_data; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 653)] + public readonly byte[] reserved16; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CdiDirectoryRecord + { + public readonly byte length; + public readonly byte xattr_len; + public readonly uint reserved1; + public readonly uint start_lbn; + public readonly uint reserved2; + public readonly uint size; + public readonly HighSierraTimestamp date; + public readonly byte reserved3; + public readonly CdiFileFlags flags; + public readonly ushort file_unit_size; + public readonly ushort reserved4; + public readonly ushort volume_sequence_number; + public readonly byte name_len; + + // Followed by name[name_len] and then CdiSystemArea until length arrives + } + + // Follows filename on directory record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CdiSystemArea + { + public readonly ushort group; + public readonly ushort owner; + public readonly CdiAttributes attributes; + public readonly ushort reserved1; + public readonly byte file_no; + public readonly byte reserved2; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/ElTorito.cs b/Aaru.Filesystems/ISO9660/Structs/ElTorito.cs index 81e22784b..dcff144aa 100644 --- a/Aaru.Filesystems/ISO9660/Structs/ElTorito.cs +++ b/Aaru.Filesystems/ISO9660/Structs/ElTorito.cs @@ -33,84 +33,83 @@ using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ElToritoBootRecord { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ElToritoBootRecord - { - public readonly byte type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] id; - public readonly byte version; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] system_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] boot_id; - public readonly uint catalog_sector; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1974)] - public readonly byte[] boot_use; - } + public readonly byte type; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] id; + public readonly byte version; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] system_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] boot_id; + public readonly uint catalog_sector; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1974)] + public readonly byte[] boot_use; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ElToritoValidationEntry - { - public readonly ElToritoIndicator header_id; - public readonly ElToritoPlatform platform_id; - public readonly ushort reserved; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] - public readonly byte[] developer_id; - public readonly ushort checksum; - public readonly ushort signature; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ElToritoValidationEntry + { + public readonly ElToritoIndicator header_id; + public readonly ElToritoPlatform platform_id; + public readonly ushort reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public readonly byte[] developer_id; + public readonly ushort checksum; + public readonly ushort signature; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ElToritoInitialEntry - { - public readonly ElToritoIndicator bootable; - public ElToritoEmulation boot_type; - public readonly ushort load_seg; - public readonly byte system_type; - public readonly byte reserved1; - public readonly ushort sector_count; - public readonly uint load_rba; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly byte[] reserved2; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ElToritoInitialEntry + { + public readonly ElToritoIndicator bootable; + public ElToritoEmulation boot_type; + public readonly ushort load_seg; + public readonly byte system_type; + public readonly byte reserved1; + public readonly ushort sector_count; + public readonly uint load_rba; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly byte[] reserved2; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ElToritoSectionHeaderEntry - { - public readonly ElToritoIndicator header_id; - public readonly ElToritoPlatform platform_id; - public readonly ushort entries; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] - public readonly byte[] identifier; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ElToritoSectionHeaderEntry + { + public readonly ElToritoIndicator header_id; + public readonly ElToritoPlatform platform_id; + public readonly ushort entries; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] + public readonly byte[] identifier; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ElToritoSectionEntry - { - public readonly ElToritoIndicator bootable; - public readonly ElToritoEmulation boot_type; - public readonly ushort load_seg; - public readonly byte system_type; - public readonly byte reserved1; - public readonly ushort sector_count; - public readonly uint load_rba; - public readonly byte selection_criteria_type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)] - public readonly byte[] selection_criterias; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ElToritoSectionEntry + { + public readonly ElToritoIndicator bootable; + public readonly ElToritoEmulation boot_type; + public readonly ushort load_seg; + public readonly byte system_type; + public readonly byte reserved1; + public readonly ushort sector_count; + public readonly uint load_rba; + public readonly byte selection_criteria_type; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 19)] + public readonly byte[] selection_criterias; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ElToritoSectionEntryExtension - { - public readonly ElToritoIndicator extension_indicator; - public readonly ElToritoFlags extension_flags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] - public readonly byte[] selection_criterias; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ElToritoSectionEntryExtension + { + public readonly ElToritoIndicator extension_indicator; + public readonly ElToritoFlags extension_flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 30)] + public readonly byte[] selection_criterias; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/HighSierra.cs b/Aaru.Filesystems/ISO9660/Structs/HighSierra.cs index 3a5c32a50..453d89d8d 100644 --- a/Aaru.Filesystems/ISO9660/Structs/HighSierra.cs +++ b/Aaru.Filesystems/ISO9660/Structs/HighSierra.cs @@ -36,170 +36,169 @@ using System.Runtime.InteropServices; using System.Text; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + static DecodedVolumeDescriptor DecodeVolumeDescriptor(HighSierraPrimaryVolumeDescriptor pvd) { - static DecodedVolumeDescriptor DecodeVolumeDescriptor(HighSierraPrimaryVolumeDescriptor pvd) + var decodedVd = new DecodedVolumeDescriptor { - var decodedVd = new DecodedVolumeDescriptor - { - SystemIdentifier = Encoding.ASCII.GetString(pvd.system_id).TrimEnd().Trim('\0'), - VolumeIdentifier = Encoding.ASCII.GetString(pvd.volume_id).TrimEnd().Trim('\0'), - VolumeSetIdentifier = Encoding.ASCII.GetString(pvd.volume_set_id).TrimEnd().Trim('\0'), - PublisherIdentifier = Encoding.ASCII.GetString(pvd.publisher_id).TrimEnd().Trim('\0'), - DataPreparerIdentifier = Encoding.ASCII.GetString(pvd.preparer_id).TrimEnd().Trim('\0'), - ApplicationIdentifier = Encoding.ASCII.GetString(pvd.application_data).TrimEnd().Trim('\0') - }; + SystemIdentifier = Encoding.ASCII.GetString(pvd.system_id).TrimEnd().Trim('\0'), + VolumeIdentifier = Encoding.ASCII.GetString(pvd.volume_id).TrimEnd().Trim('\0'), + VolumeSetIdentifier = Encoding.ASCII.GetString(pvd.volume_set_id).TrimEnd().Trim('\0'), + PublisherIdentifier = Encoding.ASCII.GetString(pvd.publisher_id).TrimEnd().Trim('\0'), + DataPreparerIdentifier = Encoding.ASCII.GetString(pvd.preparer_id).TrimEnd().Trim('\0'), + ApplicationIdentifier = Encoding.ASCII.GetString(pvd.application_data).TrimEnd().Trim('\0') + }; - if(pvd.creation_date[0] == '0' || - pvd.creation_date[0] == 0x00) - decodedVd.CreationTime = DateTime.MinValue; - else - decodedVd.CreationTime = DateHandlers.HighSierraToDateTime(pvd.creation_date); + if(pvd.creation_date[0] == '0' || + pvd.creation_date[0] == 0x00) + decodedVd.CreationTime = DateTime.MinValue; + else + decodedVd.CreationTime = DateHandlers.HighSierraToDateTime(pvd.creation_date); - if(pvd.modification_date[0] == '0' || - pvd.modification_date[0] == 0x00) - decodedVd.HasModificationTime = false; - else - { - decodedVd.HasModificationTime = true; - decodedVd.ModificationTime = DateHandlers.HighSierraToDateTime(pvd.modification_date); - } - - if(pvd.expiration_date[0] == '0' || - pvd.expiration_date[0] == 0x00) - decodedVd.HasExpirationTime = false; - else - { - decodedVd.HasExpirationTime = true; - decodedVd.ExpirationTime = DateHandlers.HighSierraToDateTime(pvd.expiration_date); - } - - if(pvd.effective_date[0] == '0' || - pvd.effective_date[0] == 0x00) - decodedVd.HasEffectiveTime = false; - else - { - decodedVd.HasEffectiveTime = true; - decodedVd.EffectiveTime = DateHandlers.HighSierraToDateTime(pvd.effective_date); - } - - decodedVd.Blocks = pvd.volume_space_size; - decodedVd.BlockSize = pvd.logical_block_size; - - return decodedVd; + if(pvd.modification_date[0] == '0' || + pvd.modification_date[0] == 0x00) + decodedVd.HasModificationTime = false; + else + { + decodedVd.HasModificationTime = true; + decodedVd.ModificationTime = DateHandlers.HighSierraToDateTime(pvd.modification_date); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HighSierraPrimaryVolumeDescriptor + if(pvd.expiration_date[0] == '0' || + pvd.expiration_date[0] == 0x00) + decodedVd.HasExpirationTime = false; + else { - public readonly uint volume_lbn; - public readonly uint volume_lbn_be; - public readonly byte type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] id; - public readonly byte version; - - // Only used in SVDs - public readonly byte flags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] system_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] volume_id; - public readonly ulong reserved1; - public readonly uint volume_space_size; - public readonly uint volume_space_size_be; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] escape_sequences; - public readonly ushort volume_set_size; - public readonly ushort volume_set_size_be; - public readonly ushort volume_sequence_number; - public readonly ushort volume_sequence_number_be; - public readonly ushort logical_block_size; - public readonly ushort logical_block_size_be; - public readonly uint path_table_size; - public readonly uint path_table_size_be; - public readonly uint mandatory_path_table_lsb; - public readonly uint opt_path_table_lsb_1; - public readonly uint opt_path_table_lsb_2; - public readonly uint opt_path_table_lsb_3; - public readonly uint mandatory_path_table_msb; - public readonly uint opt_path_table_msb_1; - public readonly uint opt_path_table_msb_2; - public readonly uint opt_path_table_msb_3; - public readonly HighSierraDirectoryRecord root_directory_record; - public readonly byte root_directory_name; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] volume_set_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] publisher_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] preparer_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] application_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] copyright_file_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] abstract_file_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] creation_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] modification_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] expiration_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] effective_date; - public readonly byte file_structure_version; - public readonly byte reserved2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] - public readonly byte[] application_data; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 680)] - public readonly byte[] reserved3; + decodedVd.HasExpirationTime = true; + decodedVd.ExpirationTime = DateHandlers.HighSierraToDateTime(pvd.expiration_date); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HighSierraDirectoryRecord + if(pvd.effective_date[0] == '0' || + pvd.effective_date[0] == 0x00) + decodedVd.HasEffectiveTime = false; + else { - public readonly byte length; - public readonly byte xattr_len; - public readonly uint extent; - public readonly uint extent_be; - public readonly uint size; - public readonly uint size_be; - public readonly HighSierraTimestamp date; - public readonly FileFlags flags; - public readonly byte reserved; - public readonly byte interleave_size; - public readonly byte interleave; - public readonly ushort volume_sequence_number; - public readonly ushort volume_sequence_number_be; - public readonly byte name_len; - - // Followed by name[name_len] and then system area until length arrives + decodedVd.HasEffectiveTime = true; + decodedVd.EffectiveTime = DateHandlers.HighSierraToDateTime(pvd.effective_date); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HighSierraTimestamp - { - public readonly byte Years; - public readonly byte Month; - public readonly byte Day; - public readonly byte Hour; - public readonly byte Minute; - public readonly byte Second; - } + decodedVd.Blocks = pvd.volume_space_size; + decodedVd.BlockSize = pvd.logical_block_size; - // There are two tables one in little endian one in big endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HighSierraPathTableEntry - { - public readonly uint start_lbn; - public readonly byte xattr_len; - public readonly byte name_len; - public readonly ushort parent_dirno; + return decodedVd; + } - // Followed by name[name_len] - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HighSierraPrimaryVolumeDescriptor + { + public readonly uint volume_lbn; + public readonly uint volume_lbn_be; + public readonly byte type; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] id; + public readonly byte version; + + // Only used in SVDs + public readonly byte flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] system_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] volume_id; + public readonly ulong reserved1; + public readonly uint volume_space_size; + public readonly uint volume_space_size_be; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] escape_sequences; + public readonly ushort volume_set_size; + public readonly ushort volume_set_size_be; + public readonly ushort volume_sequence_number; + public readonly ushort volume_sequence_number_be; + public readonly ushort logical_block_size; + public readonly ushort logical_block_size_be; + public readonly uint path_table_size; + public readonly uint path_table_size_be; + public readonly uint mandatory_path_table_lsb; + public readonly uint opt_path_table_lsb_1; + public readonly uint opt_path_table_lsb_2; + public readonly uint opt_path_table_lsb_3; + public readonly uint mandatory_path_table_msb; + public readonly uint opt_path_table_msb_1; + public readonly uint opt_path_table_msb_2; + public readonly uint opt_path_table_msb_3; + public readonly HighSierraDirectoryRecord root_directory_record; + public readonly byte root_directory_name; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] volume_set_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] publisher_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] preparer_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] application_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] copyright_file_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] abstract_file_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] creation_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] modification_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] expiration_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] effective_date; + public readonly byte file_structure_version; + public readonly byte reserved2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] + public readonly byte[] application_data; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 680)] + public readonly byte[] reserved3; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HighSierraDirectoryRecord + { + public readonly byte length; + public readonly byte xattr_len; + public readonly uint extent; + public readonly uint extent_be; + public readonly uint size; + public readonly uint size_be; + public readonly HighSierraTimestamp date; + public readonly FileFlags flags; + public readonly byte reserved; + public readonly byte interleave_size; + public readonly byte interleave; + public readonly ushort volume_sequence_number; + public readonly ushort volume_sequence_number_be; + public readonly byte name_len; + + // Followed by name[name_len] and then system area until length arrives + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HighSierraTimestamp + { + public readonly byte Years; + public readonly byte Month; + public readonly byte Day; + public readonly byte Hour; + public readonly byte Minute; + public readonly byte Second; + } + + // There are two tables one in little endian one in big endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HighSierraPathTableEntry + { + public readonly uint start_lbn; + public readonly byte xattr_len; + public readonly byte name_len; + public readonly ushort parent_dirno; + + // Followed by name[name_len] } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/ISO.cs b/Aaru.Filesystems/ISO9660/Structs/ISO.cs index 40de8f554..391747672 100644 --- a/Aaru.Filesystems/ISO9660/Structs/ISO.cs +++ b/Aaru.Filesystems/ISO9660/Structs/ISO.cs @@ -38,237 +38,236 @@ using Aaru.Helpers; // ReSharper disable UnusedType.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + static DecodedVolumeDescriptor DecodeVolumeDescriptor(PrimaryVolumeDescriptor pvd, Encoding encoding = null) { - static DecodedVolumeDescriptor DecodeVolumeDescriptor(PrimaryVolumeDescriptor pvd, Encoding encoding = null) + encoding ??= Encoding.ASCII; + + var decodedVd = new DecodedVolumeDescriptor { - encoding ??= Encoding.ASCII; + SystemIdentifier = StringHandlers.CToString(pvd.system_id, encoding).TrimEnd(), + VolumeIdentifier = StringHandlers.CToString(pvd.volume_id, encoding).TrimEnd(), + VolumeSetIdentifier = StringHandlers.CToString(pvd.volume_set_id, encoding).TrimEnd(), + PublisherIdentifier = StringHandlers.CToString(pvd.publisher_id, encoding).TrimEnd(), + DataPreparerIdentifier = StringHandlers.CToString(pvd.preparer_id, encoding).TrimEnd(), + ApplicationIdentifier = StringHandlers.CToString(pvd.application_id, encoding).TrimEnd() + }; - var decodedVd = new DecodedVolumeDescriptor - { - SystemIdentifier = StringHandlers.CToString(pvd.system_id, encoding).TrimEnd(), - VolumeIdentifier = StringHandlers.CToString(pvd.volume_id, encoding).TrimEnd(), - VolumeSetIdentifier = StringHandlers.CToString(pvd.volume_set_id, encoding).TrimEnd(), - PublisherIdentifier = StringHandlers.CToString(pvd.publisher_id, encoding).TrimEnd(), - DataPreparerIdentifier = StringHandlers.CToString(pvd.preparer_id, encoding).TrimEnd(), - ApplicationIdentifier = StringHandlers.CToString(pvd.application_id, encoding).TrimEnd() - }; + if(pvd.creation_date[0] == '0' || + pvd.creation_date[0] == 0x00) + decodedVd.CreationTime = DateTime.MinValue; + else + decodedVd.CreationTime = DateHandlers.Iso9660ToDateTime(pvd.creation_date); - if(pvd.creation_date[0] == '0' || - pvd.creation_date[0] == 0x00) - decodedVd.CreationTime = DateTime.MinValue; - else - decodedVd.CreationTime = DateHandlers.Iso9660ToDateTime(pvd.creation_date); - - if(pvd.modification_date[0] == '0' || - pvd.modification_date[0] == 0x00) - decodedVd.HasModificationTime = false; - else - { - decodedVd.HasModificationTime = true; - decodedVd.ModificationTime = DateHandlers.Iso9660ToDateTime(pvd.modification_date); - } - - if(pvd.expiration_date[0] == '0' || - pvd.expiration_date[0] == 0x00) - decodedVd.HasExpirationTime = false; - else - { - decodedVd.HasExpirationTime = true; - decodedVd.ExpirationTime = DateHandlers.Iso9660ToDateTime(pvd.expiration_date); - } - - if(pvd.effective_date[0] == '0' || - pvd.effective_date[0] == 0x00) - decodedVd.HasEffectiveTime = false; - else - { - decodedVd.HasEffectiveTime = true; - decodedVd.EffectiveTime = DateHandlers.Iso9660ToDateTime(pvd.effective_date); - } - - decodedVd.Blocks = pvd.volume_space_size; - decodedVd.BlockSize = pvd.logical_block_size; - - return decodedVd; + if(pvd.modification_date[0] == '0' || + pvd.modification_date[0] == 0x00) + decodedVd.HasModificationTime = false; + else + { + decodedVd.HasModificationTime = true; + decodedVd.ModificationTime = DateHandlers.Iso9660ToDateTime(pvd.modification_date); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PrimaryVolumeDescriptor + if(pvd.expiration_date[0] == '0' || + pvd.expiration_date[0] == 0x00) + decodedVd.HasExpirationTime = false; + else { - public readonly byte type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] id; - public readonly byte version; - - // Only used in SVDs - public readonly byte flags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] system_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] volume_id; - public readonly ulong reserved1; - public readonly uint volume_space_size; - public readonly uint volume_space_size_be; - - // Only used in SVDs - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] escape_sequences; - public readonly ushort volume_set_size; - public readonly ushort volume_set_size_be; - public readonly ushort volume_sequence_number; - public readonly ushort volume_sequence_number_be; - public readonly ushort logical_block_size; - public readonly ushort logical_block_size_be; - public readonly uint path_table_size; - public readonly uint path_table_size_be; - public readonly uint type_l_path_table; - public readonly uint opt_type_l_path_table; - public readonly uint type_m_path_table; - public readonly uint opt_type_m_path_table; - public readonly DirectoryRecord root_directory_record; - public readonly byte root_directory_name; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] volume_set_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] publisher_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] preparer_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] application_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)] - public readonly byte[] copyright_file_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)] - public readonly byte[] abstract_file_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)] - public readonly byte[] bibliographic_file_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly byte[] creation_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly byte[] modification_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly byte[] expiration_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly byte[] effective_date; - public readonly byte file_structure_version; - public readonly byte reserved2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] - public readonly byte[] application_data; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 653)] - public readonly byte[] reserved3; + decodedVd.HasExpirationTime = true; + decodedVd.ExpirationTime = DateHandlers.Iso9660ToDateTime(pvd.expiration_date); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BootRecord + if(pvd.effective_date[0] == '0' || + pvd.effective_date[0] == 0x00) + decodedVd.HasEffectiveTime = false; + else { - public readonly byte type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] id; - public readonly byte version; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] system_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] boot_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1977)] - public readonly byte[] boot_use; + decodedVd.HasEffectiveTime = true; + decodedVd.EffectiveTime = DateHandlers.Iso9660ToDateTime(pvd.effective_date); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PartitionDescriptor - { - public readonly byte type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] id; - public readonly byte version; - public readonly byte reserved1; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] system_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] partition_id; - public readonly uint partition_location; - public readonly uint partition_location_be; - public readonly uint partition_size; - public readonly uint partition_size_be; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1960)] - public readonly byte[] system_use; - } + decodedVd.Blocks = pvd.volume_space_size; + decodedVd.BlockSize = pvd.logical_block_size; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryRecord - { - public readonly byte length; - public readonly byte xattr_len; - public readonly uint extent; - public readonly uint extent_be; - public readonly uint size; - public readonly uint size_be; - public readonly IsoTimestamp date; - public readonly FileFlags flags; - public readonly byte file_unit_size; - public readonly byte interleave; - public readonly ushort volume_sequence_number; - public readonly ushort volume_sequence_number_be; - public readonly byte name_len; + return decodedVd; + } - // Followed by name[name_len] and then system area until length arrives - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PrimaryVolumeDescriptor + { + public readonly byte type; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] id; + public readonly byte version; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ExtendedAttributeRecord - { - public readonly ushort owner; - public readonly ushort owner_be; - public readonly ushort group; - public readonly ushort group_be; - public readonly Permissions permissions; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly byte[] creation_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly byte[] modification_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly byte[] expiration_date; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly byte[] effective_date; - public readonly RecordFormat record_format; - public readonly RecordAttribute record_attributes; - public readonly ushort record_length; - public readonly ushort record_length_be; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] system_id; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] system_use; - public readonly byte record_version; - public readonly byte escape_len; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] reserved1; - public readonly ushort app_use_len; - public readonly ushort app_use_len_be; - } + // Only used in SVDs + public readonly byte flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] system_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] volume_id; + public readonly ulong reserved1; + public readonly uint volume_space_size; + public readonly uint volume_space_size_be; - // There are two tables one in little endian one in big endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PathTableEntry - { - public readonly byte name_len; - public readonly byte xattr_len; - public readonly uint start_lbn; - public readonly ushort parent_dirno; + // Only used in SVDs + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] escape_sequences; + public readonly ushort volume_set_size; + public readonly ushort volume_set_size_be; + public readonly ushort volume_sequence_number; + public readonly ushort volume_sequence_number_be; + public readonly ushort logical_block_size; + public readonly ushort logical_block_size_be; + public readonly uint path_table_size; + public readonly uint path_table_size_be; + public readonly uint type_l_path_table; + public readonly uint opt_type_l_path_table; + public readonly uint type_m_path_table; + public readonly uint opt_type_m_path_table; + public readonly DirectoryRecord root_directory_record; + public readonly byte root_directory_name; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] volume_set_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] publisher_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] preparer_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] application_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)] + public readonly byte[] copyright_file_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)] + public readonly byte[] abstract_file_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 37)] + public readonly byte[] bibliographic_file_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly byte[] creation_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly byte[] modification_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly byte[] expiration_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly byte[] effective_date; + public readonly byte file_structure_version; + public readonly byte reserved2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] + public readonly byte[] application_data; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 653)] + public readonly byte[] reserved3; + } - // Followed by name[name_len] - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BootRecord + { + public readonly byte type; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] id; + public readonly byte version; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] system_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] boot_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1977)] + public readonly byte[] boot_use; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct IsoTimestamp - { - public readonly byte Years; - public readonly byte Month; - public readonly byte Day; - public readonly byte Hour; - public readonly byte Minute; - public readonly byte Second; - public readonly sbyte GmtOffset; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PartitionDescriptor + { + public readonly byte type; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] id; + public readonly byte version; + public readonly byte reserved1; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] system_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] partition_id; + public readonly uint partition_location; + public readonly uint partition_location_be; + public readonly uint partition_size; + public readonly uint partition_size_be; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 1960)] + public readonly byte[] system_use; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryRecord + { + public readonly byte length; + public readonly byte xattr_len; + public readonly uint extent; + public readonly uint extent_be; + public readonly uint size; + public readonly uint size_be; + public readonly IsoTimestamp date; + public readonly FileFlags flags; + public readonly byte file_unit_size; + public readonly byte interleave; + public readonly ushort volume_sequence_number; + public readonly ushort volume_sequence_number_be; + public readonly byte name_len; + + // Followed by name[name_len] and then system area until length arrives + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ExtendedAttributeRecord + { + public readonly ushort owner; + public readonly ushort owner_be; + public readonly ushort group; + public readonly ushort group_be; + public readonly Permissions permissions; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly byte[] creation_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly byte[] modification_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly byte[] expiration_date; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly byte[] effective_date; + public readonly RecordFormat record_format; + public readonly RecordAttribute record_attributes; + public readonly ushort record_length; + public readonly ushort record_length_be; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] system_id; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] system_use; + public readonly byte record_version; + public readonly byte escape_len; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] reserved1; + public readonly ushort app_use_len; + public readonly ushort app_use_len_be; + } + + // There are two tables one in little endian one in big endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PathTableEntry + { + public readonly byte name_len; + public readonly byte xattr_len; + public readonly uint start_lbn; + public readonly ushort parent_dirno; + + // Followed by name[name_len] + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct IsoTimestamp + { + public readonly byte Years; + public readonly byte Month; + public readonly byte Day; + public readonly byte Hour; + public readonly byte Minute; + public readonly byte Second; + public readonly sbyte GmtOffset; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/Internal.cs b/Aaru.Filesystems/ISO9660/Structs/Internal.cs index 1ad8f93c8..2406f087a 100644 --- a/Aaru.Filesystems/ISO9660/Structs/Internal.cs +++ b/Aaru.Filesystems/ISO9660/Structs/Internal.cs @@ -34,75 +34,74 @@ using System; using System.Collections.Generic; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + struct DecodedVolumeDescriptor { - struct DecodedVolumeDescriptor - { - public string SystemIdentifier; - public string VolumeIdentifier; - public string VolumeSetIdentifier; - public string PublisherIdentifier; - public string DataPreparerIdentifier; - public string ApplicationIdentifier; - public DateTime CreationTime; - public bool HasModificationTime; - public DateTime ModificationTime; - public bool HasExpirationTime; - public DateTime ExpirationTime; - public bool HasEffectiveTime; - public DateTime EffectiveTime; - public ushort BlockSize; - public uint Blocks; - } + public string SystemIdentifier; + public string VolumeIdentifier; + public string VolumeSetIdentifier; + public string PublisherIdentifier; + public string DataPreparerIdentifier; + public string ApplicationIdentifier; + public DateTime CreationTime; + public bool HasModificationTime; + public DateTime ModificationTime; + public bool HasExpirationTime; + public DateTime ExpirationTime; + public bool HasEffectiveTime; + public DateTime EffectiveTime; + public ushort BlockSize; + public uint Blocks; + } - class DecodedDirectoryEntry - { - public byte[] AmigaComment; - public AmigaProtection? AmigaProtection; - public byte? AppleDosType; - public byte[] AppleIcon; - public ushort? AppleProDosType; - public DecodedDirectoryEntry AssociatedFile; - public CdiSystemArea? CdiSystemArea; - public List<(uint extent, uint size)> Extents; - public string Filename; - public byte FileUnitSize; - public AppleCommon.FInfo? FinderInfo; - public FileFlags Flags; - public byte Interleave; - public PosixAttributes? PosixAttributes; - public PosixAttributesOld? PosixAttributesOld; - public PosixDeviceNumber? PosixDeviceNumber; - public DecodedDirectoryEntry ResourceFork; - public byte[] RockRidgeAlternateName; - public bool RockRidgeRelocated; - public byte[] RripAccess; - public byte[] RripAttributeChange; - public byte[] RripBackup; - public byte[] RripCreation; - public byte[] RripEffective; - public byte[] RripExpiration; - public byte[] RripModify; - public ulong Size; - public string SymbolicLink; - public DateTime? Timestamp; - public ushort VolumeSequenceNumber; - public CdromXa? XA; - public byte XattrLength; + class DecodedDirectoryEntry + { + public byte[] AmigaComment; + public AmigaProtection? AmigaProtection; + public byte? AppleDosType; + public byte[] AppleIcon; + public ushort? AppleProDosType; + public DecodedDirectoryEntry AssociatedFile; + public CdiSystemArea? CdiSystemArea; + public List<(uint extent, uint size)> Extents; + public string Filename; + public byte FileUnitSize; + public AppleCommon.FInfo? FinderInfo; + public FileFlags Flags; + public byte Interleave; + public PosixAttributes? PosixAttributes; + public PosixAttributesOld? PosixAttributesOld; + public PosixDeviceNumber? PosixDeviceNumber; + public DecodedDirectoryEntry ResourceFork; + public byte[] RockRidgeAlternateName; + public bool RockRidgeRelocated; + public byte[] RripAccess; + public byte[] RripAttributeChange; + public byte[] RripBackup; + public byte[] RripCreation; + public byte[] RripEffective; + public byte[] RripExpiration; + public byte[] RripModify; + public ulong Size; + public string SymbolicLink; + public DateTime? Timestamp; + public ushort VolumeSequenceNumber; + public CdromXa? XA; + public byte XattrLength; - public override string ToString() => Filename; - } + public override string ToString() => Filename; + } - class PathTableEntryInternal - { - public uint Extent; - public string Name; - public ushort Parent; - public byte XattrLength; + class PathTableEntryInternal + { + public uint Extent; + public string Name; + public ushort Parent; + public byte XattrLength; - public override string ToString() => Name; - } + public override string ToString() => Name; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/Joliet.cs b/Aaru.Filesystems/ISO9660/Structs/Joliet.cs index 704207edc..d86186bb6 100644 --- a/Aaru.Filesystems/ISO9660/Structs/Joliet.cs +++ b/Aaru.Filesystems/ISO9660/Structs/Joliet.cs @@ -35,65 +35,64 @@ using System; using System.Text; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + static DecodedVolumeDescriptor DecodeJolietDescriptor(PrimaryVolumeDescriptor jolietvd) { - static DecodedVolumeDescriptor DecodeJolietDescriptor(PrimaryVolumeDescriptor jolietvd) + var decodedVd = new DecodedVolumeDescriptor { - var decodedVd = new DecodedVolumeDescriptor - { - SystemIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.system_id).Replace('\u0000', ' '). - TrimEnd(), - VolumeIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.volume_id).Replace('\u0000', ' '). - TrimEnd(), - VolumeSetIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.volume_set_id). - Replace('\u0000', ' ').TrimEnd(), - PublisherIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.publisher_id).Replace('\u0000', ' '). - TrimEnd(), - DataPreparerIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.preparer_id). - Replace('\u0000', ' ').TrimEnd(), - ApplicationIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.application_id). - Replace('\u0000', ' ').TrimEnd() - }; + SystemIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.system_id).Replace('\u0000', ' '). + TrimEnd(), + VolumeIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.volume_id).Replace('\u0000', ' '). + TrimEnd(), + VolumeSetIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.volume_set_id). + Replace('\u0000', ' ').TrimEnd(), + PublisherIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.publisher_id).Replace('\u0000', ' '). + TrimEnd(), + DataPreparerIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.preparer_id). + Replace('\u0000', ' ').TrimEnd(), + ApplicationIdentifier = Encoding.BigEndianUnicode.GetString(jolietvd.application_id). + Replace('\u0000', ' ').TrimEnd() + }; - if(jolietvd.creation_date[0] < 0x31 || - jolietvd.creation_date[0] > 0x39) - decodedVd.CreationTime = DateTime.MinValue; - else - decodedVd.CreationTime = DateHandlers.Iso9660ToDateTime(jolietvd.creation_date); + if(jolietvd.creation_date[0] < 0x31 || + jolietvd.creation_date[0] > 0x39) + decodedVd.CreationTime = DateTime.MinValue; + else + decodedVd.CreationTime = DateHandlers.Iso9660ToDateTime(jolietvd.creation_date); - if(jolietvd.modification_date[0] < 0x31 || - jolietvd.modification_date[0] > 0x39) - decodedVd.HasModificationTime = false; - else - { - decodedVd.HasModificationTime = true; - decodedVd.ModificationTime = DateHandlers.Iso9660ToDateTime(jolietvd.modification_date); - } - - if(jolietvd.expiration_date[0] < 0x31 || - jolietvd.expiration_date[0] > 0x39) - decodedVd.HasExpirationTime = false; - else - { - decodedVd.HasExpirationTime = true; - decodedVd.ExpirationTime = DateHandlers.Iso9660ToDateTime(jolietvd.expiration_date); - } - - if(jolietvd.effective_date[0] < 0x31 || - jolietvd.effective_date[0] > 0x39) - decodedVd.HasEffectiveTime = false; - else - { - decodedVd.HasEffectiveTime = true; - decodedVd.EffectiveTime = DateHandlers.Iso9660ToDateTime(jolietvd.effective_date); - } - - decodedVd.Blocks = jolietvd.volume_space_size; - decodedVd.BlockSize = jolietvd.logical_block_size; - - return decodedVd; + if(jolietvd.modification_date[0] < 0x31 || + jolietvd.modification_date[0] > 0x39) + decodedVd.HasModificationTime = false; + else + { + decodedVd.HasModificationTime = true; + decodedVd.ModificationTime = DateHandlers.Iso9660ToDateTime(jolietvd.modification_date); } + + if(jolietvd.expiration_date[0] < 0x31 || + jolietvd.expiration_date[0] > 0x39) + decodedVd.HasExpirationTime = false; + else + { + decodedVd.HasExpirationTime = true; + decodedVd.ExpirationTime = DateHandlers.Iso9660ToDateTime(jolietvd.expiration_date); + } + + if(jolietvd.effective_date[0] < 0x31 || + jolietvd.effective_date[0] > 0x39) + decodedVd.HasEffectiveTime = false; + else + { + decodedVd.HasEffectiveTime = true; + decodedVd.EffectiveTime = DateHandlers.Iso9660ToDateTime(jolietvd.effective_date); + } + + decodedVd.Blocks = jolietvd.volume_space_size; + decodedVd.BlockSize = jolietvd.logical_block_size; + + return decodedVd; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/RRIP.cs b/Aaru.Filesystems/ISO9660/Structs/RRIP.cs index 6e0ceaecd..3f8d82fa0 100644 --- a/Aaru.Filesystems/ISO9660/Structs/RRIP.cs +++ b/Aaru.Filesystems/ISO9660/Structs/RRIP.cs @@ -34,147 +34,146 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +[SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed partial class ISO9660 { - [SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed partial class ISO9660 + // RRIP 1.10 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PosixAttributesOld { - // RRIP 1.10 - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PosixAttributesOld - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly PosixMode st_mode; - public readonly PosixMode st_mode_be; - public readonly uint st_nlink; - public readonly uint st_nlink_be; - public readonly uint st_uid; - public readonly uint st_uid_be; - public readonly uint st_gid; - public readonly uint st_gid_be; - } + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly PosixMode st_mode; + public readonly PosixMode st_mode_be; + public readonly uint st_nlink; + public readonly uint st_nlink_be; + public readonly uint st_uid; + public readonly uint st_uid_be; + public readonly uint st_gid; + public readonly uint st_gid_be; + } - // RRIP 1.12 - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PosixAttributes - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly PosixMode st_mode; - public readonly PosixMode st_mode_be; - public readonly uint st_nlink; - public readonly uint st_nlink_be; - public readonly uint st_uid; - public readonly uint st_uid_be; - public readonly uint st_gid; - public readonly uint st_gid_be; - public readonly uint st_ino; - public readonly uint st_ino_be; - } + // RRIP 1.12 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PosixAttributes + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly PosixMode st_mode; + public readonly PosixMode st_mode_be; + public readonly uint st_nlink; + public readonly uint st_nlink_be; + public readonly uint st_uid; + public readonly uint st_uid_be; + public readonly uint st_gid; + public readonly uint st_gid_be; + public readonly uint st_ino; + public readonly uint st_ino_be; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PosixDeviceNumber - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly uint dev_t_high; - public readonly uint dev_t_high_be; - public readonly uint dev_t_low; - public readonly uint dev_t_low_be; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PosixDeviceNumber + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly uint dev_t_high; + public readonly uint dev_t_high_be; + public readonly uint dev_t_low; + public readonly uint dev_t_low_be; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SymbolicLink - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly SymlinkFlags flags; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SymbolicLink + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly SymlinkFlags flags; - // Followed by SymbolicLinkComponent (link to /bar/foo uses at least two of these structs) - } + // Followed by SymbolicLinkComponent (link to /bar/foo uses at least two of these structs) + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SymbolicLinkComponent - { - public readonly SymlinkComponentFlags flags; - public readonly byte length; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SymbolicLinkComponent + { + public readonly SymlinkComponentFlags flags; + public readonly byte length; - // Followed by component content - } + // Followed by component content + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AlternateName - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly AlternateNameFlags flags; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AlternateName + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly AlternateNameFlags flags; - // Folowed by name, can be divided in pieces - } + // Folowed by name, can be divided in pieces + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ChildLink - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly uint child_dir_lba; - public readonly uint child_dir_lba_be; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ChildLink + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly uint child_dir_lba; + public readonly uint child_dir_lba_be; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ParentLink - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly uint parent_dir_lba; - public readonly uint parent_dir_lba_be; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ParentLink + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly uint parent_dir_lba; + public readonly uint parent_dir_lba_be; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct RelocatedDirectory - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct RelocatedDirectory + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Timestamps - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly TimestampFlags flags; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Timestamps + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly TimestampFlags flags; - // If flags indicate long format, timestamps are 17 bytes, if not, 7 bytes - // Followed by creation time if present - // Followed by modification time if present - // Followed by access time if present - // Followed by attribute change time if present - // Followed by backup time if present - // Followed by expiration time if present - // Followed by effective time if present - } + // If flags indicate long format, timestamps are 17 bytes, if not, 7 bytes + // Followed by creation time if present + // Followed by modification time if present + // Followed by access time if present + // Followed by attribute change time if present + // Followed by backup time if present + // Followed by expiration time if present + // Followed by effective time if present + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SparseFile - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly uint virtual_size_high; - public readonly uint virtual_size_high_be; - public readonly uint virtual_size_low; - public readonly uint virtual_size_low_be; - public readonly byte table_depth; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SparseFile + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly uint virtual_size_high; + public readonly uint virtual_size_high_be; + public readonly uint virtual_size_low; + public readonly uint virtual_size_low_be; + public readonly byte table_depth; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/SUSP.cs b/Aaru.Filesystems/ISO9660/Structs/SUSP.cs index 26949a1f2..2474b810a 100644 --- a/Aaru.Filesystems/ISO9660/Structs/SUSP.cs +++ b/Aaru.Filesystems/ISO9660/Structs/SUSP.cs @@ -35,73 +35,72 @@ using System.Runtime.InteropServices; // ReSharper disable UnusedType.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ContinuationArea { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ContinuationArea - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly uint block; - public readonly uint block_be; - public readonly uint offset; - public readonly uint offset_be; - public readonly uint ca_length; - public readonly uint ca_length_be; - } + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly uint block; + public readonly uint block_be; + public readonly uint offset; + public readonly uint offset_be; + public readonly uint ca_length; + public readonly uint ca_length_be; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PaddingArea - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PaddingArea + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct IndicatorArea - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly ushort magic; - public readonly byte skipped; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct IndicatorArea + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly ushort magic; + public readonly byte skipped; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct TerminatorArea - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct TerminatorArea + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ReferenceArea - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly byte id_len; - public readonly byte des_len; - public readonly byte src_len; - public readonly byte ext_ver; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ReferenceArea + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly byte id_len; + public readonly byte des_len; + public readonly byte src_len; + public readonly byte ext_ver; - // Follows extension identifier for id_len bytes - // Follows extension descriptor for des_len bytes - // Follows extension source for src_len bytes - } + // Follows extension identifier for id_len bytes + // Follows extension descriptor for des_len bytes + // Follows extension source for src_len bytes + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SelectorArea - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly byte sequence; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SelectorArea + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly byte sequence; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/XA.cs b/Aaru.Filesystems/ISO9660/Structs/XA.cs index 92dc5168b..836484708 100644 --- a/Aaru.Filesystems/ISO9660/Structs/XA.cs +++ b/Aaru.Filesystems/ISO9660/Structs/XA.cs @@ -33,21 +33,20 @@ using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + // Big-endian + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CdromXa { - // Big-endian - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CdromXa - { - public readonly ushort group; - public readonly ushort user; - public readonly XaAttributes attributes; - public readonly ushort signature; - public readonly byte filenumber; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] reserved; - } + public readonly ushort group; + public readonly ushort user; + public readonly XaAttributes attributes; + public readonly ushort signature; + public readonly byte filenumber; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] reserved; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Structs/Ziso.cs b/Aaru.Filesystems/ISO9660/Structs/Ziso.cs index f77703307..116f38394 100644 --- a/Aaru.Filesystems/ISO9660/Structs/Ziso.cs +++ b/Aaru.Filesystems/ISO9660/Structs/Ziso.cs @@ -34,32 +34,31 @@ using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; -namespace Aaru.Filesystems -{ - [SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed partial class ISO9660 - { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ZisofsHeader - { - public readonly ulong magic; - public readonly uint uncomp_len; - public readonly uint uncomp_len_be; - public readonly byte header_size; // Shifted >> 2 - public readonly byte block_size_log; // log2(block_size) - } +namespace Aaru.Filesystems; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ZisofsEntry - { - public readonly ushort signature; - public readonly byte length; - public readonly byte version; - public readonly ushort alogirhtm; - public readonly byte header_size; // Shifted >> 2 - public readonly byte block_size_log; // log2(block_size) - public readonly uint uncomp_len; - public readonly uint uncomp_len_be; - } +[SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed partial class ISO9660 +{ + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ZisofsHeader + { + public readonly ulong magic; + public readonly uint uncomp_len; + public readonly uint uncomp_len_be; + public readonly byte header_size; // Shifted >> 2 + public readonly byte block_size_log; // log2(block_size) + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ZisofsEntry + { + public readonly ushort signature; + public readonly byte length; + public readonly byte version; + public readonly ushort alogirhtm; + public readonly byte header_size; // Shifted >> 2 + public readonly byte block_size_log; // log2(block_size) + public readonly uint uncomp_len; + public readonly uint uncomp_len_be; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Super.cs b/Aaru.Filesystems/ISO9660/Super.cs index 6be8e94c0..0f5474efd 100644 --- a/Aaru.Filesystems/ISO9660/Super.cs +++ b/Aaru.Filesystems/ISO9660/Super.cs @@ -43,331 +43,359 @@ using Aaru.Decoders.Sega; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + Encoding = encoding ?? Encoding.GetEncoding(1252); + byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" + byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" + + options ??= GetDefaultOptions(); + + if(options.TryGetValue("debug", out string debugString)) + bool.TryParse(debugString, out _debug); + + if(options.TryGetValue("use_path_table", out string usePathTableString)) + bool.TryParse(usePathTableString, out _usePathTable); + + if(options.TryGetValue("use_trans_tbl", out string useTransTblString)) + bool.TryParse(useTransTblString, out _useTransTbl); + + if(options.TryGetValue("use_evd", out string useEvdString)) + bool.TryParse(useEvdString, out _useEvd); + + // Default namespace + @namespace ??= "joliet"; + + switch(@namespace.ToLowerInvariant()) { - Encoding = encoding ?? Encoding.GetEncoding(1252); - byte[] vdMagic = new byte[5]; // Volume Descriptor magic "CD001" - byte[] hsMagic = new byte[5]; // Volume Descriptor magic "CDROM" + case "normal": + _namespace = Namespace.Normal; - options ??= GetDefaultOptions(); + break; + case "vms": + _namespace = Namespace.Vms; - if(options.TryGetValue("debug", out string debugString)) - bool.TryParse(debugString, out _debug); + break; + case "joliet": + _namespace = Namespace.Joliet; - if(options.TryGetValue("use_path_table", out string usePathTableString)) - bool.TryParse(usePathTableString, out _usePathTable); + break; + case "rrip": + _namespace = Namespace.Rrip; - if(options.TryGetValue("use_trans_tbl", out string useTransTblString)) - bool.TryParse(useTransTblString, out _useTransTbl); + break; + case "romeo": + _namespace = Namespace.Romeo; - if(options.TryGetValue("use_evd", out string useEvdString)) - bool.TryParse(useEvdString, out _useEvd); + break; + default: return ErrorNumber.InvalidArgument; + } - // Default namespace - @namespace ??= "joliet"; + PrimaryVolumeDescriptor? pvd = null; + PrimaryVolumeDescriptor? jolietvd = null; + BootRecord? bvd = null; + HighSierraPrimaryVolumeDescriptor? hsvd = null; + FileStructureVolumeDescriptor? fsvd = null; - switch(@namespace.ToLowerInvariant()) - { - case "normal": - _namespace = Namespace.Normal; + // ISO9660 is designed for 2048 bytes/sector devices + if(imagePlugin.Info.SectorSize < 2048) + return ErrorNumber.InvalidArgument; - break; - case "vms": - _namespace = Namespace.Vms; + // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. + if(partition.End < 16) + return ErrorNumber.InvalidArgument; - break; - case "joliet": - _namespace = Namespace.Joliet; + ulong counter = 0; - break; - case "rrip": - _namespace = Namespace.Rrip; + ErrorNumber errno = imagePlugin.ReadSector(16 + counter + partition.Start, out byte[] vdSector); - break; - case "romeo": - _namespace = Namespace.Romeo; + if(errno != ErrorNumber.NoError) + return errno; - break; - default: return ErrorNumber.InvalidArgument; - } + int xaOff = vdSector.Length == 2336 ? 8 : 0; + Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); + _highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; + int hsOff = 0; - PrimaryVolumeDescriptor? pvd = null; - PrimaryVolumeDescriptor? jolietvd = null; - BootRecord? bvd = null; - HighSierraPrimaryVolumeDescriptor? hsvd = null; - FileStructureVolumeDescriptor? fsvd = null; + if(_highSierra) + hsOff = 8; - // ISO9660 is designed for 2048 bytes/sector devices - if(imagePlugin.Info.SectorSize < 2048) - return ErrorNumber.InvalidArgument; + _cdi = false; + List bvdSectors = new(); + List pvdSectors = new(); + List svdSectors = new(); + List evdSectors = new(); + List vpdSectors = new(); - // ISO9660 Primary Volume Descriptor starts at sector 16, so that's minimal size. - if(partition.End < 16) - return ErrorNumber.InvalidArgument; + while(true) + { + AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); - ulong counter = 0; - - ErrorNumber errno = imagePlugin.ReadSector(16 + counter + partition.Start, out byte[] vdSector); + // Seek to Volume Descriptor + AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); + errno = imagePlugin.ReadSector(16 + counter + partition.Start, out byte[] vdSectorTmp); if(errno != ErrorNumber.NoError) return errno; - int xaOff = vdSector.Length == 2336 ? 8 : 0; - Array.Copy(vdSector, 0x009 + xaOff, hsMagic, 0, 5); - _highSierra = Encoding.GetString(hsMagic) == HIGH_SIERRA_MAGIC; - int hsOff = 0; + vdSector = new byte[vdSectorTmp.Length - xaOff]; + Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); - if(_highSierra) - hsOff = 8; + byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. + AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); - _cdi = false; - List bvdSectors = new(); - List pvdSectors = new(); - List svdSectors = new(); - List evdSectors = new(); - List vpdSectors = new(); - - while(true) + if(vdType == 255) // Supposedly we are in the PVD. { - AaruConsole.DebugWriteLine("ISO9660 plugin", "Processing VD loop no. {0}", counter); + if(counter == 0) + return ErrorNumber.InvalidArgument; - // Seek to Volume Descriptor - AaruConsole.DebugWriteLine("ISO9660 plugin", "Reading sector {0}", 16 + counter + partition.Start); - errno = imagePlugin.ReadSector(16 + counter + partition.Start, out byte[] vdSectorTmp); + break; + } + + Array.Copy(vdSector, 0x001, vdMagic, 0, 5); + Array.Copy(vdSector, 0x009, hsMagic, 0, 5); + + if(Encoding.GetString(vdMagic) != ISO_MAGIC && + Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && + Encoding.GetString(vdMagic) != + CDI_MAGIC) // Recognized, it is an ISO9660, now check for rest of data. + { + if(counter == 0) + return ErrorNumber.InvalidArgument; + + break; + } + + _cdi |= Encoding.GetString(vdMagic) == CDI_MAGIC; + + switch(vdType) + { + case 0: + { + if(_debug) + bvdSectors.Add(16 + counter + partition.Start); + + break; + } + + case 1: + { + if(_highSierra) + hsvd = Marshal. + ByteArrayToStructureLittleEndian(vdSector); + else if(_cdi) + fsvd = Marshal.ByteArrayToStructureBigEndian(vdSector); + else + pvd = Marshal.ByteArrayToStructureLittleEndian(vdSector); + + if(_debug) + pvdSectors.Add(16 + counter + partition.Start); + + break; + } + + case 2: + { + PrimaryVolumeDescriptor svd = + Marshal.ByteArrayToStructureLittleEndian(vdSector); + + // TODO: Other escape sequences + // Check if this is Joliet + if(svd.version == 1) + { + if(svd.escape_sequences[0] == '%' && + svd.escape_sequences[1] == '/') + if(svd.escape_sequences[2] == '@' || + svd.escape_sequences[2] == 'C' || + svd.escape_sequences[2] == 'E') + jolietvd = svd; + else + AaruConsole.DebugWriteLine("ISO9660 plugin", + "Found unknown supplementary volume descriptor"); + + if(_debug) + svdSectors.Add(16 + counter + partition.Start); + } + else + { + if(_debug) + evdSectors.Add(16 + counter + partition.Start); + + if(_useEvd) + { + // Basically until escape sequences are implemented, let the user chose the encoding. + // This is the same as user choosing Romeo namespace, but using the EVD instead of the PVD + _namespace = Namespace.Romeo; + pvd = svd; + } + } + + break; + } + + case 3: + { + if(_debug) + vpdSectors.Add(16 + counter + partition.Start); + + break; + } + } + + counter++; + } + + DecodedVolumeDescriptor decodedVd; + var decodedJolietVd = new DecodedVolumeDescriptor(); + + XmlFsType = new FileSystemType(); + + if(pvd == null && + hsvd == null && + fsvd == null) + { + AaruConsole.ErrorWriteLine("ERROR: Could not find primary volume descriptor"); + + return ErrorNumber.InvalidArgument; + } + + if(_highSierra) + decodedVd = DecodeVolumeDescriptor(hsvd.Value); + else if(_cdi) + decodedVd = DecodeVolumeDescriptor(fsvd.Value); + else + decodedVd = DecodeVolumeDescriptor(pvd.Value, _namespace == Namespace.Romeo ? Encoding : Encoding.ASCII); + + if(jolietvd != null) + decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); + + if(_namespace != Namespace.Romeo) + Encoding = Encoding.ASCII; + + string fsFormat; + byte[] pathTableData; + + uint pathTableMsbLocation; + uint pathTableLsbLocation = 0; // Initialize to 0 as ignored in CD-i + + _image = imagePlugin; + + if(_highSierra) + { + _blockSize = hsvd.Value.logical_block_size; + + pathTableData = ReadSingleExtent(hsvd.Value.path_table_size, + Swapping.Swap(hsvd.Value.mandatory_path_table_msb)); + + fsFormat = "High Sierra Format"; + + pathTableMsbLocation = hsvd.Value.mandatory_path_table_msb; + pathTableLsbLocation = hsvd.Value.mandatory_path_table_lsb; + } + else if(_cdi) + { + _blockSize = fsvd.Value.logical_block_size; + + pathTableData = ReadSingleExtent(fsvd.Value.path_table_size, fsvd.Value.path_table_addr); + + fsFormat = "CD-i"; + + pathTableMsbLocation = fsvd.Value.path_table_addr; + + // TODO: Until escape sequences are implemented this is the default CD-i encoding. + Encoding = Encoding.GetEncoding("iso8859-1"); + } + else + { + _blockSize = pvd.Value.logical_block_size; + + pathTableData = ReadSingleExtent(pvd.Value.path_table_size, Swapping.Swap(pvd.Value.type_m_path_table)); + + fsFormat = "ISO9660"; + + pathTableMsbLocation = pvd.Value.type_m_path_table; + pathTableLsbLocation = pvd.Value.type_l_path_table; + } + + _pathTable = _highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData); + + if(_pathTable is null) + { + pathTableData = ReadSingleExtent(pathTableData.Length, pathTableLsbLocation); + + _pathTable = _highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData); + } + + // High Sierra and CD-i do not support Joliet or RRIP + if((_highSierra || _cdi) && + _namespace != Namespace.Normal && + _namespace != Namespace.Vms) + _namespace = Namespace.Normal; + + if(jolietvd is null && + _namespace == Namespace.Joliet) + _namespace = Namespace.Normal; + + uint rootLocation; + uint rootSize; + byte rootXattrLength = 0; + + if(!_cdi) + { + rootLocation = _highSierra ? hsvd.Value.root_directory_record.extent + : pvd.Value.root_directory_record.extent; + + rootXattrLength = _highSierra ? hsvd.Value.root_directory_record.xattr_len + : pvd.Value.root_directory_record.xattr_len; + + rootSize = _highSierra ? hsvd.Value.root_directory_record.size : pvd.Value.root_directory_record.size; + + if(_pathTable?.Length > 1 && + rootLocation != _pathTable[0].Extent) + { + AaruConsole.DebugWriteLine("ISO9660 plugin", + "Path table and PVD do not point to the same location for the root directory!"); + + errno = ReadSector(rootLocation, out byte[] firstRootSector); if(errno != ErrorNumber.NoError) return errno; - vdSector = new byte[vdSectorTmp.Length - xaOff]; - Array.Copy(vdSectorTmp, xaOff, vdSector, 0, vdSector.Length); + bool pvdWrongRoot = false; - byte vdType = vdSector[0 + hsOff]; // Volume Descriptor Type, should be 1 or 2. - AaruConsole.DebugWriteLine("ISO9660 plugin", "VDType = {0}", vdType); - - if(vdType == 255) // Supposedly we are in the PVD. + if(_highSierra) { - if(counter == 0) - return ErrorNumber.InvalidArgument; + HighSierraDirectoryRecord rootEntry = + Marshal.ByteArrayToStructureLittleEndian(firstRootSector); - break; + if(rootEntry.extent != rootLocation) + pvdWrongRoot = true; + } + else + { + DirectoryRecord rootEntry = + Marshal.ByteArrayToStructureLittleEndian(firstRootSector); + + if(rootEntry.extent != rootLocation) + pvdWrongRoot = true; } - Array.Copy(vdSector, 0x001, vdMagic, 0, 5); - Array.Copy(vdSector, 0x009, hsMagic, 0, 5); - - if(Encoding.GetString(vdMagic) != ISO_MAGIC && - Encoding.GetString(hsMagic) != HIGH_SIERRA_MAGIC && - Encoding.GetString(vdMagic) != - CDI_MAGIC) // Recognized, it is an ISO9660, now check for rest of data. - { - if(counter == 0) - return ErrorNumber.InvalidArgument; - - break; - } - - _cdi |= Encoding.GetString(vdMagic) == CDI_MAGIC; - - switch(vdType) - { - case 0: - { - if(_debug) - bvdSectors.Add(16 + counter + partition.Start); - - break; - } - - case 1: - { - if(_highSierra) - hsvd = Marshal. - ByteArrayToStructureLittleEndian(vdSector); - else if(_cdi) - fsvd = Marshal.ByteArrayToStructureBigEndian(vdSector); - else - pvd = Marshal.ByteArrayToStructureLittleEndian(vdSector); - - if(_debug) - pvdSectors.Add(16 + counter + partition.Start); - - break; - } - - case 2: - { - PrimaryVolumeDescriptor svd = - Marshal.ByteArrayToStructureLittleEndian(vdSector); - - // TODO: Other escape sequences - // Check if this is Joliet - if(svd.version == 1) - { - if(svd.escape_sequences[0] == '%' && - svd.escape_sequences[1] == '/') - if(svd.escape_sequences[2] == '@' || - svd.escape_sequences[2] == 'C' || - svd.escape_sequences[2] == 'E') - jolietvd = svd; - else - AaruConsole.DebugWriteLine("ISO9660 plugin", - "Found unknown supplementary volume descriptor"); - - if(_debug) - svdSectors.Add(16 + counter + partition.Start); - } - else - { - if(_debug) - evdSectors.Add(16 + counter + partition.Start); - - if(_useEvd) - { - // Basically until escape sequences are implemented, let the user chose the encoding. - // This is the same as user choosing Romeo namespace, but using the EVD instead of the PVD - _namespace = Namespace.Romeo; - pvd = svd; - } - } - - break; - } - - case 3: - { - if(_debug) - vpdSectors.Add(16 + counter + partition.Start); - - break; - } - } - - counter++; - } - - DecodedVolumeDescriptor decodedVd; - var decodedJolietVd = new DecodedVolumeDescriptor(); - - XmlFsType = new FileSystemType(); - - if(pvd == null && - hsvd == null && - fsvd == null) - { - AaruConsole.ErrorWriteLine("ERROR: Could not find primary volume descriptor"); - - return ErrorNumber.InvalidArgument; - } - - if(_highSierra) - decodedVd = DecodeVolumeDescriptor(hsvd.Value); - else if(_cdi) - decodedVd = DecodeVolumeDescriptor(fsvd.Value); - else - decodedVd = DecodeVolumeDescriptor(pvd.Value, _namespace == Namespace.Romeo ? Encoding : Encoding.ASCII); - - if(jolietvd != null) - decodedJolietVd = DecodeJolietDescriptor(jolietvd.Value); - - if(_namespace != Namespace.Romeo) - Encoding = Encoding.ASCII; - - string fsFormat; - byte[] pathTableData; - - uint pathTableMsbLocation; - uint pathTableLsbLocation = 0; // Initialize to 0 as ignored in CD-i - - _image = imagePlugin; - - if(_highSierra) - { - _blockSize = hsvd.Value.logical_block_size; - - pathTableData = ReadSingleExtent(hsvd.Value.path_table_size, - Swapping.Swap(hsvd.Value.mandatory_path_table_msb)); - - fsFormat = "High Sierra Format"; - - pathTableMsbLocation = hsvd.Value.mandatory_path_table_msb; - pathTableLsbLocation = hsvd.Value.mandatory_path_table_lsb; - } - else if(_cdi) - { - _blockSize = fsvd.Value.logical_block_size; - - pathTableData = ReadSingleExtent(fsvd.Value.path_table_size, fsvd.Value.path_table_addr); - - fsFormat = "CD-i"; - - pathTableMsbLocation = fsvd.Value.path_table_addr; - - // TODO: Until escape sequences are implemented this is the default CD-i encoding. - Encoding = Encoding.GetEncoding("iso8859-1"); - } - else - { - _blockSize = pvd.Value.logical_block_size; - - pathTableData = ReadSingleExtent(pvd.Value.path_table_size, Swapping.Swap(pvd.Value.type_m_path_table)); - - fsFormat = "ISO9660"; - - pathTableMsbLocation = pvd.Value.type_m_path_table; - pathTableLsbLocation = pvd.Value.type_l_path_table; - } - - _pathTable = _highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData); - - if(_pathTable is null) - { - pathTableData = ReadSingleExtent(pathTableData.Length, pathTableLsbLocation); - - _pathTable = _highSierra ? DecodeHighSierraPathTable(pathTableData) : DecodePathTable(pathTableData); - } - - // High Sierra and CD-i do not support Joliet or RRIP - if((_highSierra || _cdi) && - _namespace != Namespace.Normal && - _namespace != Namespace.Vms) - _namespace = Namespace.Normal; - - if(jolietvd is null && - _namespace == Namespace.Joliet) - _namespace = Namespace.Normal; - - uint rootLocation; - uint rootSize; - byte rootXattrLength = 0; - - if(!_cdi) - { - rootLocation = _highSierra ? hsvd.Value.root_directory_record.extent - : pvd.Value.root_directory_record.extent; - - rootXattrLength = _highSierra ? hsvd.Value.root_directory_record.xattr_len - : pvd.Value.root_directory_record.xattr_len; - - rootSize = _highSierra ? hsvd.Value.root_directory_record.size : pvd.Value.root_directory_record.size; - - if(_pathTable?.Length > 1 && - rootLocation != _pathTable[0].Extent) + if(pvdWrongRoot) { AaruConsole.DebugWriteLine("ISO9660 plugin", - "Path table and PVD do not point to the same location for the root directory!"); + "PVD does not point to correct root directory, checking path table..."); - errno = ReadSector(rootLocation, out byte[] firstRootSector); + bool pathTableWrongRoot = false; - if(errno != ErrorNumber.NoError) - return errno; + rootLocation = _pathTable[0].Extent; - bool pvdWrongRoot = false; + errno = ReadSector(_pathTable[0].Extent, out firstRootSector); if(_highSierra) { @@ -375,7 +403,7 @@ namespace Aaru.Filesystems Marshal.ByteArrayToStructureLittleEndian(firstRootSector); if(rootEntry.extent != rootLocation) - pvdWrongRoot = true; + pathTableWrongRoot = true; } else { @@ -383,423 +411,394 @@ namespace Aaru.Filesystems Marshal.ByteArrayToStructureLittleEndian(firstRootSector); if(rootEntry.extent != rootLocation) - pvdWrongRoot = true; + pathTableWrongRoot = true; } - if(pvdWrongRoot) + if(pathTableWrongRoot) { - AaruConsole.DebugWriteLine("ISO9660 plugin", - "PVD does not point to correct root directory, checking path table..."); + AaruConsole.ErrorWriteLine("Cannot find root directory..."); - bool pathTableWrongRoot = false; - - rootLocation = _pathTable[0].Extent; - - errno = ReadSector(_pathTable[0].Extent, out firstRootSector); - - if(_highSierra) - { - HighSierraDirectoryRecord rootEntry = - Marshal.ByteArrayToStructureLittleEndian(firstRootSector); - - if(rootEntry.extent != rootLocation) - pathTableWrongRoot = true; - } - else - { - DirectoryRecord rootEntry = - Marshal.ByteArrayToStructureLittleEndian(firstRootSector); - - if(rootEntry.extent != rootLocation) - pathTableWrongRoot = true; - } - - if(pathTableWrongRoot) - { - AaruConsole.ErrorWriteLine("Cannot find root directory..."); - - return ErrorNumber.InvalidArgument; - } - - _usePathTable = true; + return ErrorNumber.InvalidArgument; } + + _usePathTable = true; } } - else - { - rootLocation = _pathTable[0].Extent; + } + else + { + rootLocation = _pathTable[0].Extent; - errno = ReadSector(rootLocation, out byte[] firstRootSector); - - if(errno != ErrorNumber.NoError) - return errno; - - CdiDirectoryRecord rootEntry = - Marshal.ByteArrayToStructureBigEndian(firstRootSector); - - rootSize = rootEntry.size; - - _usePathTable = _usePathTable || _pathTable.Length == 1; - _useTransTbl = false; - } - - // In case the path table is incomplete - if(_usePathTable && (_pathTable is null || _pathTable?.Length <= 1)) - _usePathTable = false; - - if(_usePathTable && !_cdi) - { - rootLocation = _pathTable[0].Extent; - - errno = ReadSector(rootLocation, out byte[] firstRootSector); - - if(errno != ErrorNumber.NoError) - return errno; - - if(_highSierra) - { - HighSierraDirectoryRecord rootEntry = - Marshal.ByteArrayToStructureLittleEndian(firstRootSector); - - rootSize = rootEntry.size; - } - else - { - DirectoryRecord rootEntry = - Marshal.ByteArrayToStructureLittleEndian(firstRootSector); - - rootSize = rootEntry.size; - } - - rootXattrLength = _pathTable[0].XattrLength; - } - - try - { - _ = ReadSingleExtent(rootSize, rootLocation); - } - catch - { - return ErrorNumber.InvalidArgument; - } - - errno = ReadSector(partition.Start, out byte[] ipbinSector); + errno = ReadSector(rootLocation, out byte[] firstRootSector); if(errno != ErrorNumber.NoError) return errno; - CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); - Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); - Dreamcast.IPBin? dreamcast = Dreamcast.DecodeIPBin(ipbinSector); + CdiDirectoryRecord rootEntry = + Marshal.ByteArrayToStructureBigEndian(firstRootSector); - if(_namespace == Namespace.Joliet || - _namespace == Namespace.Rrip) + rootSize = rootEntry.size; + + _usePathTable = _usePathTable || _pathTable.Length == 1; + _useTransTbl = false; + } + + // In case the path table is incomplete + if(_usePathTable && (_pathTable is null || _pathTable?.Length <= 1)) + _usePathTable = false; + + if(_usePathTable && !_cdi) + { + rootLocation = _pathTable[0].Extent; + + errno = ReadSector(rootLocation, out byte[] firstRootSector); + + if(errno != ErrorNumber.NoError) + return errno; + + if(_highSierra) { - _usePathTable = false; - _useTransTbl = false; - } + HighSierraDirectoryRecord rootEntry = + Marshal.ByteArrayToStructureLittleEndian(firstRootSector); - // Cannot traverse path table if we substitute the names for the ones in TRANS.TBL - if(_useTransTbl) - _usePathTable = false; - - if(_namespace != Namespace.Joliet) - _rootDirectoryCache = _cdi - ? DecodeCdiDirectory(rootLocation + rootXattrLength, rootSize) - : _highSierra - ? DecodeHighSierraDirectory(rootLocation + rootXattrLength, rootSize) - : DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize); - - XmlFsType.Type = fsFormat; - - if(jolietvd != null && - (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip)) - { - rootLocation = jolietvd.Value.root_directory_record.extent; - rootXattrLength = jolietvd.Value.root_directory_record.xattr_len; - - rootSize = jolietvd.Value.root_directory_record.size; - - _joliet = true; - - _rootDirectoryCache = DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize); - - XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || - decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) - XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; - else - XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null - : decodedJolietVd.SystemIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || - decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) - XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; - else - XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null - : decodedJolietVd.VolumeSetIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || - decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) - XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; - else - XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null - : decodedJolietVd.PublisherIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || - decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) - XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; - else - XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) - ? null : decodedJolietVd.DataPreparerIdentifier; - - if(string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || - decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) - XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; - else - XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null - : decodedJolietVd.ApplicationIdentifier; - - XmlFsType.CreationDate = decodedJolietVd.CreationTime; - XmlFsType.CreationDateSpecified = true; - - if(decodedJolietVd.HasModificationTime) - { - XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; - XmlFsType.ModificationDateSpecified = true; - } - - if(decodedJolietVd.HasExpirationTime) - { - XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; - XmlFsType.ExpirationDateSpecified = true; - } - - if(decodedJolietVd.HasEffectiveTime) - { - XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; - XmlFsType.EffectiveDateSpecified = true; - } - - decodedVd = decodedJolietVd; + rootSize = rootEntry.size; } else { - XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; - XmlFsType.VolumeName = decodedVd.VolumeIdentifier; - XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; - XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; - XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; - XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; - XmlFsType.CreationDate = decodedVd.CreationTime; - XmlFsType.CreationDateSpecified = true; + DirectoryRecord rootEntry = + Marshal.ByteArrayToStructureLittleEndian(firstRootSector); - if(decodedVd.HasModificationTime) - { - XmlFsType.ModificationDate = decodedVd.ModificationTime; - XmlFsType.ModificationDateSpecified = true; - } - - if(decodedVd.HasExpirationTime) - { - XmlFsType.ExpirationDate = decodedVd.ExpirationTime; - XmlFsType.ExpirationDateSpecified = true; - } - - if(decodedVd.HasEffectiveTime) - { - XmlFsType.EffectiveDate = decodedVd.EffectiveTime; - XmlFsType.EffectiveDateSpecified = true; - } + rootSize = rootEntry.size; } - if(_debug) + rootXattrLength = _pathTable[0].XattrLength; + } + + try + { + _ = ReadSingleExtent(rootSize, rootLocation); + } + catch + { + return ErrorNumber.InvalidArgument; + } + + errno = ReadSector(partition.Start, out byte[] ipbinSector); + + if(errno != ErrorNumber.NoError) + return errno; + + CD.IPBin? segaCd = CD.DecodeIPBin(ipbinSector); + Saturn.IPBin? saturn = Saturn.DecodeIPBin(ipbinSector); + Dreamcast.IPBin? dreamcast = Dreamcast.DecodeIPBin(ipbinSector); + + if(_namespace == Namespace.Joliet || + _namespace == Namespace.Rrip) + { + _usePathTable = false; + _useTransTbl = false; + } + + // Cannot traverse path table if we substitute the names for the ones in TRANS.TBL + if(_useTransTbl) + _usePathTable = false; + + if(_namespace != Namespace.Joliet) + _rootDirectoryCache = _cdi + ? DecodeCdiDirectory(rootLocation + rootXattrLength, rootSize) + : _highSierra + ? DecodeHighSierraDirectory(rootLocation + rootXattrLength, rootSize) + : DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize); + + XmlFsType.Type = fsFormat; + + if(jolietvd != null && + (_namespace == Namespace.Joliet || _namespace == Namespace.Rrip)) + { + rootLocation = jolietvd.Value.root_directory_record.extent; + rootXattrLength = jolietvd.Value.root_directory_record.xattr_len; + + rootSize = jolietvd.Value.root_directory_record.size; + + _joliet = true; + + _rootDirectoryCache = DecodeIsoDirectory(rootLocation + rootXattrLength, rootSize); + + XmlFsType.VolumeName = decodedJolietVd.VolumeIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) || + decodedVd.SystemIdentifier.Length > decodedJolietVd.SystemIdentifier.Length) + XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; + else + XmlFsType.SystemIdentifier = string.IsNullOrEmpty(decodedJolietVd.SystemIdentifier) ? null + : decodedJolietVd.SystemIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) || + decodedVd.VolumeSetIdentifier.Length > decodedJolietVd.VolumeSetIdentifier.Length) + XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; + else + XmlFsType.VolumeSetIdentifier = string.IsNullOrEmpty(decodedJolietVd.VolumeSetIdentifier) ? null + : decodedJolietVd.VolumeSetIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) || + decodedVd.PublisherIdentifier.Length > decodedJolietVd.PublisherIdentifier.Length) + XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; + else + XmlFsType.PublisherIdentifier = string.IsNullOrEmpty(decodedJolietVd.PublisherIdentifier) ? null + : decodedJolietVd.PublisherIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) || + decodedVd.DataPreparerIdentifier.Length > decodedJolietVd.DataPreparerIdentifier.Length) + XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; + else + XmlFsType.DataPreparerIdentifier = string.IsNullOrEmpty(decodedJolietVd.DataPreparerIdentifier) + ? null : decodedJolietVd.DataPreparerIdentifier; + + if(string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) || + decodedVd.ApplicationIdentifier.Length > decodedJolietVd.ApplicationIdentifier.Length) + XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; + else + XmlFsType.ApplicationIdentifier = string.IsNullOrEmpty(decodedJolietVd.ApplicationIdentifier) ? null + : decodedJolietVd.ApplicationIdentifier; + + XmlFsType.CreationDate = decodedJolietVd.CreationTime; + XmlFsType.CreationDateSpecified = true; + + if(decodedJolietVd.HasModificationTime) { - _rootDirectoryCache.Add("$", new DecodedDirectoryEntry + XmlFsType.ModificationDate = decodedJolietVd.ModificationTime; + XmlFsType.ModificationDateSpecified = true; + } + + if(decodedJolietVd.HasExpirationTime) + { + XmlFsType.ExpirationDate = decodedJolietVd.ExpirationTime; + XmlFsType.ExpirationDateSpecified = true; + } + + if(decodedJolietVd.HasEffectiveTime) + { + XmlFsType.EffectiveDate = decodedJolietVd.EffectiveTime; + XmlFsType.EffectiveDateSpecified = true; + } + + decodedVd = decodedJolietVd; + } + else + { + XmlFsType.SystemIdentifier = decodedVd.SystemIdentifier; + XmlFsType.VolumeName = decodedVd.VolumeIdentifier; + XmlFsType.VolumeSetIdentifier = decodedVd.VolumeSetIdentifier; + XmlFsType.PublisherIdentifier = decodedVd.PublisherIdentifier; + XmlFsType.DataPreparerIdentifier = decodedVd.DataPreparerIdentifier; + XmlFsType.ApplicationIdentifier = decodedVd.ApplicationIdentifier; + XmlFsType.CreationDate = decodedVd.CreationTime; + XmlFsType.CreationDateSpecified = true; + + if(decodedVd.HasModificationTime) + { + XmlFsType.ModificationDate = decodedVd.ModificationTime; + XmlFsType.ModificationDateSpecified = true; + } + + if(decodedVd.HasExpirationTime) + { + XmlFsType.ExpirationDate = decodedVd.ExpirationTime; + XmlFsType.ExpirationDateSpecified = true; + } + + if(decodedVd.HasEffectiveTime) + { + XmlFsType.EffectiveDate = decodedVd.EffectiveTime; + XmlFsType.EffectiveDateSpecified = true; + } + } + + if(_debug) + { + _rootDirectoryCache.Add("$", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + (rootLocation, rootSize) + }, + Filename = "$", + Size = rootSize, + Timestamp = decodedVd.CreationTime + }); + + if(!_cdi) + _rootDirectoryCache.Add("$PATH_TABLE.LSB", new DecodedDirectoryEntry { Extents = new List<(uint extent, uint size)> { - (rootLocation, rootSize) + (pathTableLsbLocation, (uint)pathTableData.Length) }, - Filename = "$", - Size = rootSize, - Timestamp = decodedVd.CreationTime - }); - - if(!_cdi) - _rootDirectoryCache.Add("$PATH_TABLE.LSB", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - (pathTableLsbLocation, (uint)pathTableData.Length) - }, - Filename = "$PATH_TABLE.LSB", - Size = (uint)pathTableData.Length, - Timestamp = decodedVd.CreationTime - }); - - _rootDirectoryCache.Add("$PATH_TABLE.MSB", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - (Swapping.Swap(pathTableMsbLocation), (uint)pathTableData.Length) - }, - Filename = "$PATH_TABLE.MSB", + Filename = "$PATH_TABLE.LSB", Size = (uint)pathTableData.Length, Timestamp = decodedVd.CreationTime }); - for(int i = 0; i < bvdSectors.Count; i++) - _rootDirectoryCache.Add(i == 0 ? "$BOOT" : $"$BOOT_{i}", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - ((uint)i, 2048) - }, - Filename = i == 0 ? "$BOOT" : $"$BOOT_{i}", - Size = 2048, - Timestamp = decodedVd.CreationTime - }); - - for(int i = 0; i < pvdSectors.Count; i++) - _rootDirectoryCache.Add(i == 0 ? "$PVD" : $"$PVD{i}", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - ((uint)i, 2048) - }, - Filename = i == 0 ? "$PVD" : $"PVD_{i}", - Size = 2048, - Timestamp = decodedVd.CreationTime - }); - - for(int i = 0; i < svdSectors.Count; i++) - _rootDirectoryCache.Add(i == 0 ? "$SVD" : $"$SVD_{i}", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - ((uint)i, 2048) - }, - Filename = i == 0 ? "$SVD" : $"$SVD_{i}", - Size = 2048, - Timestamp = decodedVd.CreationTime - }); - - for(int i = 0; i < evdSectors.Count; i++) - _rootDirectoryCache.Add(i == 0 ? "$EVD" : $"$EVD_{i}", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - ((uint)i, 2048) - }, - Filename = i == 0 ? "$EVD" : $"$EVD_{i}", - Size = 2048, - Timestamp = decodedVd.CreationTime - }); - - for(int i = 0; i < vpdSectors.Count; i++) - _rootDirectoryCache.Add(i == 0 ? "$VPD" : $"$VPD_{i}", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - ((uint)i, 2048) - }, - Filename = i == 0 ? "$VPD" : $"$VPD_{i}", - Size = 2048, - Timestamp = decodedVd.CreationTime - }); - - if(segaCd != null) - _rootDirectoryCache.Add("$IP.BIN", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - ((uint)partition.Start, (uint)Marshal.SizeOf()) - }, - Filename = "$IP.BIN", - Size = (uint)Marshal.SizeOf(), - Timestamp = decodedVd.CreationTime - }); - - if(saturn != null) - _rootDirectoryCache.Add("$IP.BIN", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - ((uint)partition.Start, (uint)Marshal.SizeOf()) - }, - Filename = "$IP.BIN", - Size = (uint)Marshal.SizeOf(), - Timestamp = decodedVd.CreationTime - }); - - if(dreamcast != null) - _rootDirectoryCache.Add("$IP.BIN", new DecodedDirectoryEntry - { - Extents = new List<(uint extent, uint size)> - { - ((uint)partition.Start, (uint)Marshal.SizeOf()) - }, - Filename = "$IP.BIN", - Size = (uint)Marshal.SizeOf(), - Timestamp = decodedVd.CreationTime - }); - } - - XmlFsType.Bootable |= bvd != null || segaCd != null || saturn != null || dreamcast != null; - XmlFsType.Clusters = decodedVd.Blocks; - XmlFsType.ClusterSize = decodedVd.BlockSize; - - _statfs = new FileSystemInfo + _rootDirectoryCache.Add("$PATH_TABLE.MSB", new DecodedDirectoryEntry { - Blocks = decodedVd.Blocks, - FilenameLength = (ushort)(jolietvd != null ? _namespace == Namespace.Joliet - ? 110 - : 255 : 255), - PluginId = Id, - Type = fsFormat - }; + Extents = new List<(uint extent, uint size)> + { + (Swapping.Swap(pathTableMsbLocation), (uint)pathTableData.Length) + }, + Filename = "$PATH_TABLE.MSB", + Size = (uint)pathTableData.Length, + Timestamp = decodedVd.CreationTime + }); - _directoryCache = new Dictionary>(); + for(int i = 0; i < bvdSectors.Count; i++) + _rootDirectoryCache.Add(i == 0 ? "$BOOT" : $"$BOOT_{i}", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + ((uint)i, 2048) + }, + Filename = i == 0 ? "$BOOT" : $"$BOOT_{i}", + Size = 2048, + Timestamp = decodedVd.CreationTime + }); - if(_usePathTable) - foreach(DecodedDirectoryEntry subDirectory in _cdi - ? GetSubdirsFromCdiPathTable("") - : _highSierra - ? GetSubdirsFromHighSierraPathTable("") - : GetSubdirsFromIsoPathTable("")) - _rootDirectoryCache[subDirectory.Filename] = subDirectory; + for(int i = 0; i < pvdSectors.Count; i++) + _rootDirectoryCache.Add(i == 0 ? "$PVD" : $"$PVD{i}", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + ((uint)i, 2048) + }, + Filename = i == 0 ? "$PVD" : $"PVD_{i}", + Size = 2048, + Timestamp = decodedVd.CreationTime + }); - _mounted = true; + for(int i = 0; i < svdSectors.Count; i++) + _rootDirectoryCache.Add(i == 0 ? "$SVD" : $"$SVD_{i}", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + ((uint)i, 2048) + }, + Filename = i == 0 ? "$SVD" : $"$SVD_{i}", + Size = 2048, + Timestamp = decodedVd.CreationTime + }); - return ErrorNumber.NoError; + for(int i = 0; i < evdSectors.Count; i++) + _rootDirectoryCache.Add(i == 0 ? "$EVD" : $"$EVD_{i}", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + ((uint)i, 2048) + }, + Filename = i == 0 ? "$EVD" : $"$EVD_{i}", + Size = 2048, + Timestamp = decodedVd.CreationTime + }); + + for(int i = 0; i < vpdSectors.Count; i++) + _rootDirectoryCache.Add(i == 0 ? "$VPD" : $"$VPD_{i}", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + ((uint)i, 2048) + }, + Filename = i == 0 ? "$VPD" : $"$VPD_{i}", + Size = 2048, + Timestamp = decodedVd.CreationTime + }); + + if(segaCd != null) + _rootDirectoryCache.Add("$IP.BIN", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + ((uint)partition.Start, (uint)Marshal.SizeOf()) + }, + Filename = "$IP.BIN", + Size = (uint)Marshal.SizeOf(), + Timestamp = decodedVd.CreationTime + }); + + if(saturn != null) + _rootDirectoryCache.Add("$IP.BIN", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + ((uint)partition.Start, (uint)Marshal.SizeOf()) + }, + Filename = "$IP.BIN", + Size = (uint)Marshal.SizeOf(), + Timestamp = decodedVd.CreationTime + }); + + if(dreamcast != null) + _rootDirectoryCache.Add("$IP.BIN", new DecodedDirectoryEntry + { + Extents = new List<(uint extent, uint size)> + { + ((uint)partition.Start, (uint)Marshal.SizeOf()) + }, + Filename = "$IP.BIN", + Size = (uint)Marshal.SizeOf(), + Timestamp = decodedVd.CreationTime + }); } - /// - public ErrorNumber Unmount() + XmlFsType.Bootable |= bvd != null || segaCd != null || saturn != null || dreamcast != null; + XmlFsType.Clusters = decodedVd.Blocks; + XmlFsType.ClusterSize = decodedVd.BlockSize; + + _statfs = new FileSystemInfo { - if(!_mounted) - return ErrorNumber.AccessDenied; + Blocks = decodedVd.Blocks, + FilenameLength = (ushort)(jolietvd != null ? _namespace == Namespace.Joliet + ? 110 + : 255 : 255), + PluginId = Id, + Type = fsFormat + }; - _rootDirectoryCache = null; - _directoryCache = null; - _mounted = false; + _directoryCache = new Dictionary>(); - return ErrorNumber.NoError; - } + if(_usePathTable) + foreach(DecodedDirectoryEntry subDirectory in _cdi + ? GetSubdirsFromCdiPathTable("") + : _highSierra + ? GetSubdirsFromHighSierraPathTable("") + : GetSubdirsFromIsoPathTable("")) + _rootDirectoryCache[subDirectory.Filename] = subDirectory; - /// - public ErrorNumber StatFs(out FileSystemInfo stat) - { - stat = null; + _mounted = true; - if(!_mounted) - return ErrorNumber.AccessDenied; + return ErrorNumber.NoError; + } - stat = _statfs.ShallowCopy(); + /// + public ErrorNumber Unmount() + { + if(!_mounted) + return ErrorNumber.AccessDenied; - return ErrorNumber.NoError; - } + _rootDirectoryCache = null; + _directoryCache = null; + _mounted = false; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + stat = _statfs.ShallowCopy(); + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ISO9660/Xattr.cs b/Aaru.Filesystems/ISO9660/Xattr.cs index 387e47d08..046e4a96b 100644 --- a/Aaru.Filesystems/ISO9660/Xattr.cs +++ b/Aaru.Filesystems/ISO9660/Xattr.cs @@ -36,181 +36,180 @@ using System.Collections.Generic; using Aaru.CommonTypes.Enums; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class ISO9660 { - public sealed partial class ISO9660 + /// + public ErrorNumber ListXAttr(string path, out List xattrs) { - /// - public ErrorNumber ListXAttr(string path, out List xattrs) - { - xattrs = null; + xattrs = null; - if(!_mounted) - return ErrorNumber.AccessDenied; + if(!_mounted) + return ErrorNumber.AccessDenied; - ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); - if(err != ErrorNumber.NoError) - return err; + if(err != ErrorNumber.NoError) + return err; - xattrs = new List(); + xattrs = new List(); - if(entry.XattrLength > 0) - xattrs.Add("org.iso.9660.ea"); + if(entry.XattrLength > 0) + xattrs.Add("org.iso.9660.ea"); - if(entry.AssociatedFile != null) - xattrs.Add("org.iso.9660.AssociatedFile"); + if(entry.AssociatedFile != null) + xattrs.Add("org.iso.9660.AssociatedFile"); - if(entry.AppleDosType != null) - xattrs.Add("com.apple.dos.type"); + if(entry.AppleDosType != null) + xattrs.Add("com.apple.dos.type"); - if(entry.AppleProDosType != null) - xattrs.Add("com.apple.prodos.type"); + if(entry.AppleProDosType != null) + xattrs.Add("com.apple.prodos.type"); - if(entry.ResourceFork != null) - xattrs.Add("com.apple.ResourceFork"); + if(entry.ResourceFork != null) + xattrs.Add("com.apple.ResourceFork"); - if(entry.FinderInfo != null) - xattrs.Add("com.apple.FinderInfo"); + if(entry.FinderInfo != null) + xattrs.Add("com.apple.FinderInfo"); - if(entry.AppleIcon != null) - xattrs.Add("com.apple.Macintosh.Icon"); + if(entry.AppleIcon != null) + xattrs.Add("com.apple.Macintosh.Icon"); - if(entry.AmigaComment != null) - xattrs.Add("com.amiga.comments"); - - if(entry.Flags.HasFlag(FileFlags.Directory) || - entry.Extents == null || - entry.Extents.Count == 0) - return ErrorNumber.NoError; - - ErrorNumber errno = _image.ReadSectorLong(entry.Extents[0].extent * _blockSize / 2048, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return errno; - - if(sector[15] != 2) - return ErrorNumber.NoError; - - xattrs.Add("org.iso.mode2.subheader"); - xattrs.Add("org.iso.mode2.subheader.copy"); + if(entry.AmigaComment != null) + xattrs.Add("com.amiga.comments"); + if(entry.Flags.HasFlag(FileFlags.Directory) || + entry.Extents == null || + entry.Extents.Count == 0) return ErrorNumber.NoError; - } - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + ErrorNumber errno = _image.ReadSectorLong(entry.Extents[0].extent * _blockSize / 2048, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return errno; + + if(sector[15] != 2) + return ErrorNumber.NoError; + + xattrs.Add("org.iso.mode2.subheader"); + xattrs.Add("org.iso.mode2.subheader.copy"); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + { + buf = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + + if(err != ErrorNumber.NoError) + return err; + + switch(xattr) { - buf = null; + case "org.iso.9660.ea": + if(entry.XattrLength == 0) + return ErrorNumber.NoSuchExtendedAttribute; - if(!_mounted) - return ErrorNumber.AccessDenied; + if(entry.Extents is null) + return ErrorNumber.InvalidArgument; - ErrorNumber err = GetFileEntry(path, out DecodedDirectoryEntry entry); + buf = ReadSingleExtent(entry.XattrLength * _blockSize, entry.Extents[0].extent); - if(err != ErrorNumber.NoError) - return err; + return ErrorNumber.NoError; + case "org.iso.9660.AssociatedFile": + if(entry.AssociatedFile is null) + return ErrorNumber.NoSuchExtendedAttribute; - switch(xattr) - { - case "org.iso.9660.ea": - if(entry.XattrLength == 0) - return ErrorNumber.NoSuchExtendedAttribute; + if(entry.AssociatedFile.Extents is null) + return ErrorNumber.InvalidArgument; - if(entry.Extents is null) - return ErrorNumber.InvalidArgument; - - buf = ReadSingleExtent(entry.XattrLength * _blockSize, entry.Extents[0].extent); + if(entry.AssociatedFile.Size == 0) + { + buf = Array.Empty(); return ErrorNumber.NoError; - case "org.iso.9660.AssociatedFile": - if(entry.AssociatedFile is null) - return ErrorNumber.NoSuchExtendedAttribute; + } - if(entry.AssociatedFile.Extents is null) - return ErrorNumber.InvalidArgument; + buf = ReadWithExtents(0, (long)entry.AssociatedFile.Size, entry.AssociatedFile.Extents, + entry.AssociatedFile.XA?.signature == XA_MAGIC && + entry.AssociatedFile.XA?.attributes.HasFlag(XaAttributes.Interleaved) == true, + entry.AssociatedFile.XA?.filenumber ?? 0); - if(entry.AssociatedFile.Size == 0) - { - buf = Array.Empty(); + return ErrorNumber.NoError; + case "com.apple.dos.type": + if(entry.AppleDosType is null) + return ErrorNumber.NoSuchExtendedAttribute; - return ErrorNumber.NoError; - } + buf = new byte[1]; + buf[0] = entry.AppleDosType.Value; - buf = ReadWithExtents(0, (long)entry.AssociatedFile.Size, entry.AssociatedFile.Extents, - entry.AssociatedFile.XA?.signature == XA_MAGIC && - entry.AssociatedFile.XA?.attributes.HasFlag(XaAttributes.Interleaved) == true, - entry.AssociatedFile.XA?.filenumber ?? 0); + return ErrorNumber.NoError; + case "com.apple.prodos.type": + if(entry.AppleProDosType is null) + return ErrorNumber.NoSuchExtendedAttribute; + + buf = BitConverter.GetBytes(entry.AppleProDosType.Value); + + return ErrorNumber.NoError; + case "com.apple.ResourceFork": + if(entry.ResourceFork is null) + return ErrorNumber.NoSuchExtendedAttribute; + + if(entry.ResourceFork.Extents is null) + return ErrorNumber.InvalidArgument; + + if(entry.ResourceFork.Size == 0) + { + buf = Array.Empty(); return ErrorNumber.NoError; - case "com.apple.dos.type": - if(entry.AppleDosType is null) - return ErrorNumber.NoSuchExtendedAttribute; + } - buf = new byte[1]; - buf[0] = entry.AppleDosType.Value; + buf = ReadWithExtents(0, (long)entry.ResourceFork.Size, entry.ResourceFork.Extents, + entry.ResourceFork.XA?.signature == XA_MAGIC && + entry.ResourceFork.XA?.attributes.HasFlag(XaAttributes.Interleaved) == true, + entry.ResourceFork.XA?.filenumber ?? 0); - return ErrorNumber.NoError; - case "com.apple.prodos.type": - if(entry.AppleProDosType is null) - return ErrorNumber.NoSuchExtendedAttribute; + return ErrorNumber.NoError; + case "com.apple.FinderInfo": + if(entry.FinderInfo is null) + return ErrorNumber.NoSuchExtendedAttribute; - buf = BitConverter.GetBytes(entry.AppleProDosType.Value); + buf = Marshal.StructureToByteArrayBigEndian(entry.FinderInfo.Value); - return ErrorNumber.NoError; - case "com.apple.ResourceFork": - if(entry.ResourceFork is null) - return ErrorNumber.NoSuchExtendedAttribute; + return ErrorNumber.NoError; + case "com.apple.Macintosh.Icon": + if(entry.AppleIcon is null) + return ErrorNumber.NoSuchExtendedAttribute; - if(entry.ResourceFork.Extents is null) - return ErrorNumber.InvalidArgument; + buf = new byte[entry.AppleIcon.Length]; + Array.Copy(entry.AppleIcon, 0, buf, 0, entry.AppleIcon.Length); - if(entry.ResourceFork.Size == 0) - { - buf = Array.Empty(); + return ErrorNumber.NoError; + case "com.amiga.comments": + if(entry.AmigaComment is null) + return ErrorNumber.NoSuchExtendedAttribute; - return ErrorNumber.NoError; - } + buf = new byte[entry.AmigaComment.Length]; + Array.Copy(entry.AmigaComment, 0, buf, 0, entry.AmigaComment.Length); - buf = ReadWithExtents(0, (long)entry.ResourceFork.Size, entry.ResourceFork.Extents, - entry.ResourceFork.XA?.signature == XA_MAGIC && - entry.ResourceFork.XA?.attributes.HasFlag(XaAttributes.Interleaved) == true, - entry.ResourceFork.XA?.filenumber ?? 0); + return ErrorNumber.NoError; + case "org.iso.mode2.subheader": + buf = ReadSubheaderWithExtents(entry.Extents, false); - return ErrorNumber.NoError; - case "com.apple.FinderInfo": - if(entry.FinderInfo is null) - return ErrorNumber.NoSuchExtendedAttribute; + return ErrorNumber.NoError; + case "org.iso.mode2.subheader.copy": + buf = ReadSubheaderWithExtents(entry.Extents, true); - buf = Marshal.StructureToByteArrayBigEndian(entry.FinderInfo.Value); - - return ErrorNumber.NoError; - case "com.apple.Macintosh.Icon": - if(entry.AppleIcon is null) - return ErrorNumber.NoSuchExtendedAttribute; - - buf = new byte[entry.AppleIcon.Length]; - Array.Copy(entry.AppleIcon, 0, buf, 0, entry.AppleIcon.Length); - - return ErrorNumber.NoError; - case "com.amiga.comments": - if(entry.AmigaComment is null) - return ErrorNumber.NoSuchExtendedAttribute; - - buf = new byte[entry.AmigaComment.Length]; - Array.Copy(entry.AmigaComment, 0, buf, 0, entry.AmigaComment.Length); - - return ErrorNumber.NoError; - case "org.iso.mode2.subheader": - buf = ReadSubheaderWithExtents(entry.Extents, false); - - return ErrorNumber.NoError; - case "org.iso.mode2.subheader.copy": - buf = ReadSubheaderWithExtents(entry.Extents, true); - - return ErrorNumber.NoError; - default: return ErrorNumber.NoSuchExtendedAttribute; - } + return ErrorNumber.NoError; + default: return ErrorNumber.NoSuchExtendedAttribute; } } } \ No newline at end of file diff --git a/Aaru.Filesystems/JFS.cs b/Aaru.Filesystems/JFS.cs index 79de52c64..bb5162000 100644 --- a/Aaru.Filesystems/JFS.cs +++ b/Aaru.Filesystems/JFS.cs @@ -43,240 +43,239 @@ using Marshal = Aaru.Helpers.Marshal; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of IBM's Journaled File System +public sealed class JFS : IFilesystem { + const uint JFS_BOOT_BLOCKS_SIZE = 0x8000; + const uint JFS_MAGIC = 0x3153464A; + /// - /// Implements detection of IBM's Journaled File System - public sealed class JFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "JFS Plugin"; + /// + public Guid Id => new("D3BE2A41-8F28-4055-94DC-BB6C72A0E9C4"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint JFS_BOOT_BLOCKS_SIZE = 0x8000; - const uint JFS_MAGIC = 0x3153464A; + uint bootSectors = JFS_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "JFS Plugin"; - /// - public Guid Id => new("D3BE2A41-8F28-4055-94DC-BB6C72A0E9C4"); - /// - public string Author => "Natalia Portillo"; + if(partition.Start + bootSectors >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + bootSectors, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < 512) + return false; + + SuperBlock jfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + return jfsSb.s_magic == JFS_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + var sb = new StringBuilder(); + uint bootSectors = JFS_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + bootSectors, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < 512) + return; + + SuperBlock jfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + sb.AppendLine("JFS filesystem"); + sb.AppendFormat("Version {0}", jfsSb.s_version).AppendLine(); + sb.AppendFormat("{0} blocks of {1} bytes", jfsSb.s_size, jfsSb.s_bsize).AppendLine(); + sb.AppendFormat("{0} blocks per allocation group", jfsSb.s_agsize).AppendLine(); + + if(jfsSb.s_flags.HasFlag(Flags.Unicode)) + sb.AppendLine("Volume uses Unicode for directory entries"); + + if(jfsSb.s_flags.HasFlag(Flags.RemountRO)) + sb.AppendLine("Volume remounts read-only on error"); + + if(jfsSb.s_flags.HasFlag(Flags.Continue)) + sb.AppendLine("Volume continues on error"); + + if(jfsSb.s_flags.HasFlag(Flags.Panic)) + sb.AppendLine("Volume panics on error"); + + if(jfsSb.s_flags.HasFlag(Flags.UserQuota)) + sb.AppendLine("Volume has user quotas enabled"); + + if(jfsSb.s_flags.HasFlag(Flags.GroupQuota)) + sb.AppendLine("Volume has group quotas enabled"); + + if(jfsSb.s_flags.HasFlag(Flags.NoJournal)) + sb.AppendLine("Volume is not using any journal"); + + if(jfsSb.s_flags.HasFlag(Flags.Discard)) + sb.AppendLine("Volume sends TRIM/UNMAP commands to underlying device"); + + if(jfsSb.s_flags.HasFlag(Flags.GroupCommit)) + sb.AppendLine("Volume commits in groups of 1"); + + if(jfsSb.s_flags.HasFlag(Flags.LazyCommit)) + sb.AppendLine("Volume commits lazy"); + + if(jfsSb.s_flags.HasFlag(Flags.Temporary)) + sb.AppendLine("Volume does not commit to log"); + + if(jfsSb.s_flags.HasFlag(Flags.InlineLog)) + sb.AppendLine("Volume has log withing itself"); + + if(jfsSb.s_flags.HasFlag(Flags.InlineMoving)) + sb.AppendLine("Volume has log withing itself and is moving it out"); + + if(jfsSb.s_flags.HasFlag(Flags.BadSAIT)) + sb.AppendLine("Volume has bad current secondary ait"); + + if(jfsSb.s_flags.HasFlag(Flags.Sparse)) + sb.AppendLine("Volume supports sparse files"); + + if(jfsSb.s_flags.HasFlag(Flags.DASDEnabled)) + sb.AppendLine("Volume has DASD limits enabled"); + + if(jfsSb.s_flags.HasFlag(Flags.DASDPrime)) + sb.AppendLine("Volume primes DASD on boot"); + + if(jfsSb.s_flags.HasFlag(Flags.SwapBytes)) + sb.AppendLine("Volume is in a big-endian system"); + + if(jfsSb.s_flags.HasFlag(Flags.DirIndex)) + sb.AppendLine("Volume has presistent indexes"); + + if(jfsSb.s_flags.HasFlag(Flags.Linux)) + sb.AppendLine("Volume supports Linux"); + + if(jfsSb.s_flags.HasFlag(Flags.DFS)) + sb.AppendLine("Volume supports DCE DFS LFS"); + + if(jfsSb.s_flags.HasFlag(Flags.OS2)) + sb.AppendLine("Volume supports OS/2, and is case insensitive"); + + if(jfsSb.s_flags.HasFlag(Flags.AIX)) + sb.AppendLine("Volume supports AIX"); + + if(jfsSb.s_state != 0) + sb.AppendLine("Volume is dirty"); + + sb.AppendFormat("Volume was last updated on {0}", + DateHandlers.UnixUnsignedToDateTime(jfsSb.s_time.tv_sec, jfsSb.s_time.tv_nsec)). + AppendLine(); + + if(jfsSb.s_version == 1) + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(jfsSb.s_fpack, Encoding)).AppendLine(); + else + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(jfsSb.s_label, Encoding)).AppendLine(); + + sb.AppendFormat("Volume UUID: {0}", jfsSb.s_uuid).AppendLine(); + + XmlFsType = new FileSystemType { - uint bootSectors = JFS_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; + Type = "JFS filesystem", + Clusters = jfsSb.s_size, + ClusterSize = jfsSb.s_bsize, + Bootable = true, + VolumeName = StringHandlers.CToString(jfsSb.s_version == 1 ? jfsSb.s_fpack : jfsSb.s_label, Encoding), + VolumeSerial = $"{jfsSb.s_uuid}", + ModificationDate = DateHandlers.UnixUnsignedToDateTime(jfsSb.s_time.tv_sec, jfsSb.s_time.tv_nsec), + ModificationDateSpecified = true + }; - if(partition.Start + bootSectors >= partition.End) - return false; + if(jfsSb.s_state != 0) + XmlFsType.Dirty = true; - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + bootSectors, out byte[] sector); + information = sb.ToString(); + } - if(errno != ErrorNumber.NoError) - return false; + [Flags, SuppressMessage("ReSharper", "InconsistentNaming")] + enum Flags : uint + { + Unicode = 0x00000001, RemountRO = 0x00000002, Continue = 0x00000004, + Panic = 0x00000008, UserQuota = 0x00000010, GroupQuota = 0x00000020, + NoJournal = 0x00000040, Discard = 0x00000080, GroupCommit = 0x00000100, + LazyCommit = 0x00000200, Temporary = 0x00000400, InlineLog = 0x00000800, + InlineMoving = 0x00001000, BadSAIT = 0x00010000, Sparse = 0x00020000, + DASDEnabled = 0x00040000, DASDPrime = 0x00080000, SwapBytes = 0x00100000, + DirIndex = 0x00200000, Linux = 0x10000000, DFS = 0x20000000, + OS2 = 0x40000000, AIX = 0x80000000 + } - if(sector.Length < 512) - return false; + [Flags] + enum State : uint + { + Clean = 0, Mounted = 1, Dirty = 2, + Logredo = 4, Extendfs = 8 + } - SuperBlock jfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Extent + { + /// Leftmost 24 bits are extent length, rest 8 bits are most significant for + public readonly uint len_addr; + public readonly uint addr2; + } - return jfsSb.s_magic == JFS_MAGIC; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct TimeStruct + { + public readonly uint tv_sec; + public readonly uint tv_nsec; + } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - var sb = new StringBuilder(); - uint bootSectors = JFS_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + bootSectors, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < 512) - return; - - SuperBlock jfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - sb.AppendLine("JFS filesystem"); - sb.AppendFormat("Version {0}", jfsSb.s_version).AppendLine(); - sb.AppendFormat("{0} blocks of {1} bytes", jfsSb.s_size, jfsSb.s_bsize).AppendLine(); - sb.AppendFormat("{0} blocks per allocation group", jfsSb.s_agsize).AppendLine(); - - if(jfsSb.s_flags.HasFlag(Flags.Unicode)) - sb.AppendLine("Volume uses Unicode for directory entries"); - - if(jfsSb.s_flags.HasFlag(Flags.RemountRO)) - sb.AppendLine("Volume remounts read-only on error"); - - if(jfsSb.s_flags.HasFlag(Flags.Continue)) - sb.AppendLine("Volume continues on error"); - - if(jfsSb.s_flags.HasFlag(Flags.Panic)) - sb.AppendLine("Volume panics on error"); - - if(jfsSb.s_flags.HasFlag(Flags.UserQuota)) - sb.AppendLine("Volume has user quotas enabled"); - - if(jfsSb.s_flags.HasFlag(Flags.GroupQuota)) - sb.AppendLine("Volume has group quotas enabled"); - - if(jfsSb.s_flags.HasFlag(Flags.NoJournal)) - sb.AppendLine("Volume is not using any journal"); - - if(jfsSb.s_flags.HasFlag(Flags.Discard)) - sb.AppendLine("Volume sends TRIM/UNMAP commands to underlying device"); - - if(jfsSb.s_flags.HasFlag(Flags.GroupCommit)) - sb.AppendLine("Volume commits in groups of 1"); - - if(jfsSb.s_flags.HasFlag(Flags.LazyCommit)) - sb.AppendLine("Volume commits lazy"); - - if(jfsSb.s_flags.HasFlag(Flags.Temporary)) - sb.AppendLine("Volume does not commit to log"); - - if(jfsSb.s_flags.HasFlag(Flags.InlineLog)) - sb.AppendLine("Volume has log withing itself"); - - if(jfsSb.s_flags.HasFlag(Flags.InlineMoving)) - sb.AppendLine("Volume has log withing itself and is moving it out"); - - if(jfsSb.s_flags.HasFlag(Flags.BadSAIT)) - sb.AppendLine("Volume has bad current secondary ait"); - - if(jfsSb.s_flags.HasFlag(Flags.Sparse)) - sb.AppendLine("Volume supports sparse files"); - - if(jfsSb.s_flags.HasFlag(Flags.DASDEnabled)) - sb.AppendLine("Volume has DASD limits enabled"); - - if(jfsSb.s_flags.HasFlag(Flags.DASDPrime)) - sb.AppendLine("Volume primes DASD on boot"); - - if(jfsSb.s_flags.HasFlag(Flags.SwapBytes)) - sb.AppendLine("Volume is in a big-endian system"); - - if(jfsSb.s_flags.HasFlag(Flags.DirIndex)) - sb.AppendLine("Volume has presistent indexes"); - - if(jfsSb.s_flags.HasFlag(Flags.Linux)) - sb.AppendLine("Volume supports Linux"); - - if(jfsSb.s_flags.HasFlag(Flags.DFS)) - sb.AppendLine("Volume supports DCE DFS LFS"); - - if(jfsSb.s_flags.HasFlag(Flags.OS2)) - sb.AppendLine("Volume supports OS/2, and is case insensitive"); - - if(jfsSb.s_flags.HasFlag(Flags.AIX)) - sb.AppendLine("Volume supports AIX"); - - if(jfsSb.s_state != 0) - sb.AppendLine("Volume is dirty"); - - sb.AppendFormat("Volume was last updated on {0}", - DateHandlers.UnixUnsignedToDateTime(jfsSb.s_time.tv_sec, jfsSb.s_time.tv_nsec)). - AppendLine(); - - if(jfsSb.s_version == 1) - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(jfsSb.s_fpack, Encoding)).AppendLine(); - else - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(jfsSb.s_label, Encoding)).AppendLine(); - - sb.AppendFormat("Volume UUID: {0}", jfsSb.s_uuid).AppendLine(); - - XmlFsType = new FileSystemType - { - Type = "JFS filesystem", - Clusters = jfsSb.s_size, - ClusterSize = jfsSb.s_bsize, - Bootable = true, - VolumeName = StringHandlers.CToString(jfsSb.s_version == 1 ? jfsSb.s_fpack : jfsSb.s_label, Encoding), - VolumeSerial = $"{jfsSb.s_uuid}", - ModificationDate = DateHandlers.UnixUnsignedToDateTime(jfsSb.s_time.tv_sec, jfsSb.s_time.tv_nsec), - ModificationDateSpecified = true - }; - - if(jfsSb.s_state != 0) - XmlFsType.Dirty = true; - - information = sb.ToString(); - } - - [Flags, SuppressMessage("ReSharper", "InconsistentNaming")] - enum Flags : uint - { - Unicode = 0x00000001, RemountRO = 0x00000002, Continue = 0x00000004, - Panic = 0x00000008, UserQuota = 0x00000010, GroupQuota = 0x00000020, - NoJournal = 0x00000040, Discard = 0x00000080, GroupCommit = 0x00000100, - LazyCommit = 0x00000200, Temporary = 0x00000400, InlineLog = 0x00000800, - InlineMoving = 0x00001000, BadSAIT = 0x00010000, Sparse = 0x00020000, - DASDEnabled = 0x00040000, DASDPrime = 0x00080000, SwapBytes = 0x00100000, - DirIndex = 0x00200000, Linux = 0x10000000, DFS = 0x20000000, - OS2 = 0x40000000, AIX = 0x80000000 - } - - [Flags] - enum State : uint - { - Clean = 0, Mounted = 1, Dirty = 2, - Logredo = 4, Extendfs = 8 - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Extent - { - /// Leftmost 24 bits are extent length, rest 8 bits are most significant for - public readonly uint len_addr; - public readonly uint addr2; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct TimeStruct - { - public readonly uint tv_sec; - public readonly uint tv_nsec; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - public readonly uint s_magic; - public readonly uint s_version; - public readonly ulong s_size; - public readonly uint s_bsize; - public readonly ushort s_l2bsize; - public readonly ushort s_l2bfactor; - public readonly uint s_pbsize; - public readonly ushort s_l1pbsize; - public readonly ushort pad; - public readonly uint s_agsize; - public readonly Flags s_flags; - public readonly State s_state; - public readonly uint s_compress; - public readonly Extent s_ait2; - public readonly Extent s_aim2; - public readonly uint s_logdev; - public readonly uint s_logserial; - public readonly Extent s_logpxd; - public readonly Extent s_fsckpxd; - public readonly TimeStruct s_time; - public readonly uint s_fsckloglen; - public readonly sbyte s_fscklog; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] s_fpack; - public readonly ulong s_xsize; - public readonly Extent s_xfsckpxd; - public readonly Extent s_xlogpxd; - public readonly Guid s_uuid; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] s_label; - public readonly Guid s_loguuid; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + public readonly uint s_magic; + public readonly uint s_version; + public readonly ulong s_size; + public readonly uint s_bsize; + public readonly ushort s_l2bsize; + public readonly ushort s_l2bfactor; + public readonly uint s_pbsize; + public readonly ushort s_l1pbsize; + public readonly ushort pad; + public readonly uint s_agsize; + public readonly Flags s_flags; + public readonly State s_state; + public readonly uint s_compress; + public readonly Extent s_ait2; + public readonly Extent s_aim2; + public readonly uint s_logdev; + public readonly uint s_logserial; + public readonly Extent s_logpxd; + public readonly Extent s_fsckpxd; + public readonly TimeStruct s_time; + public readonly uint s_fsckloglen; + public readonly sbyte s_fscklog; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] s_fpack; + public readonly ulong s_xsize; + public readonly Extent s_xfsckpxd; + public readonly Extent s_xlogpxd; + public readonly Guid s_uuid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] s_label; + public readonly Guid s_loguuid; } } \ No newline at end of file diff --git a/Aaru.Filesystems/LIF.cs b/Aaru.Filesystems/LIF.cs index abefb2db4..721cb4861 100644 --- a/Aaru.Filesystems/LIF.cs +++ b/Aaru.Filesystems/LIF.cs @@ -41,108 +41,107 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from http://www.hp9845.net/9845/projects/hpdir/#lif_filesystem +/// +/// Implements detection of the LIF filesystem +public sealed class LIF : IFilesystem { - // Information from http://www.hp9845.net/9845/projects/hpdir/#lif_filesystem + const uint LIF_MAGIC = 0x8000; + /// - /// Implements detection of the LIF filesystem - public sealed class LIF : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "HP Logical Interchange Format Plugin"; + /// + public Guid Id => new("41535647-77A5-477B-9206-DA727ACDC704"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint LIF_MAGIC = 0x8000; + if(imagePlugin.Info.SectorSize < 256) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "HP Logical Interchange Format Plugin"; - /// - public Guid Id => new("41535647-77A5-477B-9206-DA727ACDC704"); - /// - public string Author => "Natalia Portillo"; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(errno != ErrorNumber.NoError) + return false; + + SystemBlock lifSb = Marshal.ByteArrayToStructureBigEndian(sector); + AaruConsole.DebugWriteLine("LIF plugin", "magic 0x{0:X8} (expected 0x{1:X8})", lifSb.magic, LIF_MAGIC); + + return lifSb.magic == LIF_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + if(imagePlugin.Info.SectorSize < 256) + return; + + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + SystemBlock lifSb = Marshal.ByteArrayToStructureBigEndian(sector); + + if(lifSb.magic != LIF_MAGIC) + return; + + var sb = new StringBuilder(); + + sb.AppendLine("HP Logical Interchange Format"); + sb.AppendFormat("Directory starts at cluster {0}", lifSb.directoryStart).AppendLine(); + sb.AppendFormat("LIF identifier: {0}", lifSb.lifId).AppendLine(); + sb.AppendFormat("Directory size: {0} clusters", lifSb.directorySize).AppendLine(); + sb.AppendFormat("LIF version: {0}", lifSb.lifVersion).AppendLine(); + + // How is this related to volume size? I have only CDs to test and makes no sense there + sb.AppendFormat("{0} tracks", lifSb.tracks).AppendLine(); + sb.AppendFormat("{0} heads", lifSb.heads).AppendLine(); + sb.AppendFormat("{0} sectors", lifSb.sectors).AppendLine(); + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(lifSb.volumeLabel, Encoding)).AppendLine(); + sb.AppendFormat("Volume created on {0}", DateHandlers.LifToDateTime(lifSb.creationDate)).AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - if(imagePlugin.Info.SectorSize < 256) - return false; + Type = "HP Logical Interchange Format", + ClusterSize = 256, + Clusters = partition.Size / 256, + CreationDate = DateHandlers.LifToDateTime(lifSb.creationDate), + CreationDateSpecified = true, + VolumeName = StringHandlers.CToString(lifSb.volumeLabel, Encoding) + }; + } - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - SystemBlock lifSb = Marshal.ByteArrayToStructureBigEndian(sector); - AaruConsole.DebugWriteLine("LIF plugin", "magic 0x{0:X8} (expected 0x{1:X8})", lifSb.magic, LIF_MAGIC); - - return lifSb.magic == LIF_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - if(imagePlugin.Info.SectorSize < 256) - return; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - SystemBlock lifSb = Marshal.ByteArrayToStructureBigEndian(sector); - - if(lifSb.magic != LIF_MAGIC) - return; - - var sb = new StringBuilder(); - - sb.AppendLine("HP Logical Interchange Format"); - sb.AppendFormat("Directory starts at cluster {0}", lifSb.directoryStart).AppendLine(); - sb.AppendFormat("LIF identifier: {0}", lifSb.lifId).AppendLine(); - sb.AppendFormat("Directory size: {0} clusters", lifSb.directorySize).AppendLine(); - sb.AppendFormat("LIF version: {0}", lifSb.lifVersion).AppendLine(); - - // How is this related to volume size? I have only CDs to test and makes no sense there - sb.AppendFormat("{0} tracks", lifSb.tracks).AppendLine(); - sb.AppendFormat("{0} heads", lifSb.heads).AppendLine(); - sb.AppendFormat("{0} sectors", lifSb.sectors).AppendLine(); - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(lifSb.volumeLabel, Encoding)).AppendLine(); - sb.AppendFormat("Volume created on {0}", DateHandlers.LifToDateTime(lifSb.creationDate)).AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "HP Logical Interchange Format", - ClusterSize = 256, - Clusters = partition.Size / 256, - CreationDate = DateHandlers.LifToDateTime(lifSb.creationDate), - CreationDateSpecified = true, - VolumeName = StringHandlers.CToString(lifSb.volumeLabel, Encoding) - }; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SystemBlock - { - public readonly ushort magic; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] volumeLabel; - public readonly uint directoryStart; - public readonly ushort lifId; - public readonly ushort unused; - public readonly uint directorySize; - public readonly ushort lifVersion; - public readonly ushort unused2; - public readonly uint tracks; - public readonly uint heads; - public readonly uint sectors; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] creationDate; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SystemBlock + { + public readonly ushort magic; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] volumeLabel; + public readonly uint directoryStart; + public readonly ushort lifId; + public readonly ushort unused; + public readonly uint directorySize; + public readonly ushort lifVersion; + public readonly ushort unused2; + public readonly uint tracks; + public readonly uint heads; + public readonly uint sectors; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] creationDate; } } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/Consts.cs b/Aaru.Filesystems/LisaFS/Consts.cs index 9378d1f30..2406994f5 100644 --- a/Aaru.Filesystems/LisaFS/Consts.cs +++ b/Aaru.Filesystems/LisaFS/Consts.cs @@ -32,83 +32,82 @@ using System.Diagnostics.CodeAnalysis; -namespace Aaru.Filesystems.LisaFS +namespace Aaru.Filesystems.LisaFS; + +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class LisaFS { - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class LisaFS + /// Lisa FS v1, from Lisa OS 1.0 (Workshop or Office) Never seen on Sony floppies. + const byte LISA_V1 = 0x0E; + /// + /// Lisa FS v2, from Lisa OS 2.0 (Workshop or Office) Contrary to what most information online says the only + /// difference with V1 is the Extents File size. Catalog format is the same + /// + const byte LISA_V2 = 0x0F; + /// + /// Lisa FS v3, from Lisa OS 3.0 (Workshop or Office) Adds support for user catalogs (aka subdirectories), and + /// changes the catalog format from extents to double-linked list. Uses '-' as path separator (so people that created + /// Lisa/FILE.TEXT just created a file named like that :p) + /// + const byte LISA_V3 = 0x11; + /// Maximum string size in LisaFS + const uint E_NAME = 32; + /// Unused file ID + const ushort FILEID_FREE = 0x0000; + /// Used by the boot blocks + const ushort FILEID_BOOT = 0xAAAA; + /// Used by the operating system loader blocks + const ushort FILEID_LOADER = 0xBBBB; + /// Used by the MDDF + const ushort FILEID_MDDF = 0x0001; + /// Used by the volume bitmap, sits between MDDF and S-Records file. + const ushort FILEID_BITMAP = 0x0002; + /// S-Records file + const ushort FILEID_SRECORD = 0x0003; + /// The root catalog + const ushort FILEID_CATALOG = 0x0004; + const short FILEID_BOOT_SIGNED = -21846; + const short FILEID_LOADER_SIGNED = -17477; + /// A file that has been erased + const ushort FILEID_ERASED = 0x7FFF; + const ushort FILEID_MAX = FILEID_ERASED; + + /// Root directory ID + const short DIRID_ROOT = 0; + + enum FileType : byte { - /// Lisa FS v1, from Lisa OS 1.0 (Workshop or Office) Never seen on Sony floppies. - const byte LISA_V1 = 0x0E; - /// - /// Lisa FS v2, from Lisa OS 2.0 (Workshop or Office) Contrary to what most information online says the only - /// difference with V1 is the Extents File size. Catalog format is the same - /// - const byte LISA_V2 = 0x0F; - /// - /// Lisa FS v3, from Lisa OS 3.0 (Workshop or Office) Adds support for user catalogs (aka subdirectories), and - /// changes the catalog format from extents to double-linked list. Uses '-' as path separator (so people that created - /// Lisa/FILE.TEXT just created a file named like that :p) - /// - const byte LISA_V3 = 0x11; - /// Maximum string size in LisaFS - const uint E_NAME = 32; - /// Unused file ID - const ushort FILEID_FREE = 0x0000; - /// Used by the boot blocks - const ushort FILEID_BOOT = 0xAAAA; - /// Used by the operating system loader blocks - const ushort FILEID_LOADER = 0xBBBB; - /// Used by the MDDF - const ushort FILEID_MDDF = 0x0001; - /// Used by the volume bitmap, sits between MDDF and S-Records file. - const ushort FILEID_BITMAP = 0x0002; - /// S-Records file - const ushort FILEID_SRECORD = 0x0003; - /// The root catalog - const ushort FILEID_CATALOG = 0x0004; - const short FILEID_BOOT_SIGNED = -21846; - const short FILEID_LOADER_SIGNED = -17477; - /// A file that has been erased - const ushort FILEID_ERASED = 0x7FFF; - const ushort FILEID_MAX = FILEID_ERASED; - - /// Root directory ID - const short DIRID_ROOT = 0; - - enum FileType : byte - { - /// Undefined file type - Undefined = 0, - /// MDDF - MDDFile = 1, - /// Root catalog - RootCat = 2, - /// Bitmap - FreeList = 3, - /// Unknown, maybe refers to the S-Records File? - BadBlocks = 4, - /// System data - SysData = 5, - /// Printer spool - Spool = 6, - /// Executable. Yet application files don't use it - Exec = 7, - /// User catalog - UserCat = 8, - /// Pipe. Not seen on disk. - Pipe = 9, - /// Boot file? - BootFile = 10, - /// Swap for data - SwapData = 11, - /// Swap for code - SwapCode = 12, - /// Unknown - RamAP = 13, - /// Any file - UserFile = 14, - /// Erased? - KilledObject = 15 - } + /// Undefined file type + Undefined = 0, + /// MDDF + MDDFile = 1, + /// Root catalog + RootCat = 2, + /// Bitmap + FreeList = 3, + /// Unknown, maybe refers to the S-Records File? + BadBlocks = 4, + /// System data + SysData = 5, + /// Printer spool + Spool = 6, + /// Executable. Yet application files don't use it + Exec = 7, + /// User catalog + UserCat = 8, + /// Pipe. Not seen on disk. + Pipe = 9, + /// Boot file? + BootFile = 10, + /// Swap for data + SwapData = 11, + /// Swap for code + SwapCode = 12, + /// Unknown + RamAP = 13, + /// Any file + UserFile = 14, + /// Erased? + KilledObject = 15 } } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/Dir.cs b/Aaru.Filesystems/LisaFS/Dir.cs index 87774d659..ce01e9f80 100644 --- a/Aaru.Filesystems/LisaFS/Dir.cs +++ b/Aaru.Filesystems/LisaFS/Dir.cs @@ -38,337 +38,336 @@ using Aaru.CommonTypes.Structs; using Aaru.Decoders; using Aaru.Helpers; -namespace Aaru.Filesystems.LisaFS -{ - public sealed partial class LisaFS - { - /// - public ErrorNumber ReadLink(string path, out string dest) - { - dest = null; +namespace Aaru.Filesystems.LisaFS; - // LisaFS does not support symbolic links (afaik) - return ErrorNumber.NotSupported; +public sealed partial class LisaFS +{ + /// + public ErrorNumber ReadLink(string path, out string dest) + { + dest = null; + + // LisaFS does not support symbolic links (afaik) + return ErrorNumber.NotSupported; + } + + /// + public ErrorNumber ReadDir(string path, out List contents) + { + contents = null; + ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); + + if(error != ErrorNumber.NoError) + return error; + + if(!isDir) + return ErrorNumber.NotDirectory; + + /*List catalog; + error = ReadCatalog(fileId, out catalog); + if(error != ErrorNumber.NoError) + return error;*/ + + ReadDir(fileId, out contents); + + // On debug add system files as readable files + // Syntax similar to NTFS + if(_debug && fileId == DIRID_ROOT) + { + contents.Add("$MDDF"); + contents.Add("$Boot"); + contents.Add("$Loader"); + contents.Add("$Bitmap"); + contents.Add("$S-Record"); + contents.Add("$"); } - /// - public ErrorNumber ReadDir(string path, out List contents) + contents.Sort(); + + return ErrorNumber.NoError; + } + + void ReadDir(short dirId, out List contents) => + + // Do same trick as Mac OS X, replace filesystem '/' with '-', + // as '-' is the path separator in Lisa OS + contents = (from entry in _catalogCache where entry.parentID == dirId + select StringHandlers.CToString(entry.filename, Encoding).Replace('/', '-')).ToList(); + + /// Reads, interprets and caches the Catalog File + ErrorNumber ReadCatalog() + { + ErrorNumber errno; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + _catalogCache = new List(); + + // Do differently for V1 and V2 + if(_mddf.fsversion == LISA_V2 || + _mddf.fsversion == LISA_V1) { - contents = null; - ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); + ErrorNumber error = ReadFile((short)FILEID_CATALOG, out byte[] buf); if(error != ErrorNumber.NoError) return error; - if(!isDir) - return ErrorNumber.NotDirectory; + int offset = 0; + List catalogV2 = new(); - /*List catalog; - error = ReadCatalog(fileId, out catalog); - if(error != ErrorNumber.NoError) - return error;*/ - - ReadDir(fileId, out contents); - - // On debug add system files as readable files - // Syntax similar to NTFS - if(_debug && fileId == DIRID_ROOT) + // For each entry on the catalog + while(offset + 54 < buf.Length) { - contents.Add("$MDDF"); - contents.Add("$Boot"); - contents.Add("$Loader"); - contents.Add("$Bitmap"); - contents.Add("$S-Record"); - contents.Add("$"); + var entV2 = new CatalogEntryV2 + { + filenameLen = buf[offset], + filename = new byte[E_NAME], + unknown1 = buf[offset + 0x21], + fileType = buf[offset + 0x22], + unknown2 = buf[offset + 0x23], + fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x24), + unknown3 = new byte[16] + }; + + Array.Copy(buf, offset + 0x01, entV2.filename, 0, E_NAME); + Array.Copy(buf, offset + 0x26, entV2.unknown3, 0, 16); + + offset += 54; + + // Check that the entry is correct, not empty or garbage + if(entV2.filenameLen != 0 && + entV2.filenameLen <= E_NAME && + entV2.fileType != 0 && + entV2.fileID > 0) + catalogV2.Add(entV2); } - contents.Sort(); - - return ErrorNumber.NoError; - } - - void ReadDir(short dirId, out List contents) => - - // Do same trick as Mac OS X, replace filesystem '/' with '-', - // as '-' is the path separator in Lisa OS - contents = (from entry in _catalogCache where entry.parentID == dirId - select StringHandlers.CToString(entry.filename, Encoding).Replace('/', '-')).ToList(); - - /// Reads, interprets and caches the Catalog File - ErrorNumber ReadCatalog() - { - ErrorNumber errno; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - _catalogCache = new List(); - - // Do differently for V1 and V2 - if(_mddf.fsversion == LISA_V2 || - _mddf.fsversion == LISA_V1) + // Convert entries to V3 format + foreach(CatalogEntryV2 entV2 in catalogV2) { - ErrorNumber error = ReadFile((short)FILEID_CATALOG, out byte[] buf); + error = ReadExtentsFile(entV2.fileID, out ExtentFile ext); if(error != ErrorNumber.NoError) - return error; - - int offset = 0; - List catalogV2 = new(); - - // For each entry on the catalog - while(offset + 54 < buf.Length) - { - var entV2 = new CatalogEntryV2 - { - filenameLen = buf[offset], - filename = new byte[E_NAME], - unknown1 = buf[offset + 0x21], - fileType = buf[offset + 0x22], - unknown2 = buf[offset + 0x23], - fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x24), - unknown3 = new byte[16] - }; - - Array.Copy(buf, offset + 0x01, entV2.filename, 0, E_NAME); - Array.Copy(buf, offset + 0x26, entV2.unknown3, 0, 16); - - offset += 54; - - // Check that the entry is correct, not empty or garbage - if(entV2.filenameLen != 0 && - entV2.filenameLen <= E_NAME && - entV2.fileType != 0 && - entV2.fileID > 0) - catalogV2.Add(entV2); - } - - // Convert entries to V3 format - foreach(CatalogEntryV2 entV2 in catalogV2) - { - error = ReadExtentsFile(entV2.fileID, out ExtentFile ext); - - if(error != ErrorNumber.NoError) - continue; - - var entV3 = new CatalogEntry - { - fileID = entV2.fileID, - filename = new byte[32], - fileType = entV2.fileType, - length = (int)_srecords[entV2.fileID].filesize, - dtc = ext.dtc, - dtm = ext.dtm - }; - - Array.Copy(entV2.filename, 0, entV3.filename, 0, entV2.filenameLen); - - _catalogCache.Add(entV3); - } - - return ErrorNumber.NoError; - } - - byte[] firstCatalogBlock = null; - - // Search for the first sector describing the catalog - // While root catalog is not stored in S-Records, probably rest are? (unchecked) - // If root catalog is not pointed in MDDF (unchecked) maybe it's always following S-Records File? - for(ulong i = 0; i < _device.Info.Sectors; i++) - { - errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out byte[] tag); - - if(errno != ErrorNumber.NoError) continue; - DecodeTag(tag, out LisaTag.PriamTag catTag); + var entV3 = new CatalogEntry + { + fileID = entV2.fileID, + filename = new byte[32], + fileType = entV2.fileType, + length = (int)_srecords[entV2.fileID].filesize, + dtc = ext.dtc, + dtm = ext.dtm + }; - if(catTag.FileId != FILEID_CATALOG || - catTag.RelPage != 0) - continue; + Array.Copy(entV2.filename, 0, entV3.filename, 0, entV2.filenameLen); - errno = _device.ReadSectors(i, 4, out firstCatalogBlock); - - if(errno != ErrorNumber.NoError) - return errno; - - break; - } - - // Catalog not found - if(firstCatalogBlock == null) - return ErrorNumber.NoSuchFile; - - ulong prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6); - - // Traverse double-linked list until first catalog block - while(prevCatalogPointer != 0xFFFFFFFF) - { - errno = _device.ReadSectorTag(prevCatalogPointer + _mddf.mddf_block + _volumePrefix, - SectorTagType.AppleSectorTag, out byte[] tag); - - if(errno != ErrorNumber.NoError) - return errno; - - DecodeTag(tag, out LisaTag.PriamTag prevTag); - - if(prevTag.FileId != FILEID_CATALOG) - return ErrorNumber.InvalidArgument; - - errno = _device.ReadSectors(prevCatalogPointer + _mddf.mddf_block + _volumePrefix, 4, - out firstCatalogBlock); - - if(errno != ErrorNumber.NoError) - return errno; - - prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6); - } - - ulong nextCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7FA); - - List catalogBlocks = new() - { - firstCatalogBlock - }; - - // Traverse double-linked list to read full catalog - while(nextCatalogPointer != 0xFFFFFFFF) - { - errno = _device.ReadSectorTag(nextCatalogPointer + _mddf.mddf_block + _volumePrefix, - SectorTagType.AppleSectorTag, out byte[] tag); - - if(errno != ErrorNumber.NoError) - return errno; - - DecodeTag(tag, out LisaTag.PriamTag nextTag); - - if(nextTag.FileId != FILEID_CATALOG) - return ErrorNumber.InvalidArgument; - - errno = _device.ReadSectors(nextCatalogPointer + _mddf.mddf_block + _volumePrefix, 4, - out byte[] nextCatalogBlock); - - if(errno != ErrorNumber.NoError) - return errno; - - nextCatalogPointer = BigEndianBitConverter.ToUInt32(nextCatalogBlock, 0x7FA); - catalogBlocks.Add(nextCatalogBlock); - } - - // Foreach catalog block - foreach(byte[] buf in catalogBlocks) - { - int offset = 0; - - // Traverse all entries - while(offset + 64 <= buf.Length) - - // Catalog block header - if(buf[offset + 0x24] == 0x08) - offset += 78; - - // Maybe just garbage? Found in more than 1 disk - else if(buf[offset + 0x24] == 0x7C) - offset += 50; - - // Apparently reserved to indicate end of catalog? - else if(buf[offset + 0x24] == 0xFF) - break; - - // Normal entry - else if(buf[offset + 0x24] == 0x03 && - buf[offset] == 0x24) - { - var entry = new CatalogEntry - { - marker = buf[offset], - parentID = BigEndianBitConverter.ToUInt16(buf, offset + 0x01), - filename = new byte[E_NAME], - terminator = buf[offset + 0x23], - fileType = buf[offset + 0x24], - unknown = buf[offset + 0x25], - fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x26), - dtc = BigEndianBitConverter.ToUInt32(buf, offset + 0x28), - dtm = BigEndianBitConverter.ToUInt32(buf, offset + 0x2C), - length = BigEndianBitConverter.ToInt32(buf, offset + 0x30), - wasted = BigEndianBitConverter.ToInt32(buf, offset + 0x34), - tail = new byte[8] - }; - - Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME); - Array.Copy(buf, offset + 0x38, entry.tail, 0, 8); - - if(ReadExtentsFile(entry.fileID, out _) == ErrorNumber.NoError) - if(!_fileSizeCache.ContainsKey(entry.fileID)) - { - _catalogCache.Add(entry); - _fileSizeCache.Add(entry.fileID, entry.length); - } - - offset += 64; - } - - // Subdirectory entry - else if(buf[offset + 0x24] == 0x01 && - buf[offset] == 0x24) - { - var entry = new CatalogEntry - { - marker = buf[offset], - parentID = BigEndianBitConverter.ToUInt16(buf, offset + 0x01), - filename = new byte[E_NAME], - terminator = buf[offset + 0x23], - fileType = buf[offset + 0x24], - unknown = buf[offset + 0x25], - fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x26), - dtc = BigEndianBitConverter.ToUInt32(buf, offset + 0x28), - dtm = BigEndianBitConverter.ToUInt32(buf, offset + 0x2C), - length = 0, - wasted = 0, - tail = null - }; - - Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME); - - if(!_directoryDtcCache.ContainsKey(entry.fileID)) - _directoryDtcCache.Add(entry.fileID, DateHandlers.LisaToDateTime(entry.dtc)); - - _catalogCache.Add(entry); - - offset += 48; - } - else - break; + _catalogCache.Add(entV3); } return ErrorNumber.NoError; } - ErrorNumber StatDir(short dirId, out FileEntryInfo stat) + byte[] firstCatalogBlock = null; + + // Search for the first sector describing the catalog + // While root catalog is not stored in S-Records, probably rest are? (unchecked) + // If root catalog is not pointed in MDDF (unchecked) maybe it's always following S-Records File? + for(ulong i = 0; i < _device.Info.Sectors; i++) { - stat = null; + errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out byte[] tag); - if(!_mounted) - return ErrorNumber.AccessDenied; + if(errno != ErrorNumber.NoError) + continue; - stat = new FileEntryInfo - { - Attributes = new FileAttributes(), - Inode = FILEID_CATALOG, - Mode = 0x16D, - Links = 0, - UID = 0, - GID = 0, - DeviceNo = 0, - Length = 0, - BlockSize = _mddf.datasize, - Blocks = 0 - }; + DecodeTag(tag, out LisaTag.PriamTag catTag); - _directoryDtcCache.TryGetValue(dirId, out DateTime tmp); - stat.CreationTime = tmp; + if(catTag.FileId != FILEID_CATALOG || + catTag.RelPage != 0) + continue; - return ErrorNumber.NoError; + errno = _device.ReadSectors(i, 4, out firstCatalogBlock); + + if(errno != ErrorNumber.NoError) + return errno; + + break; } + + // Catalog not found + if(firstCatalogBlock == null) + return ErrorNumber.NoSuchFile; + + ulong prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6); + + // Traverse double-linked list until first catalog block + while(prevCatalogPointer != 0xFFFFFFFF) + { + errno = _device.ReadSectorTag(prevCatalogPointer + _mddf.mddf_block + _volumePrefix, + SectorTagType.AppleSectorTag, out byte[] tag); + + if(errno != ErrorNumber.NoError) + return errno; + + DecodeTag(tag, out LisaTag.PriamTag prevTag); + + if(prevTag.FileId != FILEID_CATALOG) + return ErrorNumber.InvalidArgument; + + errno = _device.ReadSectors(prevCatalogPointer + _mddf.mddf_block + _volumePrefix, 4, + out firstCatalogBlock); + + if(errno != ErrorNumber.NoError) + return errno; + + prevCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7F6); + } + + ulong nextCatalogPointer = BigEndianBitConverter.ToUInt32(firstCatalogBlock, 0x7FA); + + List catalogBlocks = new() + { + firstCatalogBlock + }; + + // Traverse double-linked list to read full catalog + while(nextCatalogPointer != 0xFFFFFFFF) + { + errno = _device.ReadSectorTag(nextCatalogPointer + _mddf.mddf_block + _volumePrefix, + SectorTagType.AppleSectorTag, out byte[] tag); + + if(errno != ErrorNumber.NoError) + return errno; + + DecodeTag(tag, out LisaTag.PriamTag nextTag); + + if(nextTag.FileId != FILEID_CATALOG) + return ErrorNumber.InvalidArgument; + + errno = _device.ReadSectors(nextCatalogPointer + _mddf.mddf_block + _volumePrefix, 4, + out byte[] nextCatalogBlock); + + if(errno != ErrorNumber.NoError) + return errno; + + nextCatalogPointer = BigEndianBitConverter.ToUInt32(nextCatalogBlock, 0x7FA); + catalogBlocks.Add(nextCatalogBlock); + } + + // Foreach catalog block + foreach(byte[] buf in catalogBlocks) + { + int offset = 0; + + // Traverse all entries + while(offset + 64 <= buf.Length) + + // Catalog block header + if(buf[offset + 0x24] == 0x08) + offset += 78; + + // Maybe just garbage? Found in more than 1 disk + else if(buf[offset + 0x24] == 0x7C) + offset += 50; + + // Apparently reserved to indicate end of catalog? + else if(buf[offset + 0x24] == 0xFF) + break; + + // Normal entry + else if(buf[offset + 0x24] == 0x03 && + buf[offset] == 0x24) + { + var entry = new CatalogEntry + { + marker = buf[offset], + parentID = BigEndianBitConverter.ToUInt16(buf, offset + 0x01), + filename = new byte[E_NAME], + terminator = buf[offset + 0x23], + fileType = buf[offset + 0x24], + unknown = buf[offset + 0x25], + fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x26), + dtc = BigEndianBitConverter.ToUInt32(buf, offset + 0x28), + dtm = BigEndianBitConverter.ToUInt32(buf, offset + 0x2C), + length = BigEndianBitConverter.ToInt32(buf, offset + 0x30), + wasted = BigEndianBitConverter.ToInt32(buf, offset + 0x34), + tail = new byte[8] + }; + + Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME); + Array.Copy(buf, offset + 0x38, entry.tail, 0, 8); + + if(ReadExtentsFile(entry.fileID, out _) == ErrorNumber.NoError) + if(!_fileSizeCache.ContainsKey(entry.fileID)) + { + _catalogCache.Add(entry); + _fileSizeCache.Add(entry.fileID, entry.length); + } + + offset += 64; + } + + // Subdirectory entry + else if(buf[offset + 0x24] == 0x01 && + buf[offset] == 0x24) + { + var entry = new CatalogEntry + { + marker = buf[offset], + parentID = BigEndianBitConverter.ToUInt16(buf, offset + 0x01), + filename = new byte[E_NAME], + terminator = buf[offset + 0x23], + fileType = buf[offset + 0x24], + unknown = buf[offset + 0x25], + fileID = BigEndianBitConverter.ToInt16(buf, offset + 0x26), + dtc = BigEndianBitConverter.ToUInt32(buf, offset + 0x28), + dtm = BigEndianBitConverter.ToUInt32(buf, offset + 0x2C), + length = 0, + wasted = 0, + tail = null + }; + + Array.Copy(buf, offset + 0x03, entry.filename, 0, E_NAME); + + if(!_directoryDtcCache.ContainsKey(entry.fileID)) + _directoryDtcCache.Add(entry.fileID, DateHandlers.LisaToDateTime(entry.dtc)); + + _catalogCache.Add(entry); + + offset += 48; + } + else + break; + } + + return ErrorNumber.NoError; + } + + ErrorNumber StatDir(short dirId, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + stat = new FileEntryInfo + { + Attributes = new FileAttributes(), + Inode = FILEID_CATALOG, + Mode = 0x16D, + Links = 0, + UID = 0, + GID = 0, + DeviceNo = 0, + Length = 0, + BlockSize = _mddf.datasize, + Blocks = 0 + }; + + _directoryDtcCache.TryGetValue(dirId, out DateTime tmp); + stat.CreationTime = tmp; + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/Extent.cs b/Aaru.Filesystems/LisaFS/Extent.cs index 3fb788ddb..11f7a81e3 100644 --- a/Aaru.Filesystems/LisaFS/Extent.cs +++ b/Aaru.Filesystems/LisaFS/Extent.cs @@ -36,293 +36,292 @@ using Aaru.Console; using Aaru.Decoders; using Aaru.Helpers; -namespace Aaru.Filesystems.LisaFS +namespace Aaru.Filesystems.LisaFS; + +public sealed partial class LisaFS { - public sealed partial class LisaFS + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) { - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) - { - deviceBlock = 0; + deviceBlock = 0; - // TODO: Not really important. - return ErrorNumber.NotImplemented; - } + // TODO: Not really important. + return ErrorNumber.NotImplemented; + } - /// Searches the disk for an extents file (or gets it from cache) - /// Error. - /// File identifier. - /// Extents file. - ErrorNumber ReadExtentsFile(short fileId, out ExtentFile file) - { - file = new ExtentFile(); - ErrorNumber errno; + /// Searches the disk for an extents file (or gets it from cache) + /// Error. + /// File identifier. + /// Extents file. + ErrorNumber ReadExtentsFile(short fileId, out ExtentFile file) + { + file = new ExtentFile(); + ErrorNumber errno; - if(!_mounted) - return ErrorNumber.AccessDenied; + if(!_mounted) + return ErrorNumber.AccessDenied; - if(fileId < 4 || - (fileId == 4 && _mddf.fsversion != LISA_V2 && _mddf.fsversion != LISA_V1)) - return ErrorNumber.InvalidArgument; - - if(_extentCache.TryGetValue(fileId, out file)) - return ErrorNumber.NoError; - - // A file ID that cannot be stored in the S-Records File - if(fileId >= _srecords.Length) - return ErrorNumber.InvalidArgument; - - ulong ptr = _srecords[fileId].extent_ptr; - - // An invalid pointer denotes file does not exist - if(ptr == 0xFFFFFFFF || - ptr == 0x00000000) - return ErrorNumber.NoSuchFile; - - // Pointers are relative to MDDF - ptr += _mddf.mddf_block + _volumePrefix; - - LisaTag.PriamTag extTag; - byte[] tag; - - // This happens on some disks. - // This is a filesystem corruption that makes LisaOS crash on scavenge. - // This code just allow to ignore that corruption by searching the Extents File using sector tags - if(ptr >= _device.Info.Sectors) - { - bool found = false; - - for(ulong i = 0; i < _device.Info.Sectors; i++) - { - errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out tag); - - if(errno != ErrorNumber.NoError) - continue; - - DecodeTag(tag, out extTag); - - if(extTag.FileId != fileId * -1) - continue; - - ptr = i; - found = true; - - break; - } - - if(!found) - return ErrorNumber.InvalidArgument; - } - - // Checks that the sector tag indicates its the Extents File we are searching for - errno = _device.ReadSectorTag(ptr, SectorTagType.AppleSectorTag, out tag); - - if(errno != ErrorNumber.NoError) - return errno; - - DecodeTag(tag, out extTag); - - if(extTag.FileId != (short)(-1 * fileId)) - return ErrorNumber.NoSuchFile; - - byte[] sector; - - errno = _mddf.fsversion == LISA_V1 ? _device.ReadSectors(ptr, 2, out sector) - : _device.ReadSector(ptr, out sector); - - if(errno != ErrorNumber.NoError) - return errno; - - if(sector[0] >= 32 || - sector[0] == 0) - return ErrorNumber.InvalidArgument; - - file.filenameLen = sector[0]; - file.filename = new byte[file.filenameLen]; - Array.Copy(sector, 0x01, file.filename, 0, file.filenameLen); - file.unknown1 = BigEndianBitConverter.ToUInt16(sector, 0x20); - file.file_uid = BigEndianBitConverter.ToUInt64(sector, 0x22); - file.unknown2 = sector[0x2A]; - file.etype = sector[0x2B]; - file.ftype = (FileType)sector[0x2C]; - file.unknown3 = sector[0x2D]; - file.dtc = BigEndianBitConverter.ToUInt32(sector, 0x2E); - file.dta = BigEndianBitConverter.ToUInt32(sector, 0x32); - file.dtm = BigEndianBitConverter.ToUInt32(sector, 0x36); - file.dtb = BigEndianBitConverter.ToUInt32(sector, 0x3A); - file.dts = BigEndianBitConverter.ToUInt32(sector, 0x3E); - file.serial = BigEndianBitConverter.ToUInt32(sector, 0x42); - file.unknown4 = sector[0x46]; - file.locked = sector[0x47]; - file.protect = sector[0x48]; - file.master = sector[0x49]; - file.scavenged = sector[0x4A]; - file.closed = sector[0x4B]; - file.open = sector[0x4C]; - file.unknown5 = new byte[11]; - Array.Copy(sector, 0x4D, file.unknown5, 0, 11); - file.release = BigEndianBitConverter.ToUInt16(sector, 0x58); - file.build = BigEndianBitConverter.ToUInt16(sector, 0x5A); - file.compatibility = BigEndianBitConverter.ToUInt16(sector, 0x5C); - file.revision = BigEndianBitConverter.ToUInt16(sector, 0x5E); - file.unknown6 = BigEndianBitConverter.ToUInt16(sector, 0x60); - file.password_valid = sector[0x62]; - file.password = new byte[8]; - Array.Copy(sector, 0x63, file.password, 0, 8); - file.unknown7 = new byte[3]; - Array.Copy(sector, 0x6B, file.unknown7, 0, 3); - file.overhead = BigEndianBitConverter.ToUInt16(sector, 0x6E); - file.unknown8 = new byte[16]; - Array.Copy(sector, 0x70, file.unknown8, 0, 16); - file.unknown10 = BigEndianBitConverter.ToInt16(sector, 0x17E); - file.LisaInfo = new byte[128]; - Array.Copy(sector, 0x180, file.LisaInfo, 0, 128); - - int extentsCount = 0; - int extentsOffset; - - if(_mddf.fsversion == LISA_V1) - { - file.length = BigEndianBitConverter.ToInt32(sector, 0x200); - file.unknown9 = BigEndianBitConverter.ToInt32(sector, 0x204); - extentsOffset = 0x208; - } - else - { - file.length = BigEndianBitConverter.ToInt32(sector, 0x80); - file.unknown9 = BigEndianBitConverter.ToInt32(sector, 0x84); - extentsOffset = 0x88; - } - - for(int j = 0; j < 41; j++) - { - if(BigEndianBitConverter.ToInt16(sector, extentsOffset + (j * 6) + 4) == 0) - break; - - extentsCount++; - } - - file.extents = new Extent[extentsCount]; - - for(int j = 0; j < extentsCount; j++) - file.extents[j] = new Extent - { - start = BigEndianBitConverter.ToInt32(sector, extentsOffset + (j * 6)), - length = BigEndianBitConverter.ToInt16(sector, extentsOffset + (j * 6) + 4) - }; - - _extentCache.Add(fileId, file); - - if(!_debug) - return ErrorNumber.NoError; - - if(_printedExtents.Contains(fileId)) - return ErrorNumber.NoError; - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].filenameLen = {1}", fileId, file.filenameLen); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].filename = {1}", fileId, - StringHandlers.CToString(file.filename, Encoding)); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown1 = 0x{1:X4}", fileId, file.unknown1); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].file_uid = 0x{1:X16}", fileId, file.file_uid); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown2 = 0x{1:X2}", fileId, file.unknown2); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].etype = 0x{1:X2}", fileId, file.etype); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].ftype = {1}", fileId, file.ftype); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown3 = 0x{1:X2}", fileId, file.unknown3); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dtc = {1}", fileId, file.dtc); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dta = {1}", fileId, file.dta); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dtm = {1}", fileId, file.dtm); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dtb = {1}", fileId, file.dtb); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dts = {1}", fileId, file.dts); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].serial = {1}", fileId, file.serial); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown4 = 0x{1:X2}", fileId, file.unknown4); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].locked = {1}", fileId, file.locked > 0); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].protect = {1}", fileId, file.protect > 0); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].master = {1}", fileId, file.master > 0); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].scavenged = {1}", fileId, file.scavenged > 0); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].closed = {1}", fileId, file.closed > 0); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].open = {1}", fileId, file.open > 0); - - AaruConsole.DebugWriteLine("LisaFS plugin", - "ExtentFile[{0}].unknown5 = 0x{1:X2}{2:X2}{3:X2}{4:X2}{5:X2}{6:X2}{7:X2}{8:X2}{9:X2}" + - "{10:X2}{11:X2}", fileId, file.unknown5[0], file.unknown5[1], file.unknown5[2], - file.unknown5[3], file.unknown5[4], file.unknown5[5], file.unknown5[6], - file.unknown5[7], file.unknown5[8], file.unknown5[9], file.unknown5[10]); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].release = {1}", fileId, file.release); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].build = {1}", fileId, file.build); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].compatibility = {1}", fileId, - file.compatibility); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].revision = {1}", fileId, file.revision); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown6 = 0x{1:X4}", fileId, file.unknown6); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].password_valid = {1}", fileId, - file.password_valid > 0); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].password = {1}", fileId, - Encoding.GetString(file.password)); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown7 = 0x{1:X2}{2:X2}{3:X2}", fileId, - file.unknown7[0], file.unknown7[1], file.unknown7[2]); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].overhead = {1}", fileId, file.overhead); - - AaruConsole.DebugWriteLine("LisaFS plugin", - "ExtentFile[{0}].unknown8 = 0x{1:X2}{2:X2}{3:X2}{4:X2}{5:X2}{6:X2}{7:X2}{8:X2}{9:X2}" + - "{10:X2}{11:X2}{12:X2}{13:X2}{14:X2}{15:X2}{16:X2}", fileId, file.unknown8[0], - file.unknown8[1], file.unknown8[2], file.unknown8[3], file.unknown8[4], - file.unknown8[5], file.unknown8[6], file.unknown8[7], file.unknown8[8], - file.unknown8[9], file.unknown8[10], file.unknown8[11], file.unknown8[12], - file.unknown8[13], file.unknown8[14], file.unknown8[15]); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].length = {1}", fileId, file.length); - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown9 = 0x{1:X8}", fileId, file.unknown9); - - for(int ext = 0; ext < file.extents.Length; ext++) - { - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].extents[{1}].start = {2}", fileId, ext, - file.extents[ext].start); - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].extents[{1}].length = {2}", fileId, ext, - file.extents[ext].length); - } - - AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown10 = 0x{1:X4}", fileId, file.unknown10); - - _printedExtents.Add(fileId); + if(fileId < 4 || + (fileId == 4 && _mddf.fsversion != LISA_V2 && _mddf.fsversion != LISA_V1)) + return ErrorNumber.InvalidArgument; + if(_extentCache.TryGetValue(fileId, out file)) return ErrorNumber.NoError; - } - /// Reads all the S-Records and caches it - ErrorNumber ReadSRecords() + // A file ID that cannot be stored in the S-Records File + if(fileId >= _srecords.Length) + return ErrorNumber.InvalidArgument; + + ulong ptr = _srecords[fileId].extent_ptr; + + // An invalid pointer denotes file does not exist + if(ptr == 0xFFFFFFFF || + ptr == 0x00000000) + return ErrorNumber.NoSuchFile; + + // Pointers are relative to MDDF + ptr += _mddf.mddf_block + _volumePrefix; + + LisaTag.PriamTag extTag; + byte[] tag; + + // This happens on some disks. + // This is a filesystem corruption that makes LisaOS crash on scavenge. + // This code just allow to ignore that corruption by searching the Extents File using sector tags + if(ptr >= _device.Info.Sectors) { - if(!_mounted) - return ErrorNumber.AccessDenied; + bool found = false; - // Searches the S-Records place using MDDF pointers - ErrorNumber errno = _device.ReadSectors(_mddf.srec_ptr + _mddf.mddf_block + _volumePrefix, _mddf.srec_len, - out byte[] sectors); + for(ulong i = 0; i < _device.Info.Sectors; i++) + { + errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out tag); - if(errno != ErrorNumber.NoError) - return errno; + if(errno != ErrorNumber.NoError) + continue; - // Each entry takes 14 bytes - _srecords = new SRecord[sectors.Length / 14]; + DecodeTag(tag, out extTag); - for(int s = 0; s < _srecords.Length; s++) - _srecords[s] = new SRecord - { - extent_ptr = BigEndianBitConverter.ToUInt32(sectors, 0x00 + (14 * s)), - unknown = BigEndianBitConverter.ToUInt32(sectors, 0x04 + (14 * s)), - filesize = BigEndianBitConverter.ToUInt32(sectors, 0x08 + (14 * s)), - flags = BigEndianBitConverter.ToUInt16(sectors, 0x0C + (14 * s)) - }; + if(extTag.FileId != fileId * -1) + continue; - return ErrorNumber.NoError; + ptr = i; + found = true; + + break; + } + + if(!found) + return ErrorNumber.InvalidArgument; } + + // Checks that the sector tag indicates its the Extents File we are searching for + errno = _device.ReadSectorTag(ptr, SectorTagType.AppleSectorTag, out tag); + + if(errno != ErrorNumber.NoError) + return errno; + + DecodeTag(tag, out extTag); + + if(extTag.FileId != (short)(-1 * fileId)) + return ErrorNumber.NoSuchFile; + + byte[] sector; + + errno = _mddf.fsversion == LISA_V1 ? _device.ReadSectors(ptr, 2, out sector) + : _device.ReadSector(ptr, out sector); + + if(errno != ErrorNumber.NoError) + return errno; + + if(sector[0] >= 32 || + sector[0] == 0) + return ErrorNumber.InvalidArgument; + + file.filenameLen = sector[0]; + file.filename = new byte[file.filenameLen]; + Array.Copy(sector, 0x01, file.filename, 0, file.filenameLen); + file.unknown1 = BigEndianBitConverter.ToUInt16(sector, 0x20); + file.file_uid = BigEndianBitConverter.ToUInt64(sector, 0x22); + file.unknown2 = sector[0x2A]; + file.etype = sector[0x2B]; + file.ftype = (FileType)sector[0x2C]; + file.unknown3 = sector[0x2D]; + file.dtc = BigEndianBitConverter.ToUInt32(sector, 0x2E); + file.dta = BigEndianBitConverter.ToUInt32(sector, 0x32); + file.dtm = BigEndianBitConverter.ToUInt32(sector, 0x36); + file.dtb = BigEndianBitConverter.ToUInt32(sector, 0x3A); + file.dts = BigEndianBitConverter.ToUInt32(sector, 0x3E); + file.serial = BigEndianBitConverter.ToUInt32(sector, 0x42); + file.unknown4 = sector[0x46]; + file.locked = sector[0x47]; + file.protect = sector[0x48]; + file.master = sector[0x49]; + file.scavenged = sector[0x4A]; + file.closed = sector[0x4B]; + file.open = sector[0x4C]; + file.unknown5 = new byte[11]; + Array.Copy(sector, 0x4D, file.unknown5, 0, 11); + file.release = BigEndianBitConverter.ToUInt16(sector, 0x58); + file.build = BigEndianBitConverter.ToUInt16(sector, 0x5A); + file.compatibility = BigEndianBitConverter.ToUInt16(sector, 0x5C); + file.revision = BigEndianBitConverter.ToUInt16(sector, 0x5E); + file.unknown6 = BigEndianBitConverter.ToUInt16(sector, 0x60); + file.password_valid = sector[0x62]; + file.password = new byte[8]; + Array.Copy(sector, 0x63, file.password, 0, 8); + file.unknown7 = new byte[3]; + Array.Copy(sector, 0x6B, file.unknown7, 0, 3); + file.overhead = BigEndianBitConverter.ToUInt16(sector, 0x6E); + file.unknown8 = new byte[16]; + Array.Copy(sector, 0x70, file.unknown8, 0, 16); + file.unknown10 = BigEndianBitConverter.ToInt16(sector, 0x17E); + file.LisaInfo = new byte[128]; + Array.Copy(sector, 0x180, file.LisaInfo, 0, 128); + + int extentsCount = 0; + int extentsOffset; + + if(_mddf.fsversion == LISA_V1) + { + file.length = BigEndianBitConverter.ToInt32(sector, 0x200); + file.unknown9 = BigEndianBitConverter.ToInt32(sector, 0x204); + extentsOffset = 0x208; + } + else + { + file.length = BigEndianBitConverter.ToInt32(sector, 0x80); + file.unknown9 = BigEndianBitConverter.ToInt32(sector, 0x84); + extentsOffset = 0x88; + } + + for(int j = 0; j < 41; j++) + { + if(BigEndianBitConverter.ToInt16(sector, extentsOffset + (j * 6) + 4) == 0) + break; + + extentsCount++; + } + + file.extents = new Extent[extentsCount]; + + for(int j = 0; j < extentsCount; j++) + file.extents[j] = new Extent + { + start = BigEndianBitConverter.ToInt32(sector, extentsOffset + (j * 6)), + length = BigEndianBitConverter.ToInt16(sector, extentsOffset + (j * 6) + 4) + }; + + _extentCache.Add(fileId, file); + + if(!_debug) + return ErrorNumber.NoError; + + if(_printedExtents.Contains(fileId)) + return ErrorNumber.NoError; + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].filenameLen = {1}", fileId, file.filenameLen); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].filename = {1}", fileId, + StringHandlers.CToString(file.filename, Encoding)); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown1 = 0x{1:X4}", fileId, file.unknown1); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].file_uid = 0x{1:X16}", fileId, file.file_uid); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown2 = 0x{1:X2}", fileId, file.unknown2); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].etype = 0x{1:X2}", fileId, file.etype); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].ftype = {1}", fileId, file.ftype); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown3 = 0x{1:X2}", fileId, file.unknown3); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dtc = {1}", fileId, file.dtc); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dta = {1}", fileId, file.dta); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dtm = {1}", fileId, file.dtm); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dtb = {1}", fileId, file.dtb); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].dts = {1}", fileId, file.dts); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].serial = {1}", fileId, file.serial); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown4 = 0x{1:X2}", fileId, file.unknown4); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].locked = {1}", fileId, file.locked > 0); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].protect = {1}", fileId, file.protect > 0); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].master = {1}", fileId, file.master > 0); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].scavenged = {1}", fileId, file.scavenged > 0); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].closed = {1}", fileId, file.closed > 0); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].open = {1}", fileId, file.open > 0); + + AaruConsole.DebugWriteLine("LisaFS plugin", + "ExtentFile[{0}].unknown5 = 0x{1:X2}{2:X2}{3:X2}{4:X2}{5:X2}{6:X2}{7:X2}{8:X2}{9:X2}" + + "{10:X2}{11:X2}", fileId, file.unknown5[0], file.unknown5[1], file.unknown5[2], + file.unknown5[3], file.unknown5[4], file.unknown5[5], file.unknown5[6], + file.unknown5[7], file.unknown5[8], file.unknown5[9], file.unknown5[10]); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].release = {1}", fileId, file.release); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].build = {1}", fileId, file.build); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].compatibility = {1}", fileId, + file.compatibility); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].revision = {1}", fileId, file.revision); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown6 = 0x{1:X4}", fileId, file.unknown6); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].password_valid = {1}", fileId, + file.password_valid > 0); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].password = {1}", fileId, + Encoding.GetString(file.password)); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown7 = 0x{1:X2}{2:X2}{3:X2}", fileId, + file.unknown7[0], file.unknown7[1], file.unknown7[2]); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].overhead = {1}", fileId, file.overhead); + + AaruConsole.DebugWriteLine("LisaFS plugin", + "ExtentFile[{0}].unknown8 = 0x{1:X2}{2:X2}{3:X2}{4:X2}{5:X2}{6:X2}{7:X2}{8:X2}{9:X2}" + + "{10:X2}{11:X2}{12:X2}{13:X2}{14:X2}{15:X2}{16:X2}", fileId, file.unknown8[0], + file.unknown8[1], file.unknown8[2], file.unknown8[3], file.unknown8[4], + file.unknown8[5], file.unknown8[6], file.unknown8[7], file.unknown8[8], + file.unknown8[9], file.unknown8[10], file.unknown8[11], file.unknown8[12], + file.unknown8[13], file.unknown8[14], file.unknown8[15]); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].length = {1}", fileId, file.length); + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown9 = 0x{1:X8}", fileId, file.unknown9); + + for(int ext = 0; ext < file.extents.Length; ext++) + { + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].extents[{1}].start = {2}", fileId, ext, + file.extents[ext].start); + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].extents[{1}].length = {2}", fileId, ext, + file.extents[ext].length); + } + + AaruConsole.DebugWriteLine("LisaFS plugin", "ExtentFile[{0}].unknown10 = 0x{1:X4}", fileId, file.unknown10); + + _printedExtents.Add(fileId); + + return ErrorNumber.NoError; + } + + /// Reads all the S-Records and caches it + ErrorNumber ReadSRecords() + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + // Searches the S-Records place using MDDF pointers + ErrorNumber errno = _device.ReadSectors(_mddf.srec_ptr + _mddf.mddf_block + _volumePrefix, _mddf.srec_len, + out byte[] sectors); + + if(errno != ErrorNumber.NoError) + return errno; + + // Each entry takes 14 bytes + _srecords = new SRecord[sectors.Length / 14]; + + for(int s = 0; s < _srecords.Length; s++) + _srecords[s] = new SRecord + { + extent_ptr = BigEndianBitConverter.ToUInt32(sectors, 0x00 + (14 * s)), + unknown = BigEndianBitConverter.ToUInt32(sectors, 0x04 + (14 * s)), + filesize = BigEndianBitConverter.ToUInt32(sectors, 0x08 + (14 * s)), + flags = BigEndianBitConverter.ToUInt16(sectors, 0x0C + (14 * s)) + }; + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/File.cs b/Aaru.Filesystems/LisaFS/File.cs index 0b2d50e82..9657e1403 100644 --- a/Aaru.Filesystems/LisaFS/File.cs +++ b/Aaru.Filesystems/LisaFS/File.cs @@ -37,522 +37,521 @@ using Aaru.Console; using Aaru.Decoders; using Aaru.Helpers; -namespace Aaru.Filesystems.LisaFS +namespace Aaru.Filesystems.LisaFS; + +public sealed partial class LisaFS { - public sealed partial class LisaFS + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) { - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + attributes = new FileAttributes(); + + ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); + + if(error != ErrorNumber.NoError) + return error; + + if(!isDir) + return GetAttributes(fileId, out attributes); + + attributes = FileAttributes.Directory; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + if(size == 0) { - attributes = new FileAttributes(); - - ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); - - if(error != ErrorNumber.NoError) - return error; - - if(!isDir) - return GetAttributes(fileId, out attributes); - - attributes = FileAttributes.Directory; + buf = Array.Empty(); return ErrorNumber.NoError; } - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) - { - if(size == 0) + if(offset < 0) + return ErrorNumber.InvalidArgument; + + ErrorNumber error = LookupFileId(path, out short fileId, out _); + + if(error != ErrorNumber.NoError) + return error; + + byte[] tmp; + + if(_debug) + switch(fileId) { - buf = Array.Empty(); - - return ErrorNumber.NoError; - } - - if(offset < 0) - return ErrorNumber.InvalidArgument; - - ErrorNumber error = LookupFileId(path, out short fileId, out _); - - if(error != ErrorNumber.NoError) - return error; - - byte[] tmp; - - if(_debug) - switch(fileId) - { - case FILEID_BOOT_SIGNED: - case FILEID_LOADER_SIGNED: - case (short)FILEID_MDDF: - case (short)FILEID_BITMAP: - case (short)FILEID_SRECORD: - case (short)FILEID_CATALOG: - error = ReadSystemFile(fileId, out tmp); - - break; - default: - error = ReadFile(fileId, out tmp); - - break; - } - else - error = ReadFile(fileId, out tmp); - - if(error != ErrorNumber.NoError) - return error; - - if(offset >= tmp.Length) - return ErrorNumber.EINVAL; - - if(size + offset >= tmp.Length) - size = tmp.Length - offset; - - buf = new byte[size]; - Array.Copy(tmp, offset, buf, 0, size); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) - { - stat = null; - ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); - - if(error != ErrorNumber.NoError) - return error; - - return isDir ? StatDir(fileId, out stat) : Stat(fileId, out stat); - } - - ErrorNumber GetAttributes(short fileId, out FileAttributes attributes) - { - attributes = new FileAttributes(); - - if(!_mounted) - return ErrorNumber.AccessDenied; - - if(fileId < 4) - { - if(!_debug) - return ErrorNumber.NoSuchFile; - - attributes = new FileAttributes(); - attributes = FileAttributes.System; - attributes |= FileAttributes.Hidden; - - attributes |= FileAttributes.File; - - return ErrorNumber.NoError; - } - - ErrorNumber error = ReadExtentsFile(fileId, out ExtentFile extFile); - - if(error != ErrorNumber.NoError) - return error; - - switch(extFile.ftype) - { - case FileType.Spool: - attributes |= FileAttributes.CharDevice; + case FILEID_BOOT_SIGNED: + case FILEID_LOADER_SIGNED: + case (short)FILEID_MDDF: + case (short)FILEID_BITMAP: + case (short)FILEID_SRECORD: + case (short)FILEID_CATALOG: + error = ReadSystemFile(fileId, out tmp); break; - case FileType.UserCat: - case FileType.RootCat: - attributes |= FileAttributes.Directory; - - break; - case FileType.Pipe: - attributes |= FileAttributes.Pipe; - - break; - case FileType.Undefined: break; default: - attributes |= FileAttributes.File; - attributes |= FileAttributes.Extents; + error = ReadFile(fileId, out tmp); break; } + else + error = ReadFile(fileId, out tmp); - if(extFile.protect > 0) - attributes |= FileAttributes.Immutable; + if(error != ErrorNumber.NoError) + return error; - if(extFile.locked > 0) - attributes |= FileAttributes.ReadOnly; + if(offset >= tmp.Length) + return ErrorNumber.EINVAL; - if(extFile.password_valid > 0) - attributes |= FileAttributes.Password; + if(size + offset >= tmp.Length) + size = tmp.Length - offset; - return ErrorNumber.NoError; - } + buf = new byte[size]; + Array.Copy(tmp, offset, buf, 0, size); - ErrorNumber ReadSystemFile(short fileId, out byte[] buf) => ReadSystemFile(fileId, out buf, false); + return ErrorNumber.NoError; + } - ErrorNumber ReadSystemFile(short fileId, out byte[] buf, bool tags) + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); + + if(error != ErrorNumber.NoError) + return error; + + return isDir ? StatDir(fileId, out stat) : Stat(fileId, out stat); + } + + ErrorNumber GetAttributes(short fileId, out FileAttributes attributes) + { + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(fileId < 4) { - buf = null; - ErrorNumber errno; - - if(!_mounted || - !_debug) - return ErrorNumber.AccessDenied; - - if(fileId > 4 || - fileId <= 0) - if(fileId != FILEID_BOOT_SIGNED && - fileId != FILEID_LOADER_SIGNED) - return ErrorNumber.InvalidArgument; - - if(_systemFileCache.TryGetValue(fileId, out buf) && - !tags) - return ErrorNumber.NoError; - - int count = 0; - - if(fileId == FILEID_SRECORD) - if(!tags) - { - errno = _device.ReadSectors(_mddf.mddf_block + _volumePrefix + _mddf.srec_ptr, _mddf.srec_len, - out buf); - - if(errno != ErrorNumber.NoError) - return errno; - - _systemFileCache.Add(fileId, buf); - - return ErrorNumber.NoError; - } - else - { - errno = _device.ReadSectorsTag(_mddf.mddf_block + _volumePrefix + _mddf.srec_ptr, _mddf.srec_len, - SectorTagType.AppleSectorTag, out buf); - - return errno != ErrorNumber.NoError ? errno : ErrorNumber.NoError; - } - - LisaTag.PriamTag sysTag; - - // Should be enough to check 100 sectors? - for(ulong i = 0; i < 100; i++) - { - errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out byte[] tag); - - if(errno != ErrorNumber.NoError) - continue; - - DecodeTag(tag, out sysTag); - - if(sysTag.FileId == fileId) - count++; - } - - if(count == 0) + if(!_debug) return ErrorNumber.NoSuchFile; - buf = !tags ? new byte[count * _device.Info.SectorSize] : new byte[count * _devTagSize]; + attributes = new FileAttributes(); + attributes = FileAttributes.System; + attributes |= FileAttributes.Hidden; - // Should be enough to check 100 sectors? - for(ulong i = 0; i < 100; i++) - { - errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out byte[] tag); - - if(errno != ErrorNumber.NoError) - continue; - - DecodeTag(tag, out sysTag); - - if(sysTag.FileId != fileId) - continue; - - byte[] sector; - - errno = !tags ? _device.ReadSector(i, out sector) - : _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out sector); - - if(errno != ErrorNumber.NoError) - continue; - - // Relative block for $Loader starts at $Boot block - if(sysTag.FileId == FILEID_LOADER_SIGNED) - sysTag.RelPage--; - - Array.Copy(sector, 0, buf, sector.Length * sysTag.RelPage, sector.Length); - } - - if(!tags) - _systemFileCache.Add(fileId, buf); + attributes |= FileAttributes.File; return ErrorNumber.NoError; } - ErrorNumber Stat(short fileId, out FileEntryInfo stat) + ErrorNumber error = ReadExtentsFile(fileId, out ExtentFile extFile); + + if(error != ErrorNumber.NoError) + return error; + + switch(extFile.ftype) { - stat = null; + case FileType.Spool: + attributes |= FileAttributes.CharDevice; - if(!_mounted) - return ErrorNumber.AccessDenied; + break; + case FileType.UserCat: + case FileType.RootCat: + attributes |= FileAttributes.Directory; - ErrorNumber error; - ExtentFile file; + break; + case FileType.Pipe: + attributes |= FileAttributes.Pipe; - if(fileId <= 4) - if(!_debug || - fileId == 0) - return ErrorNumber.NoSuchFile; - else - { - stat = new FileEntryInfo(); + break; + case FileType.Undefined: break; + default: + attributes |= FileAttributes.File; + attributes |= FileAttributes.Extents; - error = GetAttributes(fileId, out FileAttributes attrs); - - stat.Attributes = attrs; - - if(error != ErrorNumber.NoError) - return error; - - if(fileId < 0 && - fileId != FILEID_BOOT_SIGNED && - fileId != FILEID_LOADER_SIGNED) - { - error = ReadExtentsFile((short)(fileId * -1), out file); - - if(error != ErrorNumber.NoError) - return error; - - stat.CreationTime = DateHandlers.LisaToDateTime(file.dtc); - stat.AccessTime = DateHandlers.LisaToDateTime(file.dta); - stat.BackupTime = DateHandlers.LisaToDateTime(file.dtb); - stat.LastWriteTime = DateHandlers.LisaToDateTime(file.dtm); - - stat.Inode = (ulong)fileId; - stat.Links = 0; - stat.Length = _mddf.datasize; - stat.BlockSize = _mddf.datasize; - stat.Blocks = 1; - } - else - { - error = ReadSystemFile(fileId, out byte[] buf); - - if(error != ErrorNumber.NoError) - return error; - - stat.CreationTime = fileId != 4 ? _mddf.dtvc : _mddf.dtcc; - - stat.BackupTime = _mddf.dtvb; - - stat.Inode = (ulong)fileId; - stat.Links = 0; - stat.Length = buf.Length; - stat.BlockSize = _mddf.datasize; - stat.Blocks = buf.Length / _mddf.datasize; - } - - return ErrorNumber.NoError; - } - - stat = new FileEntryInfo(); - - error = GetAttributes(fileId, out FileAttributes fileAttributes); - stat.Attributes = fileAttributes; - - if(error != ErrorNumber.NoError) - return error; - - error = ReadExtentsFile(fileId, out file); - - if(error != ErrorNumber.NoError) - return error; - - stat.CreationTime = DateHandlers.LisaToDateTime(file.dtc); - stat.AccessTime = DateHandlers.LisaToDateTime(file.dta); - stat.BackupTime = DateHandlers.LisaToDateTime(file.dtb); - stat.LastWriteTime = DateHandlers.LisaToDateTime(file.dtm); - - stat.Inode = (ulong)fileId; - stat.Links = 1; - - if(!_fileSizeCache.TryGetValue(fileId, out int len)) - stat.Length = _srecords[fileId].filesize; - else - stat.Length = len; - - stat.BlockSize = _mddf.datasize; - stat.Blocks = file.length; - - return ErrorNumber.NoError; + break; } - ErrorNumber ReadFile(short fileId, out byte[] buf) => ReadFile(fileId, out buf, false); + if(extFile.protect > 0) + attributes |= FileAttributes.Immutable; - ErrorNumber ReadFile(short fileId, out byte[] buf, bool tags) - { - buf = null; - ErrorNumber errno; + if(extFile.locked > 0) + attributes |= FileAttributes.ReadOnly; - if(!_mounted) - return ErrorNumber.AccessDenied; + if(extFile.password_valid > 0) + attributes |= FileAttributes.Password; - tags &= _debug; + return ErrorNumber.NoError; + } - if(fileId < 4 || - (fileId == 4 && _mddf.fsversion != LISA_V2 && _mddf.fsversion != LISA_V1)) + ErrorNumber ReadSystemFile(short fileId, out byte[] buf) => ReadSystemFile(fileId, out buf, false); + + ErrorNumber ReadSystemFile(short fileId, out byte[] buf, bool tags) + { + buf = null; + ErrorNumber errno; + + if(!_mounted || + !_debug) + return ErrorNumber.AccessDenied; + + if(fileId > 4 || + fileId <= 0) + if(fileId != FILEID_BOOT_SIGNED && + fileId != FILEID_LOADER_SIGNED) return ErrorNumber.InvalidArgument; - if(!tags && - _fileCache.TryGetValue(fileId, out buf)) - return ErrorNumber.NoError; + if(_systemFileCache.TryGetValue(fileId, out buf) && + !tags) + return ErrorNumber.NoError; - ErrorNumber error = ReadExtentsFile(fileId, out ExtentFile file); + int count = 0; - if(error != ErrorNumber.NoError) - return error; - - int sectorSize; - - if(tags) - sectorSize = _devTagSize; - else - sectorSize = (int)_device.Info.SectorSize; - - byte[] temp = new byte[file.length * sectorSize]; - - int offset = 0; - - for(int i = 0; i < file.extents.Length; i++) + if(fileId == FILEID_SRECORD) + if(!tags) { - byte[] sector; - - errno = !tags ? _device.ReadSectors((ulong)file.extents[i].start + _mddf.mddf_block + _volumePrefix, - (uint)file.extents[i].length, out sector) - : _device.ReadSectorsTag((ulong)file.extents[i].start + _mddf.mddf_block + _volumePrefix, - (uint)file.extents[i].length, SectorTagType.AppleSectorTag, - out sector); + errno = _device.ReadSectors(_mddf.mddf_block + _volumePrefix + _mddf.srec_ptr, _mddf.srec_len, + out buf); if(errno != ErrorNumber.NoError) return errno; - Array.Copy(sector, 0, temp, offset, sector.Length); - offset += sector.Length; - } + _systemFileCache.Add(fileId, buf); - if(!tags) - { - if(_fileSizeCache.TryGetValue(fileId, out int realSize)) - if(realSize > temp.Length) - AaruConsole.ErrorWriteLine("File {0} gets truncated.", fileId); - - buf = temp; - - _fileCache.Add(fileId, buf); + return ErrorNumber.NoError; } else - buf = temp; + { + errno = _device.ReadSectorsTag(_mddf.mddf_block + _volumePrefix + _mddf.srec_ptr, _mddf.srec_len, + SectorTagType.AppleSectorTag, out buf); + + return errno != ErrorNumber.NoError ? errno : ErrorNumber.NoError; + } + + LisaTag.PriamTag sysTag; + + // Should be enough to check 100 sectors? + for(ulong i = 0; i < 100; i++) + { + errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out byte[] tag); + + if(errno != ErrorNumber.NoError) + continue; + + DecodeTag(tag, out sysTag); + + if(sysTag.FileId == fileId) + count++; + } + + if(count == 0) + return ErrorNumber.NoSuchFile; + + buf = !tags ? new byte[count * _device.Info.SectorSize] : new byte[count * _devTagSize]; + + // Should be enough to check 100 sectors? + for(ulong i = 0; i < 100; i++) + { + errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out byte[] tag); + + if(errno != ErrorNumber.NoError) + continue; + + DecodeTag(tag, out sysTag); + + if(sysTag.FileId != fileId) + continue; + + byte[] sector; + + errno = !tags ? _device.ReadSector(i, out sector) + : _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out sector); + + if(errno != ErrorNumber.NoError) + continue; + + // Relative block for $Loader starts at $Boot block + if(sysTag.FileId == FILEID_LOADER_SIGNED) + sysTag.RelPage--; + + Array.Copy(sector, 0, buf, sector.Length * sysTag.RelPage, sector.Length); + } + + if(!tags) + _systemFileCache.Add(fileId, buf); + + return ErrorNumber.NoError; + } + + ErrorNumber Stat(short fileId, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber error; + ExtentFile file; + + if(fileId <= 4) + if(!_debug || + fileId == 0) + return ErrorNumber.NoSuchFile; + else + { + stat = new FileEntryInfo(); + + error = GetAttributes(fileId, out FileAttributes attrs); + + stat.Attributes = attrs; + + if(error != ErrorNumber.NoError) + return error; + + if(fileId < 0 && + fileId != FILEID_BOOT_SIGNED && + fileId != FILEID_LOADER_SIGNED) + { + error = ReadExtentsFile((short)(fileId * -1), out file); + + if(error != ErrorNumber.NoError) + return error; + + stat.CreationTime = DateHandlers.LisaToDateTime(file.dtc); + stat.AccessTime = DateHandlers.LisaToDateTime(file.dta); + stat.BackupTime = DateHandlers.LisaToDateTime(file.dtb); + stat.LastWriteTime = DateHandlers.LisaToDateTime(file.dtm); + + stat.Inode = (ulong)fileId; + stat.Links = 0; + stat.Length = _mddf.datasize; + stat.BlockSize = _mddf.datasize; + stat.Blocks = 1; + } + else + { + error = ReadSystemFile(fileId, out byte[] buf); + + if(error != ErrorNumber.NoError) + return error; + + stat.CreationTime = fileId != 4 ? _mddf.dtvc : _mddf.dtcc; + + stat.BackupTime = _mddf.dtvb; + + stat.Inode = (ulong)fileId; + stat.Links = 0; + stat.Length = buf.Length; + stat.BlockSize = _mddf.datasize; + stat.Blocks = buf.Length / _mddf.datasize; + } + + return ErrorNumber.NoError; + } + + stat = new FileEntryInfo(); + + error = GetAttributes(fileId, out FileAttributes fileAttributes); + stat.Attributes = fileAttributes; + + if(error != ErrorNumber.NoError) + return error; + + error = ReadExtentsFile(fileId, out file); + + if(error != ErrorNumber.NoError) + return error; + + stat.CreationTime = DateHandlers.LisaToDateTime(file.dtc); + stat.AccessTime = DateHandlers.LisaToDateTime(file.dta); + stat.BackupTime = DateHandlers.LisaToDateTime(file.dtb); + stat.LastWriteTime = DateHandlers.LisaToDateTime(file.dtm); + + stat.Inode = (ulong)fileId; + stat.Links = 1; + + if(!_fileSizeCache.TryGetValue(fileId, out int len)) + stat.Length = _srecords[fileId].filesize; + else + stat.Length = len; + + stat.BlockSize = _mddf.datasize; + stat.Blocks = file.length; + + return ErrorNumber.NoError; + } + + ErrorNumber ReadFile(short fileId, out byte[] buf) => ReadFile(fileId, out buf, false); + + ErrorNumber ReadFile(short fileId, out byte[] buf, bool tags) + { + buf = null; + ErrorNumber errno; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + tags &= _debug; + + if(fileId < 4 || + (fileId == 4 && _mddf.fsversion != LISA_V2 && _mddf.fsversion != LISA_V1)) + return ErrorNumber.InvalidArgument; + + if(!tags && + _fileCache.TryGetValue(fileId, out buf)) + return ErrorNumber.NoError; + + ErrorNumber error = ReadExtentsFile(fileId, out ExtentFile file); + + if(error != ErrorNumber.NoError) + return error; + + int sectorSize; + + if(tags) + sectorSize = _devTagSize; + else + sectorSize = (int)_device.Info.SectorSize; + + byte[] temp = new byte[file.length * sectorSize]; + + int offset = 0; + + for(int i = 0; i < file.extents.Length; i++) + { + byte[] sector; + + errno = !tags ? _device.ReadSectors((ulong)file.extents[i].start + _mddf.mddf_block + _volumePrefix, + (uint)file.extents[i].length, out sector) + : _device.ReadSectorsTag((ulong)file.extents[i].start + _mddf.mddf_block + _volumePrefix, + (uint)file.extents[i].length, SectorTagType.AppleSectorTag, + out sector); + + if(errno != ErrorNumber.NoError) + return errno; + + Array.Copy(sector, 0, temp, offset, sector.Length); + offset += sector.Length; + } + + if(!tags) + { + if(_fileSizeCache.TryGetValue(fileId, out int realSize)) + if(realSize > temp.Length) + AaruConsole.ErrorWriteLine("File {0} gets truncated.", fileId); + + buf = temp; + + _fileCache.Add(fileId, buf); + } + else + buf = temp; + + return ErrorNumber.NoError; + } + + ErrorNumber LookupFileId(string path, out short fileId, out bool isDir) + { + fileId = 0; + isDir = false; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + if(pathElements.Length == 0) + { + fileId = DIRID_ROOT; + isDir = true; return ErrorNumber.NoError; } - ErrorNumber LookupFileId(string path, out short fileId, out bool isDir) + // Only V3 supports subdirectories + if(pathElements.Length > 1 && + _mddf.fsversion != LISA_V3) + return ErrorNumber.NotSupported; + + if(_debug && pathElements.Length == 1) { - fileId = 0; - isDir = false; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - string[] pathElements = path.Split(new[] + if(string.Compare(pathElements[0], "$MDDF", StringComparison.InvariantCulture) == 0) { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + fileId = (short)FILEID_MDDF; - if(pathElements.Length == 0) + return ErrorNumber.NoError; + } + + if(string.Compare(pathElements[0], "$Boot", StringComparison.InvariantCulture) == 0) + { + fileId = FILEID_BOOT_SIGNED; + + return ErrorNumber.NoError; + } + + if(string.Compare(pathElements[0], "$Loader", StringComparison.InvariantCulture) == 0) + { + fileId = FILEID_LOADER_SIGNED; + + return ErrorNumber.NoError; + } + + if(string.Compare(pathElements[0], "$Bitmap", StringComparison.InvariantCulture) == 0) + { + fileId = (short)FILEID_BITMAP; + + return ErrorNumber.NoError; + } + + if(string.Compare(pathElements[0], "$S-Record", StringComparison.InvariantCulture) == 0) + { + fileId = (short)FILEID_SRECORD; + + return ErrorNumber.NoError; + } + + if(string.Compare(pathElements[0], "$", StringComparison.InvariantCulture) == 0) { fileId = DIRID_ROOT; isDir = true; return ErrorNumber.NoError; } - - // Only V3 supports subdirectories - if(pathElements.Length > 1 && - _mddf.fsversion != LISA_V3) - return ErrorNumber.NotSupported; - - if(_debug && pathElements.Length == 1) - { - if(string.Compare(pathElements[0], "$MDDF", StringComparison.InvariantCulture) == 0) - { - fileId = (short)FILEID_MDDF; - - return ErrorNumber.NoError; - } - - if(string.Compare(pathElements[0], "$Boot", StringComparison.InvariantCulture) == 0) - { - fileId = FILEID_BOOT_SIGNED; - - return ErrorNumber.NoError; - } - - if(string.Compare(pathElements[0], "$Loader", StringComparison.InvariantCulture) == 0) - { - fileId = FILEID_LOADER_SIGNED; - - return ErrorNumber.NoError; - } - - if(string.Compare(pathElements[0], "$Bitmap", StringComparison.InvariantCulture) == 0) - { - fileId = (short)FILEID_BITMAP; - - return ErrorNumber.NoError; - } - - if(string.Compare(pathElements[0], "$S-Record", StringComparison.InvariantCulture) == 0) - { - fileId = (short)FILEID_SRECORD; - - return ErrorNumber.NoError; - } - - if(string.Compare(pathElements[0], "$", StringComparison.InvariantCulture) == 0) - { - fileId = DIRID_ROOT; - isDir = true; - - return ErrorNumber.NoError; - } - } - - for(int lvl = 0; lvl < pathElements.Length; lvl++) - { - string wantedFilename = pathElements[0].Replace('-', '/'); - - foreach(CatalogEntry entry in _catalogCache) - { - string filename = StringHandlers.CToString(entry.filename, Encoding); - - // LisaOS is case insensitive - if(string.Compare(wantedFilename, filename, StringComparison.InvariantCultureIgnoreCase) != 0 || - entry.parentID != fileId) - continue; - - fileId = entry.fileID; - isDir = entry.fileType == 0x01; - - // Not last path element, and it's not a directory - if(lvl != pathElements.Length - 1 && - !isDir) - return ErrorNumber.NotDirectory; - - // Arrived last path element - if(lvl == pathElements.Length - 1) - return ErrorNumber.NoError; - } - } - - return ErrorNumber.NoSuchFile; } + + for(int lvl = 0; lvl < pathElements.Length; lvl++) + { + string wantedFilename = pathElements[0].Replace('-', '/'); + + foreach(CatalogEntry entry in _catalogCache) + { + string filename = StringHandlers.CToString(entry.filename, Encoding); + + // LisaOS is case insensitive + if(string.Compare(wantedFilename, filename, StringComparison.InvariantCultureIgnoreCase) != 0 || + entry.parentID != fileId) + continue; + + fileId = entry.fileID; + isDir = entry.fileType == 0x01; + + // Not last path element, and it's not a directory + if(lvl != pathElements.Length - 1 && + !isDir) + return ErrorNumber.NotDirectory; + + // Arrived last path element + if(lvl == pathElements.Length - 1) + return ErrorNumber.NoError; + } + } + + return ErrorNumber.NoSuchFile; } } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/Info.cs b/Aaru.Filesystems/LisaFS/Info.cs index f4215b8a0..b91def688 100644 --- a/Aaru.Filesystems/LisaFS/Info.cs +++ b/Aaru.Filesystems/LisaFS/Info.cs @@ -42,378 +42,377 @@ using Claunia.Encoding; using Schemas; using Encoding = System.Text.Encoding; -namespace Aaru.Filesystems.LisaFS +namespace Aaru.Filesystems.LisaFS; + +public sealed partial class LisaFS { - public sealed partial class LisaFS + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - ErrorNumber errno; - - if(imagePlugin.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true) - return false; - - // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors - if(imagePlugin.Info.Sectors < 800) - return false; - - int beforeMddf = -1; - - // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors - for(int i = 0; i < 100; i++) - { - errno = imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag, out byte[] tag); - - if(errno != ErrorNumber.NoError) - continue; - - DecodeTag(tag, out LisaTag.PriamTag searchTag); - - AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); - - if(beforeMddf == -1 && - searchTag.FileId == FILEID_LOADER_SIGNED) - beforeMddf = i - 1; - - if(searchTag.FileId != FILEID_MDDF) - continue; - - errno = imagePlugin.ReadSector((ulong)i, out byte[] sector); - - if(errno != ErrorNumber.NoError) - continue; - - var infoMddf = new MDDF - { - mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C), - volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70), - volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74), - vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78), - blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C), - datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E) - }; - - AaruConsole.DebugWriteLine("LisaFS plugin", "Current sector = {0}", i); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.mddf_block = {0}", infoMddf.mddf_block); - AaruConsole.DebugWriteLine("LisaFS plugin", "Disk size = {0} sectors", imagePlugin.Info.Sectors); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size = {0} sectors", infoMddf.vol_size); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size - 1 = {0}", infoMddf.volsize_minus_one); - - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size - mddf.mddf_block -1 = {0}", - infoMddf.volsize_minus_mddf_minus_one); - - AaruConsole.DebugWriteLine("LisaFS plugin", "Disk sector = {0} bytes", imagePlugin.Info.SectorSize); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.blocksize = {0} bytes", infoMddf.blocksize); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.datasize = {0} bytes", infoMddf.datasize); - - if(infoMddf.mddf_block != i - beforeMddf) - return false; - - if(infoMddf.vol_size > imagePlugin.Info.Sectors) - return false; - - if(infoMddf.vol_size - 1 != infoMddf.volsize_minus_one) - return false; - - if(infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf) - return false; - - if(infoMddf.datasize > infoMddf.blocksize) - return false; - - if(infoMddf.blocksize < imagePlugin.Info.SectorSize) - return false; - - return infoMddf.datasize == imagePlugin.Info.SectorSize; - } + ErrorNumber errno; + if(imagePlugin.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true) return false; + + // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors + if(imagePlugin.Info.Sectors < 800) + return false; + + int beforeMddf = -1; + + // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors + for(int i = 0; i < 100; i++) + { + errno = imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag, out byte[] tag); + + if(errno != ErrorNumber.NoError) + continue; + + DecodeTag(tag, out LisaTag.PriamTag searchTag); + + AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); + + if(beforeMddf == -1 && + searchTag.FileId == FILEID_LOADER_SIGNED) + beforeMddf = i - 1; + + if(searchTag.FileId != FILEID_MDDF) + continue; + + errno = imagePlugin.ReadSector((ulong)i, out byte[] sector); + + if(errno != ErrorNumber.NoError) + continue; + + var infoMddf = new MDDF + { + mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C), + volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70), + volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74), + vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78), + blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C), + datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E) + }; + + AaruConsole.DebugWriteLine("LisaFS plugin", "Current sector = {0}", i); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.mddf_block = {0}", infoMddf.mddf_block); + AaruConsole.DebugWriteLine("LisaFS plugin", "Disk size = {0} sectors", imagePlugin.Info.Sectors); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size = {0} sectors", infoMddf.vol_size); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size - 1 = {0}", infoMddf.volsize_minus_one); + + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.vol_size - mddf.mddf_block -1 = {0}", + infoMddf.volsize_minus_mddf_minus_one); + + AaruConsole.DebugWriteLine("LisaFS plugin", "Disk sector = {0} bytes", imagePlugin.Info.SectorSize); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.blocksize = {0} bytes", infoMddf.blocksize); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.datasize = {0} bytes", infoMddf.datasize); + + if(infoMddf.mddf_block != i - beforeMddf) + return false; + + if(infoMddf.vol_size > imagePlugin.Info.Sectors) + return false; + + if(infoMddf.vol_size - 1 != infoMddf.volsize_minus_one) + return false; + + if(infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf) + return false; + + if(infoMddf.datasize > infoMddf.blocksize) + return false; + + if(infoMddf.blocksize < imagePlugin.Info.SectorSize) + return false; + + return infoMddf.datasize == imagePlugin.Info.SectorSize; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + return false; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = new LisaRoman(); + information = ""; + var sb = new StringBuilder(); + ErrorNumber errno; + + if(imagePlugin.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true) + return; + + // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors + if(imagePlugin.Info.Sectors < 800) + return; + + int beforeMddf = -1; + + // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors + for(int i = 0; i < 100; i++) { - Encoding = new LisaRoman(); - information = ""; - var sb = new StringBuilder(); - ErrorNumber errno; + errno = imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag, out byte[] tag); - if(imagePlugin.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true) + if(errno != ErrorNumber.NoError) + continue; + + DecodeTag(tag, out LisaTag.PriamTag searchTag); + + AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); + + if(beforeMddf == -1 && + searchTag.FileId == FILEID_LOADER_SIGNED) + beforeMddf = i - 1; + + if(searchTag.FileId != FILEID_MDDF) + continue; + + errno = imagePlugin.ReadSector((ulong)i, out byte[] sector); + + if(errno != ErrorNumber.NoError) + continue; + + var infoMddf = new MDDF(); + byte[] pString = new byte[33]; + + infoMddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); + infoMddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); + infoMddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); + Array.Copy(sector, 0x0C, pString, 0, 33); + infoMddf.volname = StringHandlers.PascalToString(pString, Encoding); + infoMddf.unknown1 = sector[0x2D]; + Array.Copy(sector, 0x2E, pString, 0, 33); + + // Prevent garbage + infoMddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; + infoMddf.unknown2 = sector[0x4F]; + infoMddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); + infoMddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); + uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); + infoMddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); + lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); + infoMddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); + lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); + infoMddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); + lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); + infoMddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); + infoMddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); + infoMddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); + infoMddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); + infoMddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); + infoMddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); + infoMddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); + infoMddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); + infoMddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); + infoMddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); + infoMddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); + infoMddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); + infoMddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); + infoMddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); + infoMddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); + infoMddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); + infoMddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); + infoMddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); + infoMddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); + infoMddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); + infoMddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); + infoMddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); + infoMddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); + infoMddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); + infoMddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); + infoMddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); + infoMddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); + infoMddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); + infoMddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); + infoMddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); + infoMddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); + infoMddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); + infoMddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); + infoMddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); + infoMddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); + infoMddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); + infoMddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); + infoMddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); + infoMddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); + infoMddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); + infoMddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); + infoMddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); + infoMddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); + infoMddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); + infoMddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); + infoMddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); + infoMddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); + infoMddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); + infoMddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); + infoMddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); + infoMddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); + infoMddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); + infoMddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); + infoMddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); + infoMddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); + infoMddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); + infoMddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); + infoMddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); + infoMddf.vol_left_mounted = sector[0x138]; + + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown1 = 0x{0:X2} ({0})", infoMddf.unknown1); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown2 = 0x{0:X2} ({0})", infoMddf.unknown2); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown3 = 0x{0:X8} ({0})", infoMddf.unknown3); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown4 = 0x{0:X4} ({0})", infoMddf.unknown4); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown5 = 0x{0:X8} ({0})", infoMddf.unknown5); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown6 = 0x{0:X8} ({0})", infoMddf.unknown6); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown7 = 0x{0:X8} ({0})", infoMddf.unknown7); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown9 = 0x{0:X4} ({0})", infoMddf.unknown9); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown10 = 0x{0:X8} ({0})", infoMddf.unknown10); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown11 = 0x{0:X8} ({0})", infoMddf.unknown11); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown12 = 0x{0:X8} ({0})", infoMddf.unknown12); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown13 = 0x{0:X8} ({0})", infoMddf.unknown13); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown14 = 0x{0:X8} ({0})", infoMddf.unknown14); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown15 = 0x{0:X8} ({0})", infoMddf.unknown15); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown16 = 0x{0:X8} ({0})", infoMddf.unknown16); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown17 = 0x{0:X4} ({0})", infoMddf.unknown17); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown18 = 0x{0:X8} ({0})", infoMddf.unknown18); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown19 = 0x{0:X8} ({0})", infoMddf.unknown19); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown20 = 0x{0:X8} ({0})", infoMddf.unknown20); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown21 = 0x{0:X8} ({0})", infoMddf.unknown21); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown22 = 0x{0:X8} ({0})", infoMddf.unknown22); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown23 = 0x{0:X8} ({0})", infoMddf.unknown23); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown24 = 0x{0:X8} ({0})", infoMddf.unknown24); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown25 = 0x{0:X8} ({0})", infoMddf.unknown25); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown26 = 0x{0:X8} ({0})", infoMddf.unknown26); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown27 = 0x{0:X8} ({0})", infoMddf.unknown27); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown28 = 0x{0:X8} ({0})", infoMddf.unknown28); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown29 = 0x{0:X8} ({0})", infoMddf.unknown29); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown30 = 0x{0:X8} ({0})", infoMddf.unknown30); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown31 = 0x{0:X8} ({0})", infoMddf.unknown31); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown32 = 0x{0:X8} ({0})", infoMddf.unknown32); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown33 = 0x{0:X8} ({0})", infoMddf.unknown33); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown34 = 0x{0:X8} ({0})", infoMddf.unknown34); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown35 = 0x{0:X8} ({0})", infoMddf.unknown35); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown36 = 0x{0:X8} ({0})", infoMddf.unknown36); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown37 = 0x{0:X8} ({0})", infoMddf.unknown37); + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown38 = 0x{0:X8} ({0})", infoMddf.unknown38); + + AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown_timestamp = 0x{0:X8} ({0}, {1})", + infoMddf.unknown_timestamp, + DateHandlers.LisaToDateTime(infoMddf.unknown_timestamp)); + + if(infoMddf.mddf_block != i - beforeMddf) return; - // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors - if(imagePlugin.Info.Sectors < 800) + if(infoMddf.vol_size > imagePlugin.Info.Sectors) return; - int beforeMddf = -1; + if(infoMddf.vol_size - 1 != infoMddf.volsize_minus_one) + return; - // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors - for(int i = 0; i < 100; i++) + if(infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf) + return; + + if(infoMddf.datasize > infoMddf.blocksize) + return; + + if(infoMddf.blocksize < imagePlugin.Info.SectorSize) + return; + + if(infoMddf.datasize != imagePlugin.Info.SectorSize) + return; + + switch(infoMddf.fsversion) { - errno = imagePlugin.ReadSectorTag((ulong)i, SectorTagType.AppleSectorTag, out byte[] tag); + case LISA_V1: + sb.AppendLine("LisaFS v1"); - if(errno != ErrorNumber.NoError) - continue; + break; + case LISA_V2: + sb.AppendLine("LisaFS v2"); - DecodeTag(tag, out LisaTag.PriamTag searchTag); + break; + case LISA_V3: + sb.AppendLine("LisaFS v3"); - AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); + break; + default: + sb.AppendFormat("Unknown LisaFS version {0}", infoMddf.fsversion).AppendLine(); - if(beforeMddf == -1 && - searchTag.FileId == FILEID_LOADER_SIGNED) - beforeMddf = i - 1; - - if(searchTag.FileId != FILEID_MDDF) - continue; - - errno = imagePlugin.ReadSector((ulong)i, out byte[] sector); - - if(errno != ErrorNumber.NoError) - continue; - - var infoMddf = new MDDF(); - byte[] pString = new byte[33]; - - infoMddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); - infoMddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); - infoMddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); - Array.Copy(sector, 0x0C, pString, 0, 33); - infoMddf.volname = StringHandlers.PascalToString(pString, Encoding); - infoMddf.unknown1 = sector[0x2D]; - Array.Copy(sector, 0x2E, pString, 0, 33); - - // Prevent garbage - infoMddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; - infoMddf.unknown2 = sector[0x4F]; - infoMddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); - infoMddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); - uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); - infoMddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); - lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); - infoMddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); - lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); - infoMddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); - lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); - infoMddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); - infoMddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); - infoMddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); - infoMddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); - infoMddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); - infoMddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); - infoMddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); - infoMddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); - infoMddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); - infoMddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); - infoMddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); - infoMddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); - infoMddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); - infoMddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); - infoMddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); - infoMddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); - infoMddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); - infoMddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); - infoMddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); - infoMddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); - infoMddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); - infoMddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); - infoMddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); - infoMddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); - infoMddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); - infoMddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); - infoMddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); - infoMddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); - infoMddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); - infoMddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); - infoMddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); - infoMddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); - infoMddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); - infoMddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); - infoMddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); - infoMddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); - infoMddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); - infoMddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); - infoMddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); - infoMddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); - infoMddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); - infoMddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); - infoMddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); - infoMddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); - infoMddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); - infoMddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); - infoMddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); - infoMddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); - infoMddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); - infoMddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); - infoMddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); - infoMddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); - infoMddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); - infoMddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); - infoMddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); - infoMddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); - infoMddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); - infoMddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); - infoMddf.vol_left_mounted = sector[0x138]; - - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown1 = 0x{0:X2} ({0})", infoMddf.unknown1); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown2 = 0x{0:X2} ({0})", infoMddf.unknown2); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown3 = 0x{0:X8} ({0})", infoMddf.unknown3); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown4 = 0x{0:X4} ({0})", infoMddf.unknown4); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown5 = 0x{0:X8} ({0})", infoMddf.unknown5); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown6 = 0x{0:X8} ({0})", infoMddf.unknown6); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown7 = 0x{0:X8} ({0})", infoMddf.unknown7); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown9 = 0x{0:X4} ({0})", infoMddf.unknown9); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown10 = 0x{0:X8} ({0})", infoMddf.unknown10); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown11 = 0x{0:X8} ({0})", infoMddf.unknown11); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown12 = 0x{0:X8} ({0})", infoMddf.unknown12); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown13 = 0x{0:X8} ({0})", infoMddf.unknown13); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown14 = 0x{0:X8} ({0})", infoMddf.unknown14); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown15 = 0x{0:X8} ({0})", infoMddf.unknown15); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown16 = 0x{0:X8} ({0})", infoMddf.unknown16); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown17 = 0x{0:X4} ({0})", infoMddf.unknown17); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown18 = 0x{0:X8} ({0})", infoMddf.unknown18); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown19 = 0x{0:X8} ({0})", infoMddf.unknown19); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown20 = 0x{0:X8} ({0})", infoMddf.unknown20); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown21 = 0x{0:X8} ({0})", infoMddf.unknown21); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown22 = 0x{0:X8} ({0})", infoMddf.unknown22); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown23 = 0x{0:X8} ({0})", infoMddf.unknown23); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown24 = 0x{0:X8} ({0})", infoMddf.unknown24); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown25 = 0x{0:X8} ({0})", infoMddf.unknown25); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown26 = 0x{0:X8} ({0})", infoMddf.unknown26); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown27 = 0x{0:X8} ({0})", infoMddf.unknown27); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown28 = 0x{0:X8} ({0})", infoMddf.unknown28); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown29 = 0x{0:X8} ({0})", infoMddf.unknown29); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown30 = 0x{0:X8} ({0})", infoMddf.unknown30); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown31 = 0x{0:X8} ({0})", infoMddf.unknown31); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown32 = 0x{0:X8} ({0})", infoMddf.unknown32); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown33 = 0x{0:X8} ({0})", infoMddf.unknown33); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown34 = 0x{0:X8} ({0})", infoMddf.unknown34); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown35 = 0x{0:X8} ({0})", infoMddf.unknown35); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown36 = 0x{0:X8} ({0})", infoMddf.unknown36); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown37 = 0x{0:X8} ({0})", infoMddf.unknown37); - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown38 = 0x{0:X8} ({0})", infoMddf.unknown38); - - AaruConsole.DebugWriteLine("LisaFS plugin", "mddf.unknown_timestamp = 0x{0:X8} ({0}, {1})", - infoMddf.unknown_timestamp, - DateHandlers.LisaToDateTime(infoMddf.unknown_timestamp)); - - if(infoMddf.mddf_block != i - beforeMddf) - return; - - if(infoMddf.vol_size > imagePlugin.Info.Sectors) - return; - - if(infoMddf.vol_size - 1 != infoMddf.volsize_minus_one) - return; - - if(infoMddf.vol_size - i - 1 != infoMddf.volsize_minus_mddf_minus_one - beforeMddf) - return; - - if(infoMddf.datasize > infoMddf.blocksize) - return; - - if(infoMddf.blocksize < imagePlugin.Info.SectorSize) - return; - - if(infoMddf.datasize != imagePlugin.Info.SectorSize) - return; - - switch(infoMddf.fsversion) - { - case LISA_V1: - sb.AppendLine("LisaFS v1"); - - break; - case LISA_V2: - sb.AppendLine("LisaFS v2"); - - break; - case LISA_V3: - sb.AppendLine("LisaFS v3"); - - break; - default: - sb.AppendFormat("Unknown LisaFS version {0}", infoMddf.fsversion).AppendLine(); - - break; - } - - sb.AppendFormat("Volume name: \"{0}\"", infoMddf.volname).AppendLine(); - sb.AppendFormat("Volume password: \"{0}\"", infoMddf.password).AppendLine(); - sb.AppendFormat("Volume ID: 0x{0:X16}", infoMddf.volid).AppendLine(); - sb.AppendFormat("Backup volume ID: 0x{0:X16}", infoMddf.backup_volid).AppendLine(); - - sb.AppendFormat("Master copy ID: 0x{0:X8}", infoMddf.master_copy_id).AppendLine(); - - sb.AppendFormat("Volume is number {0} of {1}", infoMddf.volnum, infoMddf.vol_sequence).AppendLine(); - - sb.AppendFormat("Serial number of Lisa computer that created this volume: {0}", infoMddf.machine_id). - AppendLine(); - - sb.AppendFormat("Serial number of Lisa computer that can use this volume's software {0}", - infoMddf.serialization).AppendLine(); - - sb.AppendFormat("Volume created on {0}", infoMddf.dtvc).AppendLine(); - sb.AppendFormat("Some timestamp, says {0}", infoMddf.dtcc).AppendLine(); - sb.AppendFormat("Volume backed up on {0}", infoMddf.dtvb).AppendLine(); - sb.AppendFormat("Volume scavenged on {0}", infoMddf.dtvs).AppendLine(); - sb.AppendFormat("MDDF is in block {0}", infoMddf.mddf_block + beforeMddf).AppendLine(); - sb.AppendFormat("There are {0} reserved blocks before volume", beforeMddf).AppendLine(); - sb.AppendFormat("{0} blocks minus one", infoMddf.volsize_minus_one).AppendLine(); - - sb.AppendFormat("{0} blocks minus one minus MDDF offset", infoMddf.volsize_minus_mddf_minus_one). - AppendLine(); - - sb.AppendFormat("{0} blocks in volume", infoMddf.vol_size).AppendLine(); - sb.AppendFormat("{0} bytes per sector (uncooked)", infoMddf.blocksize).AppendLine(); - sb.AppendFormat("{0} bytes per sector", infoMddf.datasize).AppendLine(); - sb.AppendFormat("{0} blocks per cluster", infoMddf.clustersize).AppendLine(); - sb.AppendFormat("{0} blocks in filesystem", infoMddf.fs_size).AppendLine(); - sb.AppendFormat("{0} files in volume", infoMddf.filecount).AppendLine(); - sb.AppendFormat("{0} blocks free", infoMddf.freecount).AppendLine(); - sb.AppendFormat("{0} bytes in LisaInfo", infoMddf.label_size).AppendLine(); - sb.AppendFormat("Filesystem overhead: {0}", infoMddf.fs_overhead).AppendLine(); - sb.AppendFormat("Scavenger result code: 0x{0:X8}", infoMddf.result_scavenge).AppendLine(); - sb.AppendFormat("Boot code: 0x{0:X8}", infoMddf.boot_code).AppendLine(); - sb.AppendFormat("Boot environment: 0x{0:X8}", infoMddf.boot_environ).AppendLine(); - sb.AppendFormat("Overmount stamp: 0x{0:X16}", infoMddf.overmount_stamp).AppendLine(); - - sb.AppendFormat("S-Records start at {0} and spans for {1} blocks", - infoMddf.srec_ptr + infoMddf.mddf_block + beforeMddf, infoMddf.srec_len).AppendLine(); - - sb.AppendLine(infoMddf.vol_left_mounted == 0 ? "Volume is clean" : "Volume is dirty"); - - information = sb.ToString(); - - XmlFsType = new FileSystemType(); - - if(DateTime.Compare(infoMddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) - { - XmlFsType.BackupDate = infoMddf.dtvb; - XmlFsType.BackupDateSpecified = true; - } - - XmlFsType.Clusters = infoMddf.vol_size; - XmlFsType.ClusterSize = (uint)(infoMddf.clustersize * infoMddf.datasize); - - if(DateTime.Compare(infoMddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) - { - XmlFsType.CreationDate = infoMddf.dtvc; - XmlFsType.CreationDateSpecified = true; - } - - XmlFsType.Dirty = infoMddf.vol_left_mounted != 0; - XmlFsType.Files = infoMddf.filecount; - XmlFsType.FilesSpecified = true; - XmlFsType.FreeClusters = infoMddf.freecount; - XmlFsType.FreeClustersSpecified = true; - XmlFsType.Type = "LisaFS"; - XmlFsType.VolumeName = infoMddf.volname; - XmlFsType.VolumeSerial = $"{infoMddf.volid:X16}"; - - return; + break; } + + sb.AppendFormat("Volume name: \"{0}\"", infoMddf.volname).AppendLine(); + sb.AppendFormat("Volume password: \"{0}\"", infoMddf.password).AppendLine(); + sb.AppendFormat("Volume ID: 0x{0:X16}", infoMddf.volid).AppendLine(); + sb.AppendFormat("Backup volume ID: 0x{0:X16}", infoMddf.backup_volid).AppendLine(); + + sb.AppendFormat("Master copy ID: 0x{0:X8}", infoMddf.master_copy_id).AppendLine(); + + sb.AppendFormat("Volume is number {0} of {1}", infoMddf.volnum, infoMddf.vol_sequence).AppendLine(); + + sb.AppendFormat("Serial number of Lisa computer that created this volume: {0}", infoMddf.machine_id). + AppendLine(); + + sb.AppendFormat("Serial number of Lisa computer that can use this volume's software {0}", + infoMddf.serialization).AppendLine(); + + sb.AppendFormat("Volume created on {0}", infoMddf.dtvc).AppendLine(); + sb.AppendFormat("Some timestamp, says {0}", infoMddf.dtcc).AppendLine(); + sb.AppendFormat("Volume backed up on {0}", infoMddf.dtvb).AppendLine(); + sb.AppendFormat("Volume scavenged on {0}", infoMddf.dtvs).AppendLine(); + sb.AppendFormat("MDDF is in block {0}", infoMddf.mddf_block + beforeMddf).AppendLine(); + sb.AppendFormat("There are {0} reserved blocks before volume", beforeMddf).AppendLine(); + sb.AppendFormat("{0} blocks minus one", infoMddf.volsize_minus_one).AppendLine(); + + sb.AppendFormat("{0} blocks minus one minus MDDF offset", infoMddf.volsize_minus_mddf_minus_one). + AppendLine(); + + sb.AppendFormat("{0} blocks in volume", infoMddf.vol_size).AppendLine(); + sb.AppendFormat("{0} bytes per sector (uncooked)", infoMddf.blocksize).AppendLine(); + sb.AppendFormat("{0} bytes per sector", infoMddf.datasize).AppendLine(); + sb.AppendFormat("{0} blocks per cluster", infoMddf.clustersize).AppendLine(); + sb.AppendFormat("{0} blocks in filesystem", infoMddf.fs_size).AppendLine(); + sb.AppendFormat("{0} files in volume", infoMddf.filecount).AppendLine(); + sb.AppendFormat("{0} blocks free", infoMddf.freecount).AppendLine(); + sb.AppendFormat("{0} bytes in LisaInfo", infoMddf.label_size).AppendLine(); + sb.AppendFormat("Filesystem overhead: {0}", infoMddf.fs_overhead).AppendLine(); + sb.AppendFormat("Scavenger result code: 0x{0:X8}", infoMddf.result_scavenge).AppendLine(); + sb.AppendFormat("Boot code: 0x{0:X8}", infoMddf.boot_code).AppendLine(); + sb.AppendFormat("Boot environment: 0x{0:X8}", infoMddf.boot_environ).AppendLine(); + sb.AppendFormat("Overmount stamp: 0x{0:X16}", infoMddf.overmount_stamp).AppendLine(); + + sb.AppendFormat("S-Records start at {0} and spans for {1} blocks", + infoMddf.srec_ptr + infoMddf.mddf_block + beforeMddf, infoMddf.srec_len).AppendLine(); + + sb.AppendLine(infoMddf.vol_left_mounted == 0 ? "Volume is clean" : "Volume is dirty"); + + information = sb.ToString(); + + XmlFsType = new FileSystemType(); + + if(DateTime.Compare(infoMddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) + { + XmlFsType.BackupDate = infoMddf.dtvb; + XmlFsType.BackupDateSpecified = true; + } + + XmlFsType.Clusters = infoMddf.vol_size; + XmlFsType.ClusterSize = (uint)(infoMddf.clustersize * infoMddf.datasize); + + if(DateTime.Compare(infoMddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) + { + XmlFsType.CreationDate = infoMddf.dtvc; + XmlFsType.CreationDateSpecified = true; + } + + XmlFsType.Dirty = infoMddf.vol_left_mounted != 0; + XmlFsType.Files = infoMddf.filecount; + XmlFsType.FilesSpecified = true; + XmlFsType.FreeClusters = infoMddf.freecount; + XmlFsType.FreeClustersSpecified = true; + XmlFsType.Type = "LisaFS"; + XmlFsType.VolumeName = infoMddf.volname; + XmlFsType.VolumeSerial = $"{infoMddf.volid:X16}"; + + return; } } } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/LisaFS.cs b/Aaru.Filesystems/LisaFS/LisaFS.cs index 5d293343b..b72d22b83 100644 --- a/Aaru.Filesystems/LisaFS/LisaFS.cs +++ b/Aaru.Filesystems/LisaFS/LisaFS.cs @@ -36,72 +36,71 @@ using System.Text; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Filesystems.LisaFS +namespace Aaru.Filesystems.LisaFS; + +// All information by Natalia Portillo +// Variable names from Lisa API +/// +/// Implements the Apple Lisa File System +public sealed partial class LisaFS : IReadOnlyFilesystem { - // All information by Natalia Portillo - // Variable names from Lisa API + bool _debug; + IMediaImage _device; + int _devTagSize; + MDDF _mddf; + bool _mounted; + SRecord[] _srecords; + ulong _volumePrefix; + /// - /// Implements the Apple Lisa File System - public sealed partial class LisaFS : IReadOnlyFilesystem + public string Name => "Apple Lisa File System"; + /// + public Guid Id => new("7E6034D1-D823-4248-A54D-239742B28391"); + /// + public Encoding Encoding { get; private set; } + /// + public FileSystemType XmlFsType { get; private set; } + /// + public string Author => "Natalia Portillo"; + + // TODO: Implement Lisa 7/7 namespace (needs decoding {!CATALOG} file) + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + {}; + + /// + public Dictionary Namespaces => new() { - bool _debug; - IMediaImage _device; - int _devTagSize; - MDDF _mddf; - bool _mounted; - SRecord[] _srecords; - ulong _volumePrefix; - - /// - public string Name => "Apple Lisa File System"; - /// - public Guid Id => new("7E6034D1-D823-4248-A54D-239742B28391"); - /// - public Encoding Encoding { get; private set; } - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Author => "Natalia Portillo"; - - // TODO: Implement Lisa 7/7 namespace (needs decoding {!CATALOG} file) - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - {}; - - /// - public Dictionary Namespaces => new() { - { - "workshop", "Filenames as shown by the Lisa Pascal Workshop (default)" - }, - { - "office", "Filenames as shown by the Lisa Office System (not yet implemented)" - } - }; - - static Dictionary GetDefaultOptions() => new() + "workshop", "Filenames as shown by the Lisa Pascal Workshop (default)" + }, { - { - "debug", false.ToString() - } - }; + "office", "Filenames as shown by the Lisa Office System (not yet implemented)" + } + }; - #region Caches - /// Caches Extents Files - Dictionary _extentCache; - /// Caches system files - Dictionary _systemFileCache; - /// Caches user files files - Dictionary _fileCache; - /// Caches catalogs - List _catalogCache; - /// Caches file size - Dictionary _fileSizeCache; - /// Lists Extents Files already printed in debug mode to not repeat them - List _printedExtents; - /// Caches the creation times for subdirectories as to not have to traverse the Catalog File on each stat - Dictionary _directoryDtcCache; - #endregion Caches - } + static Dictionary GetDefaultOptions() => new() + { + { + "debug", false.ToString() + } + }; + + #region Caches + /// Caches Extents Files + Dictionary _extentCache; + /// Caches system files + Dictionary _systemFileCache; + /// Caches user files files + Dictionary _fileCache; + /// Caches catalogs + List _catalogCache; + /// Caches file size + Dictionary _fileSizeCache; + /// Lists Extents Files already printed in debug mode to not repeat them + List _printedExtents; + /// Caches the creation times for subdirectories as to not have to traverse the Catalog File on each stat + Dictionary _directoryDtcCache; + #endregion Caches } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/Structs.cs b/Aaru.Filesystems/LisaFS/Structs.cs index da1ee428b..a167d4ddc 100644 --- a/Aaru.Filesystems/LisaFS/Structs.cs +++ b/Aaru.Filesystems/LisaFS/Structs.cs @@ -35,369 +35,368 @@ using System.Diagnostics.CodeAnalysis; // ReSharper disable NotAccessedField.Local -namespace Aaru.Filesystems.LisaFS +namespace Aaru.Filesystems.LisaFS; + +public sealed partial class LisaFS { - public sealed partial class LisaFS + /// + /// The MDDF is the most import block on a Lisa FS volume. It describes the volume and its contents. On + /// initialization the memory where it resides is not emptied so it tends to contain a lot of garbage. This has + /// difficulted its reverse engineering. + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct MDDF { + /// 0x00, Filesystem version + public ushort fsversion; + /// 0x02, Volume ID + public ulong volid; + /// 0x0A, Volume sequence number + public ushort volnum; + /// 0x0C, Pascal string, 32+1 bytes, volume name + public string volname; + /// 0x2D, unknown, possible padding + public byte unknown1; + /// 0x2E, Pascal string, 32+1 bytes, password + public string password; + /// 0x4F, unknown, possible padding + public byte unknown2; + /// 0x50, Lisa serial number that init'ed this disk + public uint machine_id; + /// 0x54, ID of the master copy ? no idea really + public uint master_copy_id; + /// 0x58, Date of volume creation + public DateTime dtvc; + /// 0x5C, Date... + public DateTime dtcc; + /// 0x60, Date of volume backup + public DateTime dtvb; + /// 0x64, Date of volume scavenging + public DateTime dtvs; + /// 0x68, unknown + public uint unknown3; + /// 0x6C, block the MDDF is residing on + public uint mddf_block; + /// 0x70, volsize-1 + public uint volsize_minus_one; + /// 0x74, volsize-1-mddf_block + public uint volsize_minus_mddf_minus_one; + /// 0x78, Volume size in blocks + public uint vol_size; + /// 0x7C, Blocks size of underlying drive (data+tags) + public ushort blocksize; + /// 0x7E, Data only block size + public ushort datasize; + /// 0x80, unknown + public ushort unknown4; + /// 0x82, unknown + public uint unknown5; + /// 0x86, unknown + public uint unknown6; + /// 0x8A, Size in sectors of filesystem clusters + public ushort clustersize; + /// 0x8C, Filesystem size in blocks + public uint fs_size; + /// 0x90, unknown + public uint unknown7; + /// 0x94, Pointer to S-Records + public uint srec_ptr; + /// 0x98, unknown + public ushort unknown9; + /// 0x9A, S-Records length + public ushort srec_len; + /// 0x9C, unknown + public uint unknown10; + /// 0xA0, unknown + public uint unknown11; + /// 0xA4, unknown + public uint unknown12; + /// 0xA8, unknown + public uint unknown13; + /// 0xAC, unknown + public uint unknown14; + /// 0xB0, Files in volume + public ushort filecount; + /// 0xB2, unknown + public uint unknown15; + /// 0xB6, unknown + public uint unknown16; + /// 0xBA, Free blocks + public uint freecount; + /// 0xBE, unknown + public ushort unknown17; + /// 0xC0, unknown + public uint unknown18; + /// 0xC4, no idea + public ulong overmount_stamp; + /// 0xCC, serialization, lisa serial number authorized to use blocked software on this volume + public uint serialization; + /// 0xD0, unknown + public uint unknown19; + /// 0xD4, unknown, possible timestamp + public uint unknown_timestamp; + /// 0xD8, unknown + public uint unknown20; + /// 0xDC, unknown + public uint unknown21; + /// 0xE0, unknown + public uint unknown22; + /// 0xE4, unknown + public uint unknown23; + /// 0xE8, unknown + public uint unknown24; + /// 0xEC, unknown + public uint unknown25; + /// 0xF0, unknown + public uint unknown26; + /// 0xF4, unknown + public uint unknown27; + /// 0xF8, unknown + public uint unknown28; + /// 0xFC, unknown + public uint unknown29; + /// 0x100, unknown + public uint unknown30; + /// 0x104, unknown + public uint unknown31; + /// 0x108, unknown + public uint unknown32; + /// 0x10C, unknown + public uint unknown33; + /// 0x110, unknown + public uint unknown34; + /// 0x114, unknown + public uint unknown35; + /// 0x118, ID of volume where this volume was backed up + public ulong backup_volid; + /// 0x120, Size of LisaInfo label + public ushort label_size; + /// 0x122, not clear + public ushort fs_overhead; + /// 0x124, Return code of Scavenger + public ushort result_scavenge; + /// 0x126, No idea + public ushort boot_code; + /// 0x128, No idea + public ushort boot_environ; + /// 0x12A, unknown + public uint unknown36; + /// 0x12E, unknown + public uint unknown37; + /// 0x132, unknown + public uint unknown38; + /// 0x136, Total volumes in sequence + public ushort vol_sequence; + /// 0x138, Volume is dirty? + public byte vol_left_mounted; + /// Is password present? (On-disk position unknown) + public byte passwd_present; + /// Opened files (memory-only?) (On-disk position unknown) + public uint opencount; + /// No idea (On-disk position unknown) + public uint copy_thread; + + // Flags are boolean, but Pascal seems to use them as full unsigned 8 bit values + /// No idea (On-disk position unknown) + public byte privileged; + /// Read-only volume (On-disk position unknown) + public byte write_protected; + /// Master disk (On-disk position unknown) + public byte master; + /// Copy disk (On-disk position unknown) + public byte copy; + /// No idea (On-disk position unknown) + public byte copy_flag; + /// No idea (On-disk position unknown) + public byte scavenge_flag; + } + + /// + /// An entry in the catalog from V3. The first entry is bigger than the rest, may be a header, I have not needed + /// any of its values so I just ignored it. Each catalog is divided in 4-sector blocks, and if it needs more than a + /// block there are previous and next block pointers, effectively making the V3 catalog a double-linked list. Garbage + /// is not zeroed. + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct CatalogEntry + { + /// 0x00, seems to be 0x24 when the entry is valid + public byte marker; + /// 0x01, parent directory ID for this file, 0 for root directory + public ushort parentID; + /// 0x03, filename, 32-bytes, null-padded + public byte[] filename; + /// 0x23, null-termination + public byte terminator; /// - /// The MDDF is the most import block on a Lisa FS volume. It describes the volume and its contents. On - /// initialization the memory where it resides is not emptied so it tends to contain a lot of garbage. This has - /// difficulted its reverse engineering. + /// At 0x24 0x01 here for subdirectories, entries 48 bytes long 0x03 here for entries 64 bytes long 0x08 here for + /// entries 78 bytes long This is incomplete, may fail, mostly works... /// - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct MDDF - { - /// 0x00, Filesystem version - public ushort fsversion; - /// 0x02, Volume ID - public ulong volid; - /// 0x0A, Volume sequence number - public ushort volnum; - /// 0x0C, Pascal string, 32+1 bytes, volume name - public string volname; - /// 0x2D, unknown, possible padding - public byte unknown1; - /// 0x2E, Pascal string, 32+1 bytes, password - public string password; - /// 0x4F, unknown, possible padding - public byte unknown2; - /// 0x50, Lisa serial number that init'ed this disk - public uint machine_id; - /// 0x54, ID of the master copy ? no idea really - public uint master_copy_id; - /// 0x58, Date of volume creation - public DateTime dtvc; - /// 0x5C, Date... - public DateTime dtcc; - /// 0x60, Date of volume backup - public DateTime dtvb; - /// 0x64, Date of volume scavenging - public DateTime dtvs; - /// 0x68, unknown - public uint unknown3; - /// 0x6C, block the MDDF is residing on - public uint mddf_block; - /// 0x70, volsize-1 - public uint volsize_minus_one; - /// 0x74, volsize-1-mddf_block - public uint volsize_minus_mddf_minus_one; - /// 0x78, Volume size in blocks - public uint vol_size; - /// 0x7C, Blocks size of underlying drive (data+tags) - public ushort blocksize; - /// 0x7E, Data only block size - public ushort datasize; - /// 0x80, unknown - public ushort unknown4; - /// 0x82, unknown - public uint unknown5; - /// 0x86, unknown - public uint unknown6; - /// 0x8A, Size in sectors of filesystem clusters - public ushort clustersize; - /// 0x8C, Filesystem size in blocks - public uint fs_size; - /// 0x90, unknown - public uint unknown7; - /// 0x94, Pointer to S-Records - public uint srec_ptr; - /// 0x98, unknown - public ushort unknown9; - /// 0x9A, S-Records length - public ushort srec_len; - /// 0x9C, unknown - public uint unknown10; - /// 0xA0, unknown - public uint unknown11; - /// 0xA4, unknown - public uint unknown12; - /// 0xA8, unknown - public uint unknown13; - /// 0xAC, unknown - public uint unknown14; - /// 0xB0, Files in volume - public ushort filecount; - /// 0xB2, unknown - public uint unknown15; - /// 0xB6, unknown - public uint unknown16; - /// 0xBA, Free blocks - public uint freecount; - /// 0xBE, unknown - public ushort unknown17; - /// 0xC0, unknown - public uint unknown18; - /// 0xC4, no idea - public ulong overmount_stamp; - /// 0xCC, serialization, lisa serial number authorized to use blocked software on this volume - public uint serialization; - /// 0xD0, unknown - public uint unknown19; - /// 0xD4, unknown, possible timestamp - public uint unknown_timestamp; - /// 0xD8, unknown - public uint unknown20; - /// 0xDC, unknown - public uint unknown21; - /// 0xE0, unknown - public uint unknown22; - /// 0xE4, unknown - public uint unknown23; - /// 0xE8, unknown - public uint unknown24; - /// 0xEC, unknown - public uint unknown25; - /// 0xF0, unknown - public uint unknown26; - /// 0xF4, unknown - public uint unknown27; - /// 0xF8, unknown - public uint unknown28; - /// 0xFC, unknown - public uint unknown29; - /// 0x100, unknown - public uint unknown30; - /// 0x104, unknown - public uint unknown31; - /// 0x108, unknown - public uint unknown32; - /// 0x10C, unknown - public uint unknown33; - /// 0x110, unknown - public uint unknown34; - /// 0x114, unknown - public uint unknown35; - /// 0x118, ID of volume where this volume was backed up - public ulong backup_volid; - /// 0x120, Size of LisaInfo label - public ushort label_size; - /// 0x122, not clear - public ushort fs_overhead; - /// 0x124, Return code of Scavenger - public ushort result_scavenge; - /// 0x126, No idea - public ushort boot_code; - /// 0x128, No idea - public ushort boot_environ; - /// 0x12A, unknown - public uint unknown36; - /// 0x12E, unknown - public uint unknown37; - /// 0x132, unknown - public uint unknown38; - /// 0x136, Total volumes in sequence - public ushort vol_sequence; - /// 0x138, Volume is dirty? - public byte vol_left_mounted; - /// Is password present? (On-disk position unknown) - public byte passwd_present; - /// Opened files (memory-only?) (On-disk position unknown) - public uint opencount; - /// No idea (On-disk position unknown) - public uint copy_thread; + public byte fileType; + /// 0x25, lot of values found here, unknown + public byte unknown; + /// 0x26, file ID, must be positive and bigger than 4 + public short fileID; + /// 0x28, creation date + public uint dtc; + /// 0x2C, last modification date + public uint dtm; + /// 0x30, file length in bytes + public int length; + /// 0x34, file length in bytes, including wasted block space + public int wasted; + /// 0x38, unknown + public byte[] tail; + } - // Flags are boolean, but Pascal seems to use them as full unsigned 8 bit values - /// No idea (On-disk position unknown) - public byte privileged; - /// Read-only volume (On-disk position unknown) - public byte write_protected; - /// Master disk (On-disk position unknown) - public byte master; - /// Copy disk (On-disk position unknown) - public byte copy; - /// No idea (On-disk position unknown) - public byte copy_flag; - /// No idea (On-disk position unknown) - public byte scavenge_flag; - } + /// An extent indicating a start and a run of sectors. + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct Extent + { + public int start; + public short length; + } + /// + /// The Extents File. There is one Extents File per each file stored on disk. The file ID present on the sectors + /// tags for the Extents File is the negated value of the file ID it represents. e.g. file = 5 (0x0005) extents = -5 + /// (0xFFFB) It spans a single sector on V2 and V3 but 2 sectors on V1. It contains all information about a file, and + /// is indexed in the S-Records file. It also contains the label. Garbage is zeroed. + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct ExtentFile + { + /// 0x00, filename length + public byte filenameLen; + /// 0x01, filename + public byte[] filename; + /// 0x20, unknown + public ushort unknown1; + /// 0x22, 8 bytes + public ulong file_uid; + /// 0x2A, unknown + public byte unknown2; + /// 0x2B, entry type? gets modified + public byte etype; + /// 0x2C, file type + public FileType ftype; + /// 0x2D, unknown + public byte unknown3; + /// 0x2E, creation time + public uint dtc; + /// 0x32, last access time + public uint dta; + /// 0x36, modification time + public uint dtm; + /// 0x3A, backup time + public uint dtb; + /// 0x3E, scavenge time + public uint dts; + /// 0x42, machine serial number + public uint serial; + /// 0x46, unknown + public byte unknown4; + /// 0x47, locked file + public byte locked; + /// 0x48, protected file + public byte protect; + /// 0x49, master file + public byte master; + /// 0x4A, scavenged file + public byte scavenged; + /// 0x4B, file closed by os + public byte closed; + /// 0x4C, file left open + public byte open; + /// 0x4D, 11 bytes, unknown + public byte[] unknown5; + /// 0x58, Release number + public ushort release; + /// 0x5A, Build number + public ushort build; + /// 0x5C, Compatibility level + public ushort compatibility; + /// 0x5E, Revision level + public ushort revision; + /// 0x60, unknown + public ushort unknown6; + /// 0x62, 0x08 set if password is valid + public byte password_valid; + /// 0x63, 8 bytes, scrambled password + public byte[] password; + /// 0x6B, 3 bytes, unknown + public byte[] unknown7; + /// 0x6E, filesystem overhead + public ushort overhead; + /// 0x70, 16 bytes, unknown + public byte[] unknown8; + /// 0x80, 0x200 in v1, file length in blocks + public int length; + /// 0x84, 0x204 in v1, unknown + public int unknown9; /// - /// An entry in the catalog from V3. The first entry is bigger than the rest, may be a header, I have not needed - /// any of its values so I just ignored it. Each catalog is divided in 4-sector blocks, and if it needs more than a - /// block there are previous and next block pointers, effectively making the V3 catalog a double-linked list. Garbage - /// is not zeroed. + /// 0x88, 0x208 in v1, extents, can contain up to 41 extents (85 in v1), dunno LisaOS maximum (never seen more + /// than 3) /// - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct CatalogEntry - { - /// 0x00, seems to be 0x24 when the entry is valid - public byte marker; - /// 0x01, parent directory ID for this file, 0 for root directory - public ushort parentID; - /// 0x03, filename, 32-bytes, null-padded - public byte[] filename; - /// 0x23, null-termination - public byte terminator; - /// - /// At 0x24 0x01 here for subdirectories, entries 48 bytes long 0x03 here for entries 64 bytes long 0x08 here for - /// entries 78 bytes long This is incomplete, may fail, mostly works... - /// - public byte fileType; - /// 0x25, lot of values found here, unknown - public byte unknown; - /// 0x26, file ID, must be positive and bigger than 4 - public short fileID; - /// 0x28, creation date - public uint dtc; - /// 0x2C, last modification date - public uint dtm; - /// 0x30, file length in bytes - public int length; - /// 0x34, file length in bytes, including wasted block space - public int wasted; - /// 0x38, unknown - public byte[] tail; - } - - /// An extent indicating a start and a run of sectors. - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct Extent - { - public int start; - public short length; - } - + public Extent[] extents; + /// 0x17E, unknown, empty, padding? + public short unknown10; /// - /// The Extents File. There is one Extents File per each file stored on disk. The file ID present on the sectors - /// tags for the Extents File is the negated value of the file ID it represents. e.g. file = 5 (0x0005) extents = -5 - /// (0xFFFB) It spans a single sector on V2 and V3 but 2 sectors on V1. It contains all information about a file, and - /// is indexed in the S-Records file. It also contains the label. Garbage is zeroed. + /// At 0x180, this is the label. While 1982 pre-release documentation says the label can be up to 448 bytes, v1 + /// onward only have space for a 128 bytes one. Any application can write whatever they want in the label, however, + /// Lisa Office uses it to store its own information, something that will effectively overwrite any information a user + /// application wrote there. The information written here by Lisa Office is like the information Finder writes in the + /// FinderInfo structures, plus the non-unique name that is shown on the GUI. For this reason I called it LisaInfo. I + /// have not tried to reverse engineer it. /// - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct ExtentFile - { - /// 0x00, filename length - public byte filenameLen; - /// 0x01, filename - public byte[] filename; - /// 0x20, unknown - public ushort unknown1; - /// 0x22, 8 bytes - public ulong file_uid; - /// 0x2A, unknown - public byte unknown2; - /// 0x2B, entry type? gets modified - public byte etype; - /// 0x2C, file type - public FileType ftype; - /// 0x2D, unknown - public byte unknown3; - /// 0x2E, creation time - public uint dtc; - /// 0x32, last access time - public uint dta; - /// 0x36, modification time - public uint dtm; - /// 0x3A, backup time - public uint dtb; - /// 0x3E, scavenge time - public uint dts; - /// 0x42, machine serial number - public uint serial; - /// 0x46, unknown - public byte unknown4; - /// 0x47, locked file - public byte locked; - /// 0x48, protected file - public byte protect; - /// 0x49, master file - public byte master; - /// 0x4A, scavenged file - public byte scavenged; - /// 0x4B, file closed by os - public byte closed; - /// 0x4C, file left open - public byte open; - /// 0x4D, 11 bytes, unknown - public byte[] unknown5; - /// 0x58, Release number - public ushort release; - /// 0x5A, Build number - public ushort build; - /// 0x5C, Compatibility level - public ushort compatibility; - /// 0x5E, Revision level - public ushort revision; - /// 0x60, unknown - public ushort unknown6; - /// 0x62, 0x08 set if password is valid - public byte password_valid; - /// 0x63, 8 bytes, scrambled password - public byte[] password; - /// 0x6B, 3 bytes, unknown - public byte[] unknown7; - /// 0x6E, filesystem overhead - public ushort overhead; - /// 0x70, 16 bytes, unknown - public byte[] unknown8; - /// 0x80, 0x200 in v1, file length in blocks - public int length; - /// 0x84, 0x204 in v1, unknown - public int unknown9; - /// - /// 0x88, 0x208 in v1, extents, can contain up to 41 extents (85 in v1), dunno LisaOS maximum (never seen more - /// than 3) - /// - public Extent[] extents; - /// 0x17E, unknown, empty, padding? - public short unknown10; - /// - /// At 0x180, this is the label. While 1982 pre-release documentation says the label can be up to 448 bytes, v1 - /// onward only have space for a 128 bytes one. Any application can write whatever they want in the label, however, - /// Lisa Office uses it to store its own information, something that will effectively overwrite any information a user - /// application wrote there. The information written here by Lisa Office is like the information Finder writes in the - /// FinderInfo structures, plus the non-unique name that is shown on the GUI. For this reason I called it LisaInfo. I - /// have not tried to reverse engineer it. - /// - public byte[] LisaInfo; - } + public byte[] LisaInfo; + } - /// - /// The S-Records File is a hashtable of S-Records, where the hash is the file ID they belong to. The S-Records - /// File cannot be fragmented or grown, and it can easily become full before the 32766 file IDs are exhausted. Each - /// S-Record entry contains a block pointer to the Extents File that correspond to that file ID as well as the real - /// file size, the only important information about a file that's not inside the Extents File. It also contains a low - /// value (less than 0x200) variable field of unknown meaning and another one that seems to be flags, with values like - /// 0, 1, 3 and 5. - /// - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct SRecord - { - /// 0x00, block where ExtentsFile for this entry resides - public uint extent_ptr; - /// 0x04, unknown - public uint unknown; - /// 0x08, filesize in bytes - public uint filesize; - /// 0x0C, some kind of flags, meaning unknown - public ushort flags; - } + /// + /// The S-Records File is a hashtable of S-Records, where the hash is the file ID they belong to. The S-Records + /// File cannot be fragmented or grown, and it can easily become full before the 32766 file IDs are exhausted. Each + /// S-Record entry contains a block pointer to the Extents File that correspond to that file ID as well as the real + /// file size, the only important information about a file that's not inside the Extents File. It also contains a low + /// value (less than 0x200) variable field of unknown meaning and another one that seems to be flags, with values like + /// 0, 1, 3 and 5. + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct SRecord + { + /// 0x00, block where ExtentsFile for this entry resides + public uint extent_ptr; + /// 0x04, unknown + public uint unknown; + /// 0x08, filesize in bytes + public uint filesize; + /// 0x0C, some kind of flags, meaning unknown + public ushort flags; + } - /// - /// The catalog entry for the V1 and V2 volume formats. It merely contains the file name, type and ID, plus a few - /// (mostly empty) unknown fields. Contrary to V3, it has no header and instead of being a double-linked list it is - /// fragmented using an Extents File. The Extents File position for the root catalog is then stored in the S-Records - /// File. Its entries are not filed sequentially denoting some kind of in-memory structure while at the same time - /// forcing LisaOS to read the whole catalog. That or I missed the pointers. Empty entries just contain a 0-len - /// filename. Garbage is not zeroed. - /// - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct CatalogEntryV2 - { - /// 0x00, filename, 32-bytes, null-padded - public byte filenameLen; - /// 0x01, filename, 31-bytes - public byte[] filename; - /// 0x21, unknown - public byte unknown1; - /// 0x22, unknown - public byte fileType; - /// 0x23, unknown - public byte unknown2; - /// 0x24, unknown - public short fileID; - /// 0x26, 16 bytes, unknown - public byte[] unknown3; - } + /// + /// The catalog entry for the V1 and V2 volume formats. It merely contains the file name, type and ID, plus a few + /// (mostly empty) unknown fields. Contrary to V3, it has no header and instead of being a double-linked list it is + /// fragmented using an Extents File. The Extents File position for the root catalog is then stored in the S-Records + /// File. Its entries are not filed sequentially denoting some kind of in-memory structure while at the same time + /// forcing LisaOS to read the whole catalog. That or I missed the pointers. Empty entries just contain a 0-len + /// filename. Garbage is not zeroed. + /// + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct CatalogEntryV2 + { + /// 0x00, filename, 32-bytes, null-padded + public byte filenameLen; + /// 0x01, filename, 31-bytes + public byte[] filename; + /// 0x21, unknown + public byte unknown1; + /// 0x22, unknown + public byte fileType; + /// 0x23, unknown + public byte unknown2; + /// 0x24, unknown + public short fileID; + /// 0x26, 16 bytes, unknown + public byte[] unknown3; } } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/Super.cs b/Aaru.Filesystems/LisaFS/Super.cs index 6aa132dec..6c36f2a6c 100644 --- a/Aaru.Filesystems/LisaFS/Super.cs +++ b/Aaru.Filesystems/LisaFS/Super.cs @@ -43,386 +43,385 @@ using Claunia.Encoding; using Schemas; using Encoding = System.Text.Encoding; -namespace Aaru.Filesystems.LisaFS +namespace Aaru.Filesystems.LisaFS; + +public sealed partial class LisaFS { - public sealed partial class LisaFS + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + try { - try + _device = imagePlugin; + Encoding = new LisaRoman(); + + // Lisa OS is unable to work on disks without tags. + // This code is designed like that. + // However with some effort the code may be modified to ignore them. + if(_device.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true) { - _device = imagePlugin; - Encoding = new LisaRoman(); + AaruConsole.DebugWriteLine("LisaFS plugin", "Underlying device does not support Lisa tags"); - // Lisa OS is unable to work on disks without tags. - // This code is designed like that. - // However with some effort the code may be modified to ignore them. - if(_device.Info.ReadableSectorTags?.Contains(SectorTagType.AppleSectorTag) != true) + return ErrorNumber.InOutError; + } + + // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors + if(_device.Info.Sectors < 800) + { + AaruConsole.DebugWriteLine("LisaFS plugin", "Device is too small"); + + return ErrorNumber.InOutError; + } + + // MDDF cannot be at end of device, of course + _volumePrefix = _device.Info.Sectors; + + // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors + for(ulong i = 0; i < 100; i++) + { + ErrorNumber errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out byte[] tag); + + if(errno != ErrorNumber.NoError) + continue; + + DecodeTag(tag, out LisaTag.PriamTag searchTag); + + AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); + + if(_volumePrefix == _device.Info.Sectors && + searchTag.FileId == FILEID_LOADER_SIGNED) + _volumePrefix = i - 1; + + if(searchTag.FileId != FILEID_MDDF) + continue; + + _devTagSize = tag.Length; + errno = _device.ReadSector(i, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return errno; + + _mddf = new MDDF(); + byte[] pString = new byte[33]; + + _mddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); + _mddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); + _mddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); + Array.Copy(sector, 0x0C, pString, 0, 33); + _mddf.volname = StringHandlers.PascalToString(pString, Encoding); + _mddf.unknown1 = sector[0x2D]; + Array.Copy(sector, 0x2E, pString, 0, 33); + + // Prevent garbage + _mddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; + _mddf.unknown2 = sector[0x4F]; + _mddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); + _mddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); + uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); + _mddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); + lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); + _mddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); + lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); + _mddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); + lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); + _mddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); + _mddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); + _mddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); + _mddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); + _mddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); + _mddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); + _mddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); + _mddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); + _mddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); + _mddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); + _mddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); + _mddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); + _mddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); + _mddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); + _mddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); + _mddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); + _mddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); + _mddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); + _mddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); + _mddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); + _mddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); + _mddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); + _mddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); + _mddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); + _mddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); + _mddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); + _mddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); + _mddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); + _mddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); + _mddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); + _mddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); + _mddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); + _mddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); + _mddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); + _mddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); + _mddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); + _mddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); + _mddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); + _mddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); + _mddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); + _mddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); + _mddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); + _mddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); + _mddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); + _mddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); + _mddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); + _mddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); + _mddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); + _mddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); + _mddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); + _mddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); + _mddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); + _mddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); + _mddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); + _mddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); + _mddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); + _mddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); + _mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); + _mddf.vol_left_mounted = sector[0x138]; + + // Check that the MDDF is correct + if(_mddf.mddf_block != i - _volumePrefix || + _mddf.vol_size > _device.Info.Sectors || + _mddf.vol_size - 1 != _mddf.volsize_minus_one || + _mddf.vol_size - i - 1 != _mddf.volsize_minus_mddf_minus_one - _volumePrefix || + _mddf.datasize > _mddf.blocksize || + _mddf.blocksize < _device.Info.SectorSize || + _mddf.datasize != _device.Info.SectorSize) { - AaruConsole.DebugWriteLine("LisaFS plugin", "Underlying device does not support Lisa tags"); + AaruConsole.DebugWriteLine("LisaFS plugin", "Incorrect MDDF found"); - return ErrorNumber.InOutError; + return ErrorNumber.InvalidArgument; } - // Minimal LisaOS disk is 3.5" single sided double density, 800 sectors - if(_device.Info.Sectors < 800) + // Check MDDF version + switch(_mddf.fsversion) { - AaruConsole.DebugWriteLine("LisaFS plugin", "Device is too small"); + case LISA_V1: + AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v1"); - return ErrorNumber.InOutError; + break; + case LISA_V2: + AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v2"); + + break; + case LISA_V3: + AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v3"); + + break; + default: + AaruConsole.ErrorWriteLine("Cannot mount LisaFS version {0}", _mddf.fsversion.ToString()); + + return ErrorNumber.NotSupported; } - // MDDF cannot be at end of device, of course - _volumePrefix = _device.Info.Sectors; + // Initialize caches + _extentCache = new Dictionary(); + _systemFileCache = new Dictionary(); + _fileCache = new Dictionary(); - // LisaOS searches sectors until tag tells MDDF resides there, so we'll search 100 sectors - for(ulong i = 0; i < 100; i++) + //catalogCache = new Dictionary>(); + _fileSizeCache = new Dictionary(); + + _mounted = true; + + options ??= GetDefaultOptions(); + + if(options.TryGetValue("debug", out string debugString)) + bool.TryParse(debugString, out _debug); + + if(_debug) + _printedExtents = new List(); + + // Read the S-Records file + ErrorNumber error = ReadSRecords(); + + if(error != ErrorNumber.NoError) { - ErrorNumber errno = _device.ReadSectorTag(i, SectorTagType.AppleSectorTag, out byte[] tag); + AaruConsole.ErrorWriteLine("Error {0} reading S-Records file.", error); - if(errno != ErrorNumber.NoError) - continue; + return error; + } - DecodeTag(tag, out LisaTag.PriamTag searchTag); - - AaruConsole.DebugWriteLine("LisaFS plugin", "Sector {0}, file ID 0x{1:X4}", i, searchTag.FileId); - - if(_volumePrefix == _device.Info.Sectors && - searchTag.FileId == FILEID_LOADER_SIGNED) - _volumePrefix = i - 1; - - if(searchTag.FileId != FILEID_MDDF) - continue; - - _devTagSize = tag.Length; - errno = _device.ReadSector(i, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return errno; - - _mddf = new MDDF(); - byte[] pString = new byte[33]; - - _mddf.fsversion = BigEndianBitConverter.ToUInt16(sector, 0x00); - _mddf.volid = BigEndianBitConverter.ToUInt64(sector, 0x02); - _mddf.volnum = BigEndianBitConverter.ToUInt16(sector, 0x0A); - Array.Copy(sector, 0x0C, pString, 0, 33); - _mddf.volname = StringHandlers.PascalToString(pString, Encoding); - _mddf.unknown1 = sector[0x2D]; - Array.Copy(sector, 0x2E, pString, 0, 33); - - // Prevent garbage - _mddf.password = pString[0] <= 32 ? StringHandlers.PascalToString(pString, Encoding) : ""; - _mddf.unknown2 = sector[0x4F]; - _mddf.machine_id = BigEndianBitConverter.ToUInt32(sector, 0x50); - _mddf.master_copy_id = BigEndianBitConverter.ToUInt32(sector, 0x54); - uint lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x58); - _mddf.dtvc = DateHandlers.LisaToDateTime(lisaTime); - lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x5C); - _mddf.dtcc = DateHandlers.LisaToDateTime(lisaTime); - lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x60); - _mddf.dtvb = DateHandlers.LisaToDateTime(lisaTime); - lisaTime = BigEndianBitConverter.ToUInt32(sector, 0x64); - _mddf.dtvs = DateHandlers.LisaToDateTime(lisaTime); - _mddf.unknown3 = BigEndianBitConverter.ToUInt32(sector, 0x68); - _mddf.mddf_block = BigEndianBitConverter.ToUInt32(sector, 0x6C); - _mddf.volsize_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x70); - _mddf.volsize_minus_mddf_minus_one = BigEndianBitConverter.ToUInt32(sector, 0x74); - _mddf.vol_size = BigEndianBitConverter.ToUInt32(sector, 0x78); - _mddf.blocksize = BigEndianBitConverter.ToUInt16(sector, 0x7C); - _mddf.datasize = BigEndianBitConverter.ToUInt16(sector, 0x7E); - _mddf.unknown4 = BigEndianBitConverter.ToUInt16(sector, 0x80); - _mddf.unknown5 = BigEndianBitConverter.ToUInt32(sector, 0x82); - _mddf.unknown6 = BigEndianBitConverter.ToUInt32(sector, 0x86); - _mddf.clustersize = BigEndianBitConverter.ToUInt16(sector, 0x8A); - _mddf.fs_size = BigEndianBitConverter.ToUInt32(sector, 0x8C); - _mddf.unknown7 = BigEndianBitConverter.ToUInt32(sector, 0x90); - _mddf.srec_ptr = BigEndianBitConverter.ToUInt32(sector, 0x94); - _mddf.unknown9 = BigEndianBitConverter.ToUInt16(sector, 0x98); - _mddf.srec_len = BigEndianBitConverter.ToUInt16(sector, 0x9A); - _mddf.unknown10 = BigEndianBitConverter.ToUInt32(sector, 0x9C); - _mddf.unknown11 = BigEndianBitConverter.ToUInt32(sector, 0xA0); - _mddf.unknown12 = BigEndianBitConverter.ToUInt32(sector, 0xA4); - _mddf.unknown13 = BigEndianBitConverter.ToUInt32(sector, 0xA8); - _mddf.unknown14 = BigEndianBitConverter.ToUInt32(sector, 0xAC); - _mddf.filecount = BigEndianBitConverter.ToUInt16(sector, 0xB0); - _mddf.unknown15 = BigEndianBitConverter.ToUInt32(sector, 0xB2); - _mddf.unknown16 = BigEndianBitConverter.ToUInt32(sector, 0xB6); - _mddf.freecount = BigEndianBitConverter.ToUInt32(sector, 0xBA); - _mddf.unknown17 = BigEndianBitConverter.ToUInt16(sector, 0xBE); - _mddf.unknown18 = BigEndianBitConverter.ToUInt32(sector, 0xC0); - _mddf.overmount_stamp = BigEndianBitConverter.ToUInt64(sector, 0xC4); - _mddf.serialization = BigEndianBitConverter.ToUInt32(sector, 0xCC); - _mddf.unknown19 = BigEndianBitConverter.ToUInt32(sector, 0xD0); - _mddf.unknown_timestamp = BigEndianBitConverter.ToUInt32(sector, 0xD4); - _mddf.unknown20 = BigEndianBitConverter.ToUInt32(sector, 0xD8); - _mddf.unknown21 = BigEndianBitConverter.ToUInt32(sector, 0xDC); - _mddf.unknown22 = BigEndianBitConverter.ToUInt32(sector, 0xE0); - _mddf.unknown23 = BigEndianBitConverter.ToUInt32(sector, 0xE4); - _mddf.unknown24 = BigEndianBitConverter.ToUInt32(sector, 0xE8); - _mddf.unknown25 = BigEndianBitConverter.ToUInt32(sector, 0xEC); - _mddf.unknown26 = BigEndianBitConverter.ToUInt32(sector, 0xF0); - _mddf.unknown27 = BigEndianBitConverter.ToUInt32(sector, 0xF4); - _mddf.unknown28 = BigEndianBitConverter.ToUInt32(sector, 0xF8); - _mddf.unknown29 = BigEndianBitConverter.ToUInt32(sector, 0xFC); - _mddf.unknown30 = BigEndianBitConverter.ToUInt32(sector, 0x100); - _mddf.unknown31 = BigEndianBitConverter.ToUInt32(sector, 0x104); - _mddf.unknown32 = BigEndianBitConverter.ToUInt32(sector, 0x108); - _mddf.unknown33 = BigEndianBitConverter.ToUInt32(sector, 0x10C); - _mddf.unknown34 = BigEndianBitConverter.ToUInt32(sector, 0x110); - _mddf.unknown35 = BigEndianBitConverter.ToUInt32(sector, 0x114); - _mddf.backup_volid = BigEndianBitConverter.ToUInt64(sector, 0x118); - _mddf.label_size = BigEndianBitConverter.ToUInt16(sector, 0x120); - _mddf.fs_overhead = BigEndianBitConverter.ToUInt16(sector, 0x122); - _mddf.result_scavenge = BigEndianBitConverter.ToUInt16(sector, 0x124); - _mddf.boot_code = BigEndianBitConverter.ToUInt16(sector, 0x126); - _mddf.boot_environ = BigEndianBitConverter.ToUInt16(sector, 0x6C); - _mddf.unknown36 = BigEndianBitConverter.ToUInt32(sector, 0x12A); - _mddf.unknown37 = BigEndianBitConverter.ToUInt32(sector, 0x12E); - _mddf.unknown38 = BigEndianBitConverter.ToUInt32(sector, 0x132); - _mddf.vol_sequence = BigEndianBitConverter.ToUInt16(sector, 0x136); - _mddf.vol_left_mounted = sector[0x138]; - - // Check that the MDDF is correct - if(_mddf.mddf_block != i - _volumePrefix || - _mddf.vol_size > _device.Info.Sectors || - _mddf.vol_size - 1 != _mddf.volsize_minus_one || - _mddf.vol_size - i - 1 != _mddf.volsize_minus_mddf_minus_one - _volumePrefix || - _mddf.datasize > _mddf.blocksize || - _mddf.blocksize < _device.Info.SectorSize || - _mddf.datasize != _device.Info.SectorSize) + _directoryDtcCache = new Dictionary + { { - AaruConsole.DebugWriteLine("LisaFS plugin", "Incorrect MDDF found"); - - return ErrorNumber.InvalidArgument; + DIRID_ROOT, _mddf.dtcc } + }; - // Check MDDF version - switch(_mddf.fsversion) - { - case LISA_V1: - AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v1"); + // Read the Catalog File + error = ReadCatalog(); - break; - case LISA_V2: - AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v2"); + if(error != ErrorNumber.NoError) + { + AaruConsole.DebugWriteLine("LisaFS plugin", "Cannot read Catalog File, error {0}", + error.ToString()); - break; - case LISA_V3: - AaruConsole.DebugWriteLine("LisaFS plugin", "Mounting LisaFS v3"); + _mounted = false; - break; - default: - AaruConsole.ErrorWriteLine("Cannot mount LisaFS version {0}", _mddf.fsversion.ToString()); + return error; + } - return ErrorNumber.NotSupported; - } - - // Initialize caches - _extentCache = new Dictionary(); - _systemFileCache = new Dictionary(); - _fileCache = new Dictionary(); - - //catalogCache = new Dictionary>(); - _fileSizeCache = new Dictionary(); - - _mounted = true; - - options ??= GetDefaultOptions(); - - if(options.TryGetValue("debug", out string debugString)) - bool.TryParse(debugString, out _debug); - - if(_debug) - _printedExtents = new List(); - - // Read the S-Records file - ErrorNumber error = ReadSRecords(); + // If debug, cache system files + if(_debug) + { + error = ReadSystemFile(FILEID_BOOT_SIGNED, out _); if(error != ErrorNumber.NoError) { - AaruConsole.ErrorWriteLine("Error {0} reading S-Records file.", error); - - return error; - } - - _directoryDtcCache = new Dictionary - { - { - DIRID_ROOT, _mddf.dtcc - } - }; - - // Read the Catalog File - error = ReadCatalog(); - - if(error != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("LisaFS plugin", "Cannot read Catalog File, error {0}", - error.ToString()); - + AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot blocks"); _mounted = false; return error; } - // If debug, cache system files - if(_debug) + error = ReadSystemFile(FILEID_LOADER_SIGNED, out _); + + if(error != ErrorNumber.NoError) { - error = ReadSystemFile(FILEID_BOOT_SIGNED, out _); + AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot loader"); + _mounted = false; - if(error != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot blocks"); - _mounted = false; - - return error; - } - - error = ReadSystemFile(FILEID_LOADER_SIGNED, out _); - - if(error != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read boot loader"); - _mounted = false; - - return error; - } - - error = ReadSystemFile((short)FILEID_MDDF, out _); - - if(error != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read MDDF"); - _mounted = false; - - return error; - } - - error = ReadSystemFile((short)FILEID_BITMAP, out _); - - if(error != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read volume bitmap"); - _mounted = false; - - return error; - } - - error = ReadSystemFile((short)FILEID_SRECORD, out _); - - if(error != ErrorNumber.NoError) - { - AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read S-Records file"); - _mounted = false; - - return error; - } + return error; } - // Create XML metadata for mounted filesystem - XmlFsType = new FileSystemType(); + error = ReadSystemFile((short)FILEID_MDDF, out _); - if(DateTime.Compare(_mddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) + if(error != ErrorNumber.NoError) { - XmlFsType.BackupDate = _mddf.dtvb; - XmlFsType.BackupDateSpecified = true; + AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read MDDF"); + _mounted = false; + + return error; } - XmlFsType.Clusters = _mddf.vol_size; - XmlFsType.ClusterSize = (uint)(_mddf.clustersize * _mddf.datasize); + error = ReadSystemFile((short)FILEID_BITMAP, out _); - if(DateTime.Compare(_mddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) + if(error != ErrorNumber.NoError) { - XmlFsType.CreationDate = _mddf.dtvc; - XmlFsType.CreationDateSpecified = true; + AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read volume bitmap"); + _mounted = false; + + return error; } - XmlFsType.Dirty = _mddf.vol_left_mounted != 0; - XmlFsType.Files = _mddf.filecount; - XmlFsType.FilesSpecified = true; - XmlFsType.FreeClusters = _mddf.freecount; - XmlFsType.FreeClustersSpecified = true; - XmlFsType.Type = "LisaFS"; - XmlFsType.VolumeName = _mddf.volname; - XmlFsType.VolumeSerial = $"{_mddf.volid:X16}"; + error = ReadSystemFile((short)FILEID_SRECORD, out _); - return ErrorNumber.NoError; + if(error != ErrorNumber.NoError) + { + AaruConsole.DebugWriteLine("LisaFS plugin", "Unable to read S-Records file"); + _mounted = false; + + return error; + } } - AaruConsole.DebugWriteLine("LisaFS plugin", "Not a Lisa filesystem"); + // Create XML metadata for mounted filesystem + XmlFsType = new FileSystemType(); - return ErrorNumber.NotSupported; - } - catch(Exception ex) - { - AaruConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace); - - return ErrorNumber.InOutError; - } - } - - /// - public ErrorNumber Unmount() - { - _mounted = false; - _extentCache = null; - _systemFileCache = null; - _fileCache = null; - _catalogCache = null; - _fileSizeCache = null; - _printedExtents = null; - _mddf = new MDDF(); - _volumePrefix = 0; - _devTagSize = 0; - _srecords = null; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber StatFs(out FileSystemInfo stat) - { - stat = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - stat = new FileSystemInfo - { - Blocks = _mddf.vol_size, - FilenameLength = (ushort)E_NAME, - Files = _mddf.filecount, - FreeBlocks = _mddf.freecount, - Id = + if(DateTime.Compare(_mddf.dtvb, DateHandlers.LisaToDateTime(0)) > 0) { - Serial64 = _mddf.volid, - IsLong = true - }, - PluginId = Id - }; + XmlFsType.BackupDate = _mddf.dtvb; + XmlFsType.BackupDateSpecified = true; + } - stat.FreeFiles = FILEID_MAX - stat.Files; + XmlFsType.Clusters = _mddf.vol_size; + XmlFsType.ClusterSize = (uint)(_mddf.clustersize * _mddf.datasize); - switch(_mddf.fsversion) - { - case LISA_V1: - stat.Type = "LisaFS v1"; + if(DateTime.Compare(_mddf.dtvc, DateHandlers.LisaToDateTime(0)) > 0) + { + XmlFsType.CreationDate = _mddf.dtvc; + XmlFsType.CreationDateSpecified = true; + } - break; - case LISA_V2: - stat.Type = "LisaFS v2"; + XmlFsType.Dirty = _mddf.vol_left_mounted != 0; + XmlFsType.Files = _mddf.filecount; + XmlFsType.FilesSpecified = true; + XmlFsType.FreeClusters = _mddf.freecount; + XmlFsType.FreeClustersSpecified = true; + XmlFsType.Type = "LisaFS"; + XmlFsType.VolumeName = _mddf.volname; + XmlFsType.VolumeSerial = $"{_mddf.volid:X16}"; - break; - case LISA_V3: - stat.Type = "LisaFS v3"; - - break; + return ErrorNumber.NoError; } - return ErrorNumber.NoError; + AaruConsole.DebugWriteLine("LisaFS plugin", "Not a Lisa filesystem"); + + return ErrorNumber.NotSupported; + } + catch(Exception ex) + { + AaruConsole.ErrorWriteLine("Exception {0}, {1}, {2}", ex.Message, ex.InnerException, ex.StackTrace); + + return ErrorNumber.InOutError; } } + + /// + public ErrorNumber Unmount() + { + _mounted = false; + _extentCache = null; + _systemFileCache = null; + _fileCache = null; + _catalogCache = null; + _fileSizeCache = null; + _printedExtents = null; + _mddf = new MDDF(); + _volumePrefix = 0; + _devTagSize = 0; + _srecords = null; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + stat = new FileSystemInfo + { + Blocks = _mddf.vol_size, + FilenameLength = (ushort)E_NAME, + Files = _mddf.filecount, + FreeBlocks = _mddf.freecount, + Id = + { + Serial64 = _mddf.volid, + IsLong = true + }, + PluginId = Id + }; + + stat.FreeFiles = FILEID_MAX - stat.Files; + + switch(_mddf.fsversion) + { + case LISA_V1: + stat.Type = "LisaFS v1"; + + break; + case LISA_V2: + stat.Type = "LisaFS v2"; + + break; + case LISA_V3: + stat.Type = "LisaFS v3"; + + break; + } + + return ErrorNumber.NoError; + } } \ No newline at end of file diff --git a/Aaru.Filesystems/LisaFS/Xattr.cs b/Aaru.Filesystems/LisaFS/Xattr.cs index c173b9b88..fd46350e6 100644 --- a/Aaru.Filesystems/LisaFS/Xattr.cs +++ b/Aaru.Filesystems/LisaFS/Xattr.cs @@ -38,179 +38,178 @@ using Aaru.CommonTypes.Enums; using Aaru.Decoders; using Aaru.Helpers; -namespace Aaru.Filesystems.LisaFS +namespace Aaru.Filesystems.LisaFS; + +public sealed partial class LisaFS { - public sealed partial class LisaFS + /// + public ErrorNumber ListXAttr(string path, out List xattrs) { - /// - public ErrorNumber ListXAttr(string path, out List xattrs) + xattrs = null; + ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); + + if(error != ErrorNumber.NoError) + return error; + + return isDir ? ErrorNumber.InvalidArgument : ListXAttr(fileId, out xattrs); + } + + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) + { + ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); + + if(error != ErrorNumber.NoError) + return error; + + return isDir ? ErrorNumber.InvalidArgument : GetXattr(fileId, xattr, out buf); + } + + /// Lists special Apple Lisa filesystem features as extended attributes + /// Error number. + /// File identifier. + /// Extended attributes. + ErrorNumber ListXAttr(short fileId, out List xattrs) + { + xattrs = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + // System files + if(fileId < 4) { - xattrs = null; - ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); + if(!_debug || + fileId == 0) + return ErrorNumber.InvalidArgument; - if(error != ErrorNumber.NoError) - return error; + xattrs = new List(); - return isDir ? ErrorNumber.InvalidArgument : ListXAttr(fileId, out xattrs); - } - - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) - { - ErrorNumber error = LookupFileId(path, out short fileId, out bool isDir); - - if(error != ErrorNumber.NoError) - return error; - - return isDir ? ErrorNumber.InvalidArgument : GetXattr(fileId, xattr, out buf); - } - - /// Lists special Apple Lisa filesystem features as extended attributes - /// Error number. - /// File identifier. - /// Extended attributes. - ErrorNumber ListXAttr(short fileId, out List xattrs) - { - xattrs = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - // System files - if(fileId < 4) + // Only MDDF contains an extended attributes + if(fileId == FILEID_MDDF) { - if(!_debug || - fileId == 0) - return ErrorNumber.InvalidArgument; + byte[] buf = Encoding.ASCII.GetBytes(_mddf.password); - xattrs = new List(); - - // Only MDDF contains an extended attributes - if(fileId == FILEID_MDDF) - { - byte[] buf = Encoding.ASCII.GetBytes(_mddf.password); - - // If the MDDF contains a password, show it - if(buf.Length > 0) - xattrs.Add("com.apple.lisa.password"); - } - } - else - { - // Search for the file - ErrorNumber error = ReadExtentsFile(fileId, out ExtentFile file); - - if(error != ErrorNumber.NoError) - return error; - - xattrs = new List(); - - // Password field is never emptied, check if valid - if(file.password_valid > 0) + // If the MDDF contains a password, show it + if(buf.Length > 0) xattrs.Add("com.apple.lisa.password"); - - // Check for a valid copy-protection serial number - if(file.serial > 0) - xattrs.Add("com.apple.lisa.serial"); - - // Check if the label contains something or is empty - if(!ArrayHelpers.ArrayIsNullOrEmpty(file.LisaInfo)) - xattrs.Add("com.apple.lisa.label"); } - - // On debug mode allow sector tags to be accessed as an xattr - if(_debug) - xattrs.Add("com.apple.lisa.tags"); - - xattrs.Sort(); - - return ErrorNumber.NoError; } - - /// Lists special Apple Lisa filesystem features as extended attributes - /// Error number. - /// File identifier. - /// Extended attribute name. - /// Buffer where the extended attribute will be stored. - ErrorNumber GetXattr(short fileId, string xattr, out byte[] buf) + else { - buf = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - // System files - if(fileId < 4) - { - if(!_debug || - fileId == 0) - return ErrorNumber.InvalidArgument; - - // Only MDDF contains an extended attributes - if(fileId == FILEID_MDDF) - if(xattr == "com.apple.lisa.password") - { - buf = Encoding.ASCII.GetBytes(_mddf.password); - - return ErrorNumber.NoError; - } - - // But on debug mode even system files contain tags - if(_debug && xattr == "com.apple.lisa.tags") - return ReadSystemFile(fileId, out buf, true); - - return ErrorNumber.NoSuchExtendedAttribute; - } - // Search for the file ErrorNumber error = ReadExtentsFile(fileId, out ExtentFile file); if(error != ErrorNumber.NoError) return error; - switch(xattr) - { - case "com.apple.lisa.password" when file.password_valid > 0: - buf = new byte[8]; - Array.Copy(file.password, 0, buf, 0, 8); + xattrs = new List(); + + // Password field is never emptied, check if valid + if(file.password_valid > 0) + xattrs.Add("com.apple.lisa.password"); + + // Check for a valid copy-protection serial number + if(file.serial > 0) + xattrs.Add("com.apple.lisa.serial"); + + // Check if the label contains something or is empty + if(!ArrayHelpers.ArrayIsNullOrEmpty(file.LisaInfo)) + xattrs.Add("com.apple.lisa.label"); + } + + // On debug mode allow sector tags to be accessed as an xattr + if(_debug) + xattrs.Add("com.apple.lisa.tags"); + + xattrs.Sort(); + + return ErrorNumber.NoError; + } + + /// Lists special Apple Lisa filesystem features as extended attributes + /// Error number. + /// File identifier. + /// Extended attribute name. + /// Buffer where the extended attribute will be stored. + ErrorNumber GetXattr(short fileId, string xattr, out byte[] buf) + { + buf = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + // System files + if(fileId < 4) + { + if(!_debug || + fileId == 0) + return ErrorNumber.InvalidArgument; + + // Only MDDF contains an extended attributes + if(fileId == FILEID_MDDF) + if(xattr == "com.apple.lisa.password") + { + buf = Encoding.ASCII.GetBytes(_mddf.password); return ErrorNumber.NoError; - case "com.apple.lisa.serial" when file.serial > 0: - buf = Encoding.ASCII.GetBytes(file.serial.ToString()); - - return ErrorNumber.NoError; - } - - if(!ArrayHelpers.ArrayIsNullOrEmpty(file.LisaInfo) && - xattr == "com.apple.lisa.label") - { - buf = new byte[128]; - Array.Copy(file.LisaInfo, 0, buf, 0, 128); - - return ErrorNumber.NoError; - } + } + // But on debug mode even system files contain tags if(_debug && xattr == "com.apple.lisa.tags") - return ReadFile(fileId, out buf, true); + return ReadSystemFile(fileId, out buf, true); return ErrorNumber.NoSuchExtendedAttribute; } - /// Decodes a sector tag. Not tested with 24-byte tags. - /// Error number. - /// Sector tag. - /// Decoded sector tag. - static ErrorNumber DecodeTag(byte[] tag, out LisaTag.PriamTag decoded) + // Search for the file + ErrorNumber error = ReadExtentsFile(fileId, out ExtentFile file); + + if(error != ErrorNumber.NoError) + return error; + + switch(xattr) { - decoded = new LisaTag.PriamTag(); - LisaTag.PriamTag? pmTag = LisaTag.DecodeTag(tag); + case "com.apple.lisa.password" when file.password_valid > 0: + buf = new byte[8]; + Array.Copy(file.password, 0, buf, 0, 8); - if(!pmTag.HasValue) - return ErrorNumber.InvalidArgument; + return ErrorNumber.NoError; + case "com.apple.lisa.serial" when file.serial > 0: + buf = Encoding.ASCII.GetBytes(file.serial.ToString()); - decoded = pmTag.Value; + return ErrorNumber.NoError; + } + + if(!ArrayHelpers.ArrayIsNullOrEmpty(file.LisaInfo) && + xattr == "com.apple.lisa.label") + { + buf = new byte[128]; + Array.Copy(file.LisaInfo, 0, buf, 0, 128); return ErrorNumber.NoError; } + + if(_debug && xattr == "com.apple.lisa.tags") + return ReadFile(fileId, out buf, true); + + return ErrorNumber.NoSuchExtendedAttribute; + } + + /// Decodes a sector tag. Not tested with 24-byte tags. + /// Error number. + /// Sector tag. + /// Decoded sector tag. + static ErrorNumber DecodeTag(byte[] tag, out LisaTag.PriamTag decoded) + { + decoded = new LisaTag.PriamTag(); + LisaTag.PriamTag? pmTag = LisaTag.DecodeTag(tag); + + if(!pmTag.HasValue) + return ErrorNumber.InvalidArgument; + + decoded = pmTag.Value; + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Locus.cs b/Aaru.Filesystems/Locus.cs index 659b07ce8..4dacdef44 100644 --- a/Aaru.Filesystems/Locus.cs +++ b/Aaru.Filesystems/Locus.cs @@ -66,361 +66,360 @@ using time_t = System.Int32; // ReSharper disable UnusedMember.Local // ReSharper disable UnusedType.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Locus filesystem +public sealed class Locus : IFilesystem { + const int NICINOD = 325; + const int NICFREE = 600; + const int OLDNICINOD = 700; + const int OLDNICFREE = 500; + + const uint LOCUS_MAGIC = 0xFFEEDDCD; + const uint LOCUS_CIGAM = 0xCDDDEEFF; + const uint LOCUS_MAGIC_OLD = 0xFFEEDDCC; + const uint LOCUS_CIGAM_OLD = 0xCCDDEEFF; + /// - /// Implements detection of the Locus filesystem - public sealed class Locus : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Locus Filesystem Plugin"; + /// + public Guid Id => new("1A70B30A-437D-479A-88E1-D0C9C1797FF4"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const int NICINOD = 325; - const int NICFREE = 600; - const int OLDNICINOD = 700; - const int OLDNICFREE = 500; + if(imagePlugin.Info.SectorSize < 512) + return false; - const uint LOCUS_MAGIC = 0xFFEEDDCD; - const uint LOCUS_CIGAM = 0xCDDDEEFF; - const uint LOCUS_MAGIC_OLD = 0xFFEEDDCC; - const uint LOCUS_CIGAM_OLD = 0xCCDDEEFF; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Locus Filesystem Plugin"; - /// - public Guid Id => new("1A70B30A-437D-479A-88E1-D0C9C1797FF4"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + for(ulong location = 0; location <= 8; location++) { - if(imagePlugin.Info.SectorSize < 512) + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + if(partition.Start + location + sbSize >= imagePlugin.Info.Sectors) + break; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) return false; - for(ulong location = 0; location <= 8; location++) - { - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + if(sector.Length < Marshal.SizeOf()) + return false; - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; + Superblock locusSb = Marshal.ByteArrayToStructureLittleEndian(sector); - if(partition.Start + location + sbSize >= imagePlugin.Info.Sectors) - break; + AaruConsole.DebugWriteLine("Locus plugin", "magic at {1} = 0x{0:X8}", locusSb.s_magic, location); - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - Superblock locusSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - AaruConsole.DebugWriteLine("Locus plugin", "magic at {1} = 0x{0:X8}", locusSb.s_magic, location); - - if(locusSb.s_magic == LOCUS_MAGIC || - locusSb.s_magic == LOCUS_CIGAM || - locusSb.s_magic == LOCUS_MAGIC_OLD || - locusSb.s_magic == LOCUS_CIGAM_OLD) - return true; - } - - return false; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - if(imagePlugin.Info.SectorSize < 512) - return; - - var locusSb = new Superblock(); - byte[] sector = null; - - for(ulong location = 0; location <= 8; location++) - { - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out sector); - - if(errno != ErrorNumber.NoError) - continue; - - if(sector.Length < Marshal.SizeOf()) - return; - - locusSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - if(locusSb.s_magic == LOCUS_MAGIC || - locusSb.s_magic == LOCUS_CIGAM || - locusSb.s_magic == LOCUS_MAGIC_OLD || - locusSb.s_magic == LOCUS_CIGAM_OLD) - break; - } - - // We don't care about old version for information - if(locusSb.s_magic != LOCUS_MAGIC && - locusSb.s_magic != LOCUS_CIGAM && - locusSb.s_magic != LOCUS_MAGIC_OLD && - locusSb.s_magic != LOCUS_CIGAM_OLD) - return; - - // Numerical arrays are not important for information so no need to swap them - if(locusSb.s_magic == LOCUS_CIGAM || + if(locusSb.s_magic == LOCUS_MAGIC || + locusSb.s_magic == LOCUS_CIGAM || + locusSb.s_magic == LOCUS_MAGIC_OLD || locusSb.s_magic == LOCUS_CIGAM_OLD) - { - locusSb = Marshal.ByteArrayToStructureBigEndian(sector); - locusSb.s_flags = (Flags)Swapping.Swap((ushort)locusSb.s_flags); - } - - var sb = new StringBuilder(); - - sb.AppendLine(locusSb.s_magic == LOCUS_MAGIC_OLD ? "Locus filesystem (old)" : "Locus filesystem"); - - int blockSize = locusSb.s_version == Version.SB_SB4096 ? 4096 : 1024; - - // ReSharper disable once InconsistentNaming - string s_fsmnt = StringHandlers.CToString(locusSb.s_fsmnt, Encoding); - - // ReSharper disable once InconsistentNaming - string s_fpack = StringHandlers.CToString(locusSb.s_fpack, Encoding); - - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_magic = 0x{0:X8}", locusSb.s_magic); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_gfs = {0}", locusSb.s_gfs); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fsize = {0}", locusSb.s_fsize); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_lwm = {0}", locusSb.s_lwm); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_hwm = {0}", locusSb.s_hwm); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_llst = {0}", locusSb.s_llst); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fstore = {0}", locusSb.s_fstore); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_time = {0}", locusSb.s_time); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_tfree = {0}", locusSb.s_tfree); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_isize = {0}", locusSb.s_isize); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_nfree = {0}", locusSb.s_nfree); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_flags = {0}", locusSb.s_flags); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_tinode = {0}", locusSb.s_tinode); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_lasti = {0}", locusSb.s_lasti); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_nbehind = {0}", locusSb.s_nbehind); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_gfspack = {0}", locusSb.s_gfspack); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_ninode = {0}", locusSb.s_ninode); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_flock = {0}", locusSb.s_flock); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_ilock = {0}", locusSb.s_ilock); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fmod = {0}", locusSb.s_fmod); - AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_version = {0}", locusSb.s_version); - - sb.AppendFormat("Superblock last modified on {0}", DateHandlers.UnixToDateTime(locusSb.s_time)). - AppendLine(); - - sb.AppendFormat("Volume has {0} blocks of {1} bytes each (total {2} bytes)", locusSb.s_fsize, blockSize, - locusSb.s_fsize * blockSize).AppendLine(); - - sb.AppendFormat("{0} blocks free ({1} bytes)", locusSb.s_tfree, locusSb.s_tfree * blockSize).AppendLine(); - sb.AppendFormat("I-node list uses {0} blocks", locusSb.s_isize).AppendLine(); - sb.AppendFormat("{0} free inodes", locusSb.s_tinode).AppendLine(); - sb.AppendFormat("Next free inode search will start at inode {0}", locusSb.s_lasti).AppendLine(); - - sb.AppendFormat("There are an estimate of {0} free inodes before next search start", locusSb.s_nbehind). - AppendLine(); - - if(locusSb.s_flags.HasFlag(Flags.SB_RDONLY)) - sb.AppendLine("Read-only volume"); - - if(locusSb.s_flags.HasFlag(Flags.SB_CLEAN)) - sb.AppendLine("Clean volume"); - - if(locusSb.s_flags.HasFlag(Flags.SB_DIRTY)) - sb.AppendLine("Dirty volume"); - - if(locusSb.s_flags.HasFlag(Flags.SB_RMV)) - sb.AppendLine("Removable volume"); - - if(locusSb.s_flags.HasFlag(Flags.SB_PRIMPACK)) - sb.AppendLine("This is the primary pack"); - - if(locusSb.s_flags.HasFlag(Flags.SB_REPLTYPE)) - sb.AppendLine("Replicated volume"); - - if(locusSb.s_flags.HasFlag(Flags.SB_USER)) - sb.AppendLine("User replicated volume"); - - if(locusSb.s_flags.HasFlag(Flags.SB_BACKBONE)) - sb.AppendLine("Backbone volume"); - - if(locusSb.s_flags.HasFlag(Flags.SB_NFS)) - sb.AppendLine("NFS volume"); - - if(locusSb.s_flags.HasFlag(Flags.SB_BYHAND)) - sb.AppendLine("Volume inhibits automatic fsck"); - - if(locusSb.s_flags.HasFlag(Flags.SB_NOSUID)) - sb.AppendLine("Set-uid/set-gid is disabled"); - - if(locusSb.s_flags.HasFlag(Flags.SB_SYNCW)) - sb.AppendLine("Volume uses synchronous writes"); - - sb.AppendFormat("Volume label: {0}", s_fsmnt).AppendLine(); - sb.AppendFormat("Physical volume name: {0}", s_fpack).AppendLine(); - sb.AppendFormat("Global File System number: {0}", locusSb.s_gfs).AppendLine(); - sb.AppendFormat("Global File System pack number {0}", locusSb.s_gfspack).AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "Locus filesystem", - ClusterSize = (uint)blockSize, - Clusters = (ulong)locusSb.s_fsize, - - // Sometimes it uses one, or the other. Use the bigger - VolumeName = string.IsNullOrEmpty(s_fsmnt) ? s_fpack : s_fsmnt, - ModificationDate = DateHandlers.UnixToDateTime(locusSb.s_time), - ModificationDateSpecified = true, - Dirty = !locusSb.s_flags.HasFlag(Flags.SB_CLEAN) || locusSb.s_flags.HasFlag(Flags.SB_DIRTY), - FreeClusters = (ulong)locusSb.s_tfree, - FreeClustersSpecified = true - }; + return true; } - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle"), - StructLayout(LayoutKind.Sequential, Pack = 1)] - struct Superblock + return false; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + var locusSb = new Superblock(); + byte[] sector = null; + + for(ulong location = 0; location <= 8; location++) { - public readonly uint s_magic; /* identifies this as a locus filesystem */ - /* defined as a constant below */ - public readonly gfs_t s_gfs; /* global filesystem number */ - public readonly daddr_t s_fsize; /* size in blocks of entire volume */ - /* several ints for replicated filesystems */ - public readonly commitcnt_t s_lwm; /* all prior commits propagated */ - public readonly commitcnt_t s_hwm; /* highest commit propagated */ - /* oldest committed version in the list. - * llst mod NCMTLST is the offset of commit #llst in the list, - * which wraps around from there. - */ - public readonly commitcnt_t s_llst; - public readonly fstore_t s_fstore; /* filesystem storage bit mask; if the + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out sector); + + if(errno != ErrorNumber.NoError) + continue; + + if(sector.Length < Marshal.SizeOf()) + return; + + locusSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(locusSb.s_magic == LOCUS_MAGIC || + locusSb.s_magic == LOCUS_CIGAM || + locusSb.s_magic == LOCUS_MAGIC_OLD || + locusSb.s_magic == LOCUS_CIGAM_OLD) + break; + } + + // We don't care about old version for information + if(locusSb.s_magic != LOCUS_MAGIC && + locusSb.s_magic != LOCUS_CIGAM && + locusSb.s_magic != LOCUS_MAGIC_OLD && + locusSb.s_magic != LOCUS_CIGAM_OLD) + return; + + // Numerical arrays are not important for information so no need to swap them + if(locusSb.s_magic == LOCUS_CIGAM || + locusSb.s_magic == LOCUS_CIGAM_OLD) + { + locusSb = Marshal.ByteArrayToStructureBigEndian(sector); + locusSb.s_flags = (Flags)Swapping.Swap((ushort)locusSb.s_flags); + } + + var sb = new StringBuilder(); + + sb.AppendLine(locusSb.s_magic == LOCUS_MAGIC_OLD ? "Locus filesystem (old)" : "Locus filesystem"); + + int blockSize = locusSb.s_version == Version.SB_SB4096 ? 4096 : 1024; + + // ReSharper disable once InconsistentNaming + string s_fsmnt = StringHandlers.CToString(locusSb.s_fsmnt, Encoding); + + // ReSharper disable once InconsistentNaming + string s_fpack = StringHandlers.CToString(locusSb.s_fpack, Encoding); + + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_magic = 0x{0:X8}", locusSb.s_magic); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_gfs = {0}", locusSb.s_gfs); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fsize = {0}", locusSb.s_fsize); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_lwm = {0}", locusSb.s_lwm); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_hwm = {0}", locusSb.s_hwm); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_llst = {0}", locusSb.s_llst); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fstore = {0}", locusSb.s_fstore); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_time = {0}", locusSb.s_time); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_tfree = {0}", locusSb.s_tfree); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_isize = {0}", locusSb.s_isize); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_nfree = {0}", locusSb.s_nfree); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_flags = {0}", locusSb.s_flags); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_tinode = {0}", locusSb.s_tinode); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_lasti = {0}", locusSb.s_lasti); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_nbehind = {0}", locusSb.s_nbehind); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_gfspack = {0}", locusSb.s_gfspack); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_ninode = {0}", locusSb.s_ninode); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_flock = {0}", locusSb.s_flock); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_ilock = {0}", locusSb.s_ilock); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_fmod = {0}", locusSb.s_fmod); + AaruConsole.DebugWriteLine("Locus plugin", "LocusSb.s_version = {0}", locusSb.s_version); + + sb.AppendFormat("Superblock last modified on {0}", DateHandlers.UnixToDateTime(locusSb.s_time)). + AppendLine(); + + sb.AppendFormat("Volume has {0} blocks of {1} bytes each (total {2} bytes)", locusSb.s_fsize, blockSize, + locusSb.s_fsize * blockSize).AppendLine(); + + sb.AppendFormat("{0} blocks free ({1} bytes)", locusSb.s_tfree, locusSb.s_tfree * blockSize).AppendLine(); + sb.AppendFormat("I-node list uses {0} blocks", locusSb.s_isize).AppendLine(); + sb.AppendFormat("{0} free inodes", locusSb.s_tinode).AppendLine(); + sb.AppendFormat("Next free inode search will start at inode {0}", locusSb.s_lasti).AppendLine(); + + sb.AppendFormat("There are an estimate of {0} free inodes before next search start", locusSb.s_nbehind). + AppendLine(); + + if(locusSb.s_flags.HasFlag(Flags.SB_RDONLY)) + sb.AppendLine("Read-only volume"); + + if(locusSb.s_flags.HasFlag(Flags.SB_CLEAN)) + sb.AppendLine("Clean volume"); + + if(locusSb.s_flags.HasFlag(Flags.SB_DIRTY)) + sb.AppendLine("Dirty volume"); + + if(locusSb.s_flags.HasFlag(Flags.SB_RMV)) + sb.AppendLine("Removable volume"); + + if(locusSb.s_flags.HasFlag(Flags.SB_PRIMPACK)) + sb.AppendLine("This is the primary pack"); + + if(locusSb.s_flags.HasFlag(Flags.SB_REPLTYPE)) + sb.AppendLine("Replicated volume"); + + if(locusSb.s_flags.HasFlag(Flags.SB_USER)) + sb.AppendLine("User replicated volume"); + + if(locusSb.s_flags.HasFlag(Flags.SB_BACKBONE)) + sb.AppendLine("Backbone volume"); + + if(locusSb.s_flags.HasFlag(Flags.SB_NFS)) + sb.AppendLine("NFS volume"); + + if(locusSb.s_flags.HasFlag(Flags.SB_BYHAND)) + sb.AppendLine("Volume inhibits automatic fsck"); + + if(locusSb.s_flags.HasFlag(Flags.SB_NOSUID)) + sb.AppendLine("Set-uid/set-gid is disabled"); + + if(locusSb.s_flags.HasFlag(Flags.SB_SYNCW)) + sb.AppendLine("Volume uses synchronous writes"); + + sb.AppendFormat("Volume label: {0}", s_fsmnt).AppendLine(); + sb.AppendFormat("Physical volume name: {0}", s_fpack).AppendLine(); + sb.AppendFormat("Global File System number: {0}", locusSb.s_gfs).AppendLine(); + sb.AppendFormat("Global File System pack number {0}", locusSb.s_gfspack).AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType + { + Type = "Locus filesystem", + ClusterSize = (uint)blockSize, + Clusters = (ulong)locusSb.s_fsize, + + // Sometimes it uses one, or the other. Use the bigger + VolumeName = string.IsNullOrEmpty(s_fsmnt) ? s_fpack : s_fsmnt, + ModificationDate = DateHandlers.UnixToDateTime(locusSb.s_time), + ModificationDateSpecified = true, + Dirty = !locusSb.s_flags.HasFlag(Flags.SB_CLEAN) || locusSb.s_flags.HasFlag(Flags.SB_DIRTY), + FreeClusters = (ulong)locusSb.s_tfree, + FreeClustersSpecified = true + }; + } + + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle"), + StructLayout(LayoutKind.Sequential, Pack = 1)] + struct Superblock + { + public readonly uint s_magic; /* identifies this as a locus filesystem */ + /* defined as a constant below */ + public readonly gfs_t s_gfs; /* global filesystem number */ + public readonly daddr_t s_fsize; /* size in blocks of entire volume */ + /* several ints for replicated filesystems */ + public readonly commitcnt_t s_lwm; /* all prior commits propagated */ + public readonly commitcnt_t s_hwm; /* highest commit propagated */ + /* oldest committed version in the list. + * llst mod NCMTLST is the offset of commit #llst in the list, + * which wraps around from there. + */ + public readonly commitcnt_t s_llst; + public readonly fstore_t s_fstore; /* filesystem storage bit mask; if the filsys is replicated and this is not a primary or backbone copy, this bit mask determines which files are stored */ - public readonly time_t s_time; /* last super block update */ - public readonly daddr_t s_tfree; /* total free blocks*/ + public readonly time_t s_time; /* last super block update */ + public readonly daddr_t s_tfree; /* total free blocks*/ - public readonly ino_t s_isize; /* size in blocks of i-list */ - public readonly short s_nfree; /* number of addresses in s_free */ - public Flags s_flags; /* filsys flags, defined below */ - public readonly ino_t s_tinode; /* total free inodes */ - public readonly ino_t s_lasti; /* start place for circular search */ - public readonly ino_t s_nbehind; /* est # free inodes before s_lasti */ - public readonly pckno_t s_gfspack; /* global filesystem pack number */ - public readonly short s_ninode; /* number of i-nodes in s_inode */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly short[] s_dinfo; /* interleave stuff */ + public readonly ino_t s_isize; /* size in blocks of i-list */ + public readonly short s_nfree; /* number of addresses in s_free */ + public Flags s_flags; /* filsys flags, defined below */ + public readonly ino_t s_tinode; /* total free inodes */ + public readonly ino_t s_lasti; /* start place for circular search */ + public readonly ino_t s_nbehind; /* est # free inodes before s_lasti */ + public readonly pckno_t s_gfspack; /* global filesystem pack number */ + public readonly short s_ninode; /* number of i-nodes in s_inode */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly short[] s_dinfo; /* interleave stuff */ - //#define s_m s_dinfo[0] - //#define s_skip s_dinfo[0] /* AIX defines */ - //#define s_n s_dinfo[1] - //#define s_cyl s_dinfo[1] /* AIX defines */ - public readonly byte s_flock; /* lock during free list manipulation */ - public readonly byte s_ilock; /* lock during i-list manipulation */ - public readonly byte s_fmod; /* super block modified flag */ - public readonly Version s_version; /* version of the data format in fs. */ - /* defined below. */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] s_fsmnt; /* name of this file system */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] s_fpack; /* name of this physical volume */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NICINOD)] - public readonly ino_t[] s_inode; /* free i-node list */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NICFREE)] - public readonly daddr_t[] su_free; /* free block list for non-replicated filsys */ - public readonly byte s_byteorder; /* byte order of integers */ - } + //#define s_m s_dinfo[0] + //#define s_skip s_dinfo[0] /* AIX defines */ + //#define s_n s_dinfo[1] + //#define s_cyl s_dinfo[1] /* AIX defines */ + public readonly byte s_flock; /* lock during free list manipulation */ + public readonly byte s_ilock; /* lock during i-list manipulation */ + public readonly byte s_fmod; /* super block modified flag */ + public readonly Version s_version; /* version of the data format in fs. */ + /* defined below. */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] s_fsmnt; /* name of this file system */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] s_fpack; /* name of this physical volume */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NICINOD)] + public readonly ino_t[] s_inode; /* free i-node list */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NICFREE)] + public readonly daddr_t[] su_free; /* free block list for non-replicated filsys */ + public readonly byte s_byteorder; /* byte order of integers */ + } - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle"), - StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct OldSuperblock - { - public readonly uint s_magic; /* identifies this as a locus filesystem */ - /* defined as a constant below */ - public readonly gfs_t s_gfs; /* global filesystem number */ - public readonly daddr_t s_fsize; /* size in blocks of entire volume */ - /* several ints for replicated filsystems */ - public readonly commitcnt_t s_lwm; /* all prior commits propagated */ - public readonly commitcnt_t s_hwm; /* highest commit propagated */ - /* oldest committed version in the list. - * llst mod NCMTLST is the offset of commit #llst in the list, - * which wraps around from there. - */ - public readonly commitcnt_t s_llst; - public readonly fstore_t s_fstore; /* filesystem storage bit mask; if the + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle"), + StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct OldSuperblock + { + public readonly uint s_magic; /* identifies this as a locus filesystem */ + /* defined as a constant below */ + public readonly gfs_t s_gfs; /* global filesystem number */ + public readonly daddr_t s_fsize; /* size in blocks of entire volume */ + /* several ints for replicated filsystems */ + public readonly commitcnt_t s_lwm; /* all prior commits propagated */ + public readonly commitcnt_t s_hwm; /* highest commit propagated */ + /* oldest committed version in the list. + * llst mod NCMTLST is the offset of commit #llst in the list, + * which wraps around from there. + */ + public readonly commitcnt_t s_llst; + public readonly fstore_t s_fstore; /* filesystem storage bit mask; if the filsys is replicated and this is not a primary or backbone copy, this bit mask determines which files are stored */ - public readonly time_t s_time; /* last super block update */ - public readonly daddr_t s_tfree; /* total free blocks*/ + public readonly time_t s_time; /* last super block update */ + public readonly daddr_t s_tfree; /* total free blocks*/ - public readonly ino_t s_isize; /* size in blocks of i-list */ - public readonly short s_nfree; /* number of addresses in s_free */ - public readonly Flags s_flags; /* filsys flags, defined below */ - public readonly ino_t s_tinode; /* total free inodes */ - public readonly ino_t s_lasti; /* start place for circular search */ - public readonly ino_t s_nbehind; /* est # free inodes before s_lasti */ - public readonly pckno_t s_gfspack; /* global filesystem pack number */ - public readonly short s_ninode; /* number of i-nodes in s_inode */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly short[] s_dinfo; /* interleave stuff */ + public readonly ino_t s_isize; /* size in blocks of i-list */ + public readonly short s_nfree; /* number of addresses in s_free */ + public readonly Flags s_flags; /* filsys flags, defined below */ + public readonly ino_t s_tinode; /* total free inodes */ + public readonly ino_t s_lasti; /* start place for circular search */ + public readonly ino_t s_nbehind; /* est # free inodes before s_lasti */ + public readonly pckno_t s_gfspack; /* global filesystem pack number */ + public readonly short s_ninode; /* number of i-nodes in s_inode */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly short[] s_dinfo; /* interleave stuff */ - //#define s_m s_dinfo[0] - //#define s_skip s_dinfo[0] /* AIX defines */ - //#define s_n s_dinfo[1] - //#define s_cyl s_dinfo[1] /* AIX defines */ - public readonly byte s_flock; /* lock during free list manipulation */ - public readonly byte s_ilock; /* lock during i-list manipulation */ - public readonly byte s_fmod; /* super block modified flag */ - public readonly Version s_version; /* version of the data format in fs. */ - /* defined below. */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] s_fsmnt; /* name of this file system */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] s_fpack; /* name of this physical volume */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = OLDNICINOD)] - public readonly ino_t[] s_inode; /* free i-node list */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = OLDNICFREE)] - public readonly daddr_t[] su_free; /* free block list for non-replicated filsys */ - public readonly byte s_byteorder; /* byte order of integers */ - } + //#define s_m s_dinfo[0] + //#define s_skip s_dinfo[0] /* AIX defines */ + //#define s_n s_dinfo[1] + //#define s_cyl s_dinfo[1] /* AIX defines */ + public readonly byte s_flock; /* lock during free list manipulation */ + public readonly byte s_ilock; /* lock during i-list manipulation */ + public readonly byte s_fmod; /* super block modified flag */ + public readonly Version s_version; /* version of the data format in fs. */ + /* defined below. */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] s_fsmnt; /* name of this file system */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] s_fpack; /* name of this physical volume */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = OLDNICINOD)] + public readonly ino_t[] s_inode; /* free i-node list */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = OLDNICFREE)] + public readonly daddr_t[] su_free; /* free block list for non-replicated filsys */ + public readonly byte s_byteorder; /* byte order of integers */ + } - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle"), - Flags] - enum Flags : ushort - { - SB_RDONLY = 0x1, /* no writes on filesystem */ SB_CLEAN = 0x2, /* fs unmounted cleanly (or checks run) */ - SB_DIRTY = 0x4, /* fs mounted without CLEAN bit set */ SB_RMV = 0x8, /* fs is a removable file system */ - SB_PRIMPACK = 0x10, /* This is the primary pack of the filesystem */ - SB_REPLTYPE = 0x20, /* This is a replicated type filesystem. */ - SB_USER = 0x40, /* This is a "user" replicated filesystem. */ - SB_BACKBONE = 0x80, /* backbone pack ; complete copy of primary pack but not modifiable */ - SB_NFS = 0x100, /* This is a NFS type filesystem */ - SB_BYHAND = 0x200, /* Inhibits automatic fscks on a mangled file system */ - SB_NOSUID = 0x400, /* Set-uid/Set-gid is disabled */ SB_SYNCW = 0x800 /* Synchronous Write */ - } + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle"), + Flags] + enum Flags : ushort + { + SB_RDONLY = 0x1, /* no writes on filesystem */ SB_CLEAN = 0x2, /* fs unmounted cleanly (or checks run) */ + SB_DIRTY = 0x4, /* fs mounted without CLEAN bit set */ SB_RMV = 0x8, /* fs is a removable file system */ + SB_PRIMPACK = 0x10, /* This is the primary pack of the filesystem */ + SB_REPLTYPE = 0x20, /* This is a replicated type filesystem. */ + SB_USER = 0x40, /* This is a "user" replicated filesystem. */ + SB_BACKBONE = 0x80, /* backbone pack ; complete copy of primary pack but not modifiable */ + SB_NFS = 0x100, /* This is a NFS type filesystem */ + SB_BYHAND = 0x200, /* Inhibits automatic fscks on a mangled file system */ + SB_NOSUID = 0x400, /* Set-uid/Set-gid is disabled */ SB_SYNCW = 0x800 /* Synchronous Write */ + } - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle"), - Flags] - enum Version : byte - { - SB_SB4096 = 1, /* smallblock filesys with 4096 byte blocks */ SB_B1024 = 2, /* 1024 byte block filesystem */ - NUMSCANDEV = 5 /* Used by scangfs(), refed in space.h */ - } + [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle"), + Flags] + enum Version : byte + { + SB_SB4096 = 1, /* smallblock filesys with 4096 byte blocks */ SB_B1024 = 2, /* 1024 byte block filesystem */ + NUMSCANDEV = 5 /* Used by scangfs(), refed in space.h */ } } \ No newline at end of file diff --git a/Aaru.Filesystems/MicroDOS.cs b/Aaru.Filesystems/MicroDOS.cs index 8e99212da..5f8788c7f 100644 --- a/Aaru.Filesystems/MicroDOS.cs +++ b/Aaru.Filesystems/MicroDOS.cs @@ -42,144 +42,143 @@ using Marshal = Aaru.Helpers.Marshal; // ReSharper disable UnusedType.Local // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// +/// Implements detection for the MicroDOS filesystem. Information from http://www.owg.ru/mkt/BK/MKDOS.TXT Thanks +/// to tarlabnor for translating it +/// +public sealed class MicroDOS : IFilesystem { + const ushort MAGIC = 0xA72E; + const ushort MAGIC2 = 0x530C; + /// - /// - /// Implements detection for the MicroDOS filesystem. Information from http://www.owg.ru/mkt/BK/MKDOS.TXT Thanks - /// to tarlabnor for translating it - /// - public sealed class MicroDOS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "MicroDOS file system"; + /// + public Guid Id => new("9F9A364A-1A27-48A3-B730-7A7122000324"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const ushort MAGIC = 0xA72E; - const ushort MAGIC2 = 0x530C; + if(1 + partition.Start >= partition.End) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "MicroDOS file system"; - /// - public Guid Id => new("9F9A364A-1A27-48A3-B730-7A7122000324"); - /// - public string Author => "Natalia Portillo"; + if(imagePlugin.Info.SectorSize < 512) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bk0); + + if(errno != ErrorNumber.NoError) + return false; + + Block0 block0 = Marshal.ByteArrayToStructureLittleEndian(bk0); + + return block0.label == MAGIC && block0.mklabel == MAGIC2; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("koi8-r"); + information = ""; + + var sb = new StringBuilder(); + + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bk0); + + if(errno != ErrorNumber.NoError) + return; + + Block0 block0 = Marshal.ByteArrayToStructureLittleEndian(bk0); + + sb.AppendLine("MicroDOS filesystem"); + sb.AppendFormat("Volume has {0} blocks ({1} bytes)", block0.blocks, block0.blocks * 512).AppendLine(); + + sb.AppendFormat("Volume has {0} blocks used ({1} bytes)", block0.usedBlocks, block0.usedBlocks * 512). + AppendLine(); + + sb.AppendFormat("Volume contains {0} files", block0.files).AppendLine(); + sb.AppendFormat("First used block is {0}", block0.firstUsedBlock).AppendLine(); + + XmlFsType = new FileSystemType { - if(1 + partition.Start >= partition.End) - return false; + Type = "MicroDOS", + ClusterSize = 512, + Clusters = block0.blocks, + Files = block0.files, + FilesSpecified = true, + FreeClusters = (ulong)(block0.blocks - block0.usedBlocks), + FreeClustersSpecified = true + }; - if(imagePlugin.Info.SectorSize < 512) - return false; + information = sb.ToString(); + } - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bk0); + // Followed by directory entries + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Block0 + { + /// BK starts booting here + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] + public readonly byte[] bootCode; + /// Number of files in directory + public readonly ushort files; + /// Total number of blocks in files of the directory + public readonly ushort usedBlocks; + /// Unknown + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 228)] + public readonly byte[] unknown; + /// Ownership label (label that shows it belongs to Micro DOS format) + public readonly ushort label; + /// MK-DOS directory format label + public readonly ushort mklabel; + /// Unknown + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] + public readonly byte[] unknown2; + /// + /// Disk size in blocks (absolute value for the system unlike NORD, NORTON etc.) that doesn't use two fixed values + /// 40 or 80 tracks, but i.e. if you drive works with 76 tracks this field will contain an appropriate number of blocks + /// + public readonly ushort blocks; + /// Number of the first file's block. Value is changable + public readonly ushort firstUsedBlock; + /// Unknown + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] unknown3; + } - if(errno != ErrorNumber.NoError) - return false; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryEntry + { + /// File status + public readonly byte status; + /// Directory number (0 - root) + public readonly byte directory; + /// File name 14. symbols in ASCII KOI8 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] filename; + /// Block number + public readonly ushort blockNo; + /// Length in blocks + public readonly ushort blocks; + /// Address + public readonly ushort address; + /// Length + public readonly ushort length; + } - Block0 block0 = Marshal.ByteArrayToStructureLittleEndian(bk0); - - return block0.label == MAGIC && block0.mklabel == MAGIC2; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("koi8-r"); - information = ""; - - var sb = new StringBuilder(); - - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bk0); - - if(errno != ErrorNumber.NoError) - return; - - Block0 block0 = Marshal.ByteArrayToStructureLittleEndian(bk0); - - sb.AppendLine("MicroDOS filesystem"); - sb.AppendFormat("Volume has {0} blocks ({1} bytes)", block0.blocks, block0.blocks * 512).AppendLine(); - - sb.AppendFormat("Volume has {0} blocks used ({1} bytes)", block0.usedBlocks, block0.usedBlocks * 512). - AppendLine(); - - sb.AppendFormat("Volume contains {0} files", block0.files).AppendLine(); - sb.AppendFormat("First used block is {0}", block0.firstUsedBlock).AppendLine(); - - XmlFsType = new FileSystemType - { - Type = "MicroDOS", - ClusterSize = 512, - Clusters = block0.blocks, - Files = block0.files, - FilesSpecified = true, - FreeClusters = (ulong)(block0.blocks - block0.usedBlocks), - FreeClustersSpecified = true - }; - - information = sb.ToString(); - } - - // Followed by directory entries - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Block0 - { - /// BK starts booting here - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 24)] - public readonly byte[] bootCode; - /// Number of files in directory - public readonly ushort files; - /// Total number of blocks in files of the directory - public readonly ushort usedBlocks; - /// Unknown - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 228)] - public readonly byte[] unknown; - /// Ownership label (label that shows it belongs to Micro DOS format) - public readonly ushort label; - /// MK-DOS directory format label - public readonly ushort mklabel; - /// Unknown - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 50)] - public readonly byte[] unknown2; - /// - /// Disk size in blocks (absolute value for the system unlike NORD, NORTON etc.) that doesn't use two fixed values - /// 40 or 80 tracks, but i.e. if you drive works with 76 tracks this field will contain an appropriate number of blocks - /// - public readonly ushort blocks; - /// Number of the first file's block. Value is changable - public readonly ushort firstUsedBlock; - /// Unknown - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] unknown3; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryEntry - { - /// File status - public readonly byte status; - /// Directory number (0 - root) - public readonly byte directory; - /// File name 14. symbols in ASCII KOI8 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] filename; - /// Block number - public readonly ushort blockNo; - /// Length in blocks - public readonly ushort blocks; - /// Address - public readonly ushort address; - /// Length - public readonly ushort length; - } - - enum FileStatus : byte - { - CommonFile = 0, Protected = 1, LogicalDisk = 2, - BadFile = 0x80, Deleted = 0xFF - } + enum FileStatus : byte + { + CommonFile = 0, Protected = 1, LogicalDisk = 2, + BadFile = 0x80, Deleted = 0xFF } } \ No newline at end of file diff --git a/Aaru.Filesystems/MinixFS.cs b/Aaru.Filesystems/MinixFS.cs index dd14c2db2..e2aed578f 100644 --- a/Aaru.Filesystems/MinixFS.cs +++ b/Aaru.Filesystems/MinixFS.cs @@ -39,360 +39,359 @@ using Aaru.CommonTypes.Interfaces; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from the Linux kernel +/// +/// Implements detection of the MINIX filesystem +public sealed class MinixFS : IFilesystem { - // Information from the Linux kernel + /// Minix v1, 14 char filenames + const ushort MINIX_MAGIC = 0x137F; + /// Minix v1, 30 char filenames + const ushort MINIX_MAGIC2 = 0x138F; + /// Minix v2, 14 char filenames + const ushort MINIX2_MAGIC = 0x2468; + /// Minix v2, 30 char filenames + const ushort MINIX2_MAGIC2 = 0x2478; + /// Minix v3, 60 char filenames + const ushort MINIX3_MAGIC = 0x4D5A; + + // Byteswapped + /// Minix v1, 14 char filenames + const ushort MINIX_CIGAM = 0x7F13; + /// Minix v1, 30 char filenames + const ushort MINIX_CIGAM2 = 0x8F13; + /// Minix v2, 14 char filenames + const ushort MINIX2_CIGAM = 0x6824; + /// Minix v2, 30 char filenames + const ushort MINIX2_CIGAM2 = 0x7824; + /// Minix v3, 60 char filenames + const ushort MINIX3_CIGAM = 0x5A4D; + /// - /// Implements detection of the MINIX filesystem - public sealed class MinixFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Minix Filesystem"; + /// + public Guid Id => new("FE248C3B-B727-4AE5-A39F-79EA9A07D4B3"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Minix v1, 14 char filenames - const ushort MINIX_MAGIC = 0x137F; - /// Minix v1, 30 char filenames - const ushort MINIX_MAGIC2 = 0x138F; - /// Minix v2, 14 char filenames - const ushort MINIX2_MAGIC = 0x2468; - /// Minix v2, 30 char filenames - const ushort MINIX2_MAGIC2 = 0x2478; - /// Minix v3, 60 char filenames - const ushort MINIX3_MAGIC = 0x4D5A; + uint sector = 2; + uint offset = 0; - // Byteswapped - /// Minix v1, 14 char filenames - const ushort MINIX_CIGAM = 0x7F13; - /// Minix v1, 30 char filenames - const ushort MINIX_CIGAM2 = 0x8F13; - /// Minix v2, 14 char filenames - const ushort MINIX2_CIGAM = 0x6824; - /// Minix v2, 30 char filenames - const ushort MINIX2_CIGAM2 = 0x7824; - /// Minix v3, 60 char filenames - const ushort MINIX3_CIGAM = 0x5A4D; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Minix Filesystem"; - /// - public Guid Id => new("FE248C3B-B727-4AE5-A39F-79EA9A07D4B3"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - uint sector = 2; - uint offset = 0; - - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - sector = 0; - offset = 0x400; - } - - if(sector + partition.Start >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(sector + partition.Start, out byte[] minixSbSector); - - if(errno != ErrorNumber.NoError) - return false; - - // Optical media - if(offset > 0) - { - byte[] tmp = new byte[0x200]; - Array.Copy(minixSbSector, offset, tmp, 0, 0x200); - minixSbSector = tmp; - } - - ushort magic = BitConverter.ToUInt16(minixSbSector, 0x010); - - if(magic == MINIX_MAGIC || - magic == MINIX_MAGIC2 || - magic == MINIX2_MAGIC || - magic == MINIX2_MAGIC2 || - magic == MINIX_CIGAM || - magic == MINIX_CIGAM2 || - magic == MINIX2_CIGAM || - magic == MINIX2_CIGAM2) - return true; - - magic = BitConverter.ToUInt16(minixSbSector, 0x018); // Here should reside magic number on Minix v3 - - return magic == MINIX_MAGIC || magic == MINIX2_MAGIC || magic == MINIX3_MAGIC || magic == MINIX_CIGAM || - magic == MINIX2_CIGAM || magic == MINIX3_CIGAM; + sector = 0; + offset = 0x400; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + if(sector + partition.Start >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSector(sector + partition.Start, out byte[] minixSbSector); + + if(errno != ErrorNumber.NoError) + return false; + + // Optical media + if(offset > 0) { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + byte[] tmp = new byte[0x200]; + Array.Copy(minixSbSector, offset, tmp, 0, 0x200); + minixSbSector = tmp; + } - var sb = new StringBuilder(); + ushort magic = BitConverter.ToUInt16(minixSbSector, 0x010); - uint sector = 2; - uint offset = 0; + if(magic == MINIX_MAGIC || + magic == MINIX_MAGIC2 || + magic == MINIX2_MAGIC || + magic == MINIX2_MAGIC2 || + magic == MINIX_CIGAM || + magic == MINIX_CIGAM2 || + magic == MINIX2_CIGAM || + magic == MINIX2_CIGAM2) + return true; - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) + magic = BitConverter.ToUInt16(minixSbSector, 0x018); // Here should reside magic number on Minix v3 + + return magic == MINIX_MAGIC || magic == MINIX2_MAGIC || magic == MINIX3_MAGIC || magic == MINIX_CIGAM || + magic == MINIX2_CIGAM || magic == MINIX3_CIGAM; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + + uint sector = 2; + uint offset = 0; + + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) + { + sector = 0; + offset = 0x400; + } + + bool minix3 = false; + int filenamesize; + string minixVersion; + ErrorNumber errno = imagePlugin.ReadSector(sector + partition.Start, out byte[] minixSbSector); + + if(errno != ErrorNumber.NoError) + return; + + // Optical media + if(offset > 0) + { + byte[] tmp = new byte[0x200]; + Array.Copy(minixSbSector, offset, tmp, 0, 0x200); + minixSbSector = tmp; + } + + ushort magic = BitConverter.ToUInt16(minixSbSector, 0x018); + + XmlFsType = new FileSystemType(); + + bool littleEndian; + + if(magic == MINIX3_MAGIC || + magic == MINIX3_CIGAM || + magic == MINIX2_MAGIC || + magic == MINIX2_CIGAM || + magic == MINIX_MAGIC || + magic == MINIX_CIGAM) + { + filenamesize = 60; + littleEndian = magic != MINIX3_CIGAM || magic == MINIX2_CIGAM || magic == MINIX_CIGAM; + + switch(magic) { - sector = 0; - offset = 0x400; + case MINIX3_MAGIC: + case MINIX3_CIGAM: + minixVersion = "Minix v3 filesystem"; + XmlFsType.Type = "Minix v3"; + + break; + case MINIX2_MAGIC: + case MINIX2_CIGAM: + minixVersion = "Minix 3 v2 filesystem"; + XmlFsType.Type = "Minix 3 v2"; + + break; + default: + minixVersion = "Minix 3 v1 filesystem"; + XmlFsType.Type = "Minix 3 v1"; + + break; } - bool minix3 = false; - int filenamesize; - string minixVersion; - ErrorNumber errno = imagePlugin.ReadSector(sector + partition.Start, out byte[] minixSbSector); + minix3 = true; + } + else + { + magic = BitConverter.ToUInt16(minixSbSector, 0x010); - if(errno != ErrorNumber.NoError) - return; - - // Optical media - if(offset > 0) + switch(magic) { - byte[] tmp = new byte[0x200]; - Array.Copy(minixSbSector, offset, tmp, 0, 0x200); - minixSbSector = tmp; + case MINIX_MAGIC: + filenamesize = 14; + minixVersion = "Minix v1 filesystem"; + littleEndian = true; + XmlFsType.Type = "Minix v1"; + + break; + case MINIX_MAGIC2: + filenamesize = 30; + minixVersion = "Minix v1 filesystem"; + littleEndian = true; + XmlFsType.Type = "Minix v1"; + + break; + case MINIX2_MAGIC: + filenamesize = 14; + minixVersion = "Minix v2 filesystem"; + littleEndian = true; + XmlFsType.Type = "Minix v2"; + + break; + case MINIX2_MAGIC2: + filenamesize = 30; + minixVersion = "Minix v2 filesystem"; + littleEndian = true; + XmlFsType.Type = "Minix v2"; + + break; + case MINIX_CIGAM: + filenamesize = 14; + minixVersion = "Minix v1 filesystem"; + littleEndian = false; + XmlFsType.Type = "Minix v1"; + + break; + case MINIX_CIGAM2: + filenamesize = 30; + minixVersion = "Minix v1 filesystem"; + littleEndian = false; + XmlFsType.Type = "Minix v1"; + + break; + case MINIX2_CIGAM: + filenamesize = 14; + minixVersion = "Minix v2 filesystem"; + littleEndian = false; + XmlFsType.Type = "Minix v2"; + + break; + case MINIX2_CIGAM2: + filenamesize = 30; + minixVersion = "Minix v2 filesystem"; + littleEndian = false; + XmlFsType.Type = "Minix v2"; + + break; + default: return; } + } - ushort magic = BitConverter.ToUInt16(minixSbSector, 0x018); + if(minix3) + { + SuperBlock3 mnxSb = littleEndian ? Marshal.ByteArrayToStructureLittleEndian(minixSbSector) + : Marshal.ByteArrayToStructureBigEndian(minixSbSector); - XmlFsType = new FileSystemType(); + if(magic != MINIX3_MAGIC && + magic != MINIX3_CIGAM) + mnxSb.s_blocksize = 1024; - bool littleEndian; + sb.AppendLine(minixVersion); + sb.AppendFormat("{0} chars in filename", filenamesize).AppendLine(); - if(magic == MINIX3_MAGIC || - magic == MINIX3_CIGAM || - magic == MINIX2_MAGIC || - magic == MINIX2_CIGAM || - magic == MINIX_MAGIC || - magic == MINIX_CIGAM) - { - filenamesize = 60; - littleEndian = magic != MINIX3_CIGAM || magic == MINIX2_CIGAM || magic == MINIX_CIGAM; - - switch(magic) - { - case MINIX3_MAGIC: - case MINIX3_CIGAM: - minixVersion = "Minix v3 filesystem"; - XmlFsType.Type = "Minix v3"; - - break; - case MINIX2_MAGIC: - case MINIX2_CIGAM: - minixVersion = "Minix 3 v2 filesystem"; - XmlFsType.Type = "Minix 3 v2"; - - break; - default: - minixVersion = "Minix 3 v1 filesystem"; - XmlFsType.Type = "Minix 3 v1"; - - break; - } - - minix3 = true; - } + if(mnxSb.s_zones > 0) // On V2 + sb.AppendFormat("{0} zones on volume ({1} bytes)", mnxSb.s_zones, mnxSb.s_zones * 1024). + AppendLine(); else - { - magic = BitConverter.ToUInt16(minixSbSector, 0x010); - - switch(magic) - { - case MINIX_MAGIC: - filenamesize = 14; - minixVersion = "Minix v1 filesystem"; - littleEndian = true; - XmlFsType.Type = "Minix v1"; - - break; - case MINIX_MAGIC2: - filenamesize = 30; - minixVersion = "Minix v1 filesystem"; - littleEndian = true; - XmlFsType.Type = "Minix v1"; - - break; - case MINIX2_MAGIC: - filenamesize = 14; - minixVersion = "Minix v2 filesystem"; - littleEndian = true; - XmlFsType.Type = "Minix v2"; - - break; - case MINIX2_MAGIC2: - filenamesize = 30; - minixVersion = "Minix v2 filesystem"; - littleEndian = true; - XmlFsType.Type = "Minix v2"; - - break; - case MINIX_CIGAM: - filenamesize = 14; - minixVersion = "Minix v1 filesystem"; - littleEndian = false; - XmlFsType.Type = "Minix v1"; - - break; - case MINIX_CIGAM2: - filenamesize = 30; - minixVersion = "Minix v1 filesystem"; - littleEndian = false; - XmlFsType.Type = "Minix v1"; - - break; - case MINIX2_CIGAM: - filenamesize = 14; - minixVersion = "Minix v2 filesystem"; - littleEndian = false; - XmlFsType.Type = "Minix v2"; - - break; - case MINIX2_CIGAM2: - filenamesize = 30; - minixVersion = "Minix v2 filesystem"; - littleEndian = false; - XmlFsType.Type = "Minix v2"; - - break; - default: return; - } - } - - if(minix3) - { - SuperBlock3 mnxSb = littleEndian ? Marshal.ByteArrayToStructureLittleEndian(minixSbSector) - : Marshal.ByteArrayToStructureBigEndian(minixSbSector); - - if(magic != MINIX3_MAGIC && - magic != MINIX3_CIGAM) - mnxSb.s_blocksize = 1024; - - sb.AppendLine(minixVersion); - sb.AppendFormat("{0} chars in filename", filenamesize).AppendLine(); - - if(mnxSb.s_zones > 0) // On V2 - sb.AppendFormat("{0} zones on volume ({1} bytes)", mnxSb.s_zones, mnxSb.s_zones * 1024). - AppendLine(); - else - sb.AppendFormat("{0} zones on volume ({1} bytes)", mnxSb.s_nzones, mnxSb.s_nzones * 1024). - AppendLine(); - - sb.AppendFormat("{0} bytes/block", mnxSb.s_blocksize).AppendLine(); - sb.AppendFormat("{0} inodes on volume", mnxSb.s_ninodes).AppendLine(); - - sb.AppendFormat("{0} blocks on inode map ({1} bytes)", mnxSb.s_imap_blocks, - mnxSb.s_imap_blocks * mnxSb.s_blocksize).AppendLine(); - - sb.AppendFormat("{0} blocks on zone map ({1} bytes)", mnxSb.s_zmap_blocks, - mnxSb.s_zmap_blocks * mnxSb.s_blocksize).AppendLine(); - - sb.AppendFormat("First data zone: {0}", mnxSb.s_firstdatazone).AppendLine(); - - //sb.AppendFormat("log2 of blocks/zone: {0}", mnx_sb.s_log_zone_size).AppendLine(); // Apparently 0 - sb.AppendFormat("{0} bytes maximum per file", mnxSb.s_max_size).AppendLine(); - sb.AppendFormat("On-disk filesystem version: {0}", mnxSb.s_disk_version).AppendLine(); - - XmlFsType.ClusterSize = mnxSb.s_blocksize; - XmlFsType.Clusters = mnxSb.s_zones > 0 ? mnxSb.s_zones : mnxSb.s_nzones; - } - else - { - SuperBlock mnxSb = littleEndian ? Marshal.ByteArrayToStructureLittleEndian(minixSbSector) - : Marshal.ByteArrayToStructureBigEndian(minixSbSector); - - sb.AppendLine(minixVersion); - sb.AppendFormat("{0} chars in filename", filenamesize).AppendLine(); - - if(mnxSb.s_zones > 0) // On V2 - sb.AppendFormat("{0} zones on volume ({1} bytes)", mnxSb.s_zones, mnxSb.s_zones * 1024). - AppendLine(); - else - sb.AppendFormat("{0} zones on volume ({1} bytes)", mnxSb.s_nzones, mnxSb.s_nzones * 1024). - AppendLine(); - - sb.AppendFormat("{0} inodes on volume", mnxSb.s_ninodes).AppendLine(); - - sb.AppendFormat("{0} blocks on inode map ({1} bytes)", mnxSb.s_imap_blocks, mnxSb.s_imap_blocks * 1024). + sb.AppendFormat("{0} zones on volume ({1} bytes)", mnxSb.s_nzones, mnxSb.s_nzones * 1024). AppendLine(); - sb.AppendFormat("{0} blocks on zone map ({1} bytes)", mnxSb.s_zmap_blocks, mnxSb.s_zmap_blocks * 1024). + sb.AppendFormat("{0} bytes/block", mnxSb.s_blocksize).AppendLine(); + sb.AppendFormat("{0} inodes on volume", mnxSb.s_ninodes).AppendLine(); + + sb.AppendFormat("{0} blocks on inode map ({1} bytes)", mnxSb.s_imap_blocks, + mnxSb.s_imap_blocks * mnxSb.s_blocksize).AppendLine(); + + sb.AppendFormat("{0} blocks on zone map ({1} bytes)", mnxSb.s_zmap_blocks, + mnxSb.s_zmap_blocks * mnxSb.s_blocksize).AppendLine(); + + sb.AppendFormat("First data zone: {0}", mnxSb.s_firstdatazone).AppendLine(); + + //sb.AppendFormat("log2 of blocks/zone: {0}", mnx_sb.s_log_zone_size).AppendLine(); // Apparently 0 + sb.AppendFormat("{0} bytes maximum per file", mnxSb.s_max_size).AppendLine(); + sb.AppendFormat("On-disk filesystem version: {0}", mnxSb.s_disk_version).AppendLine(); + + XmlFsType.ClusterSize = mnxSb.s_blocksize; + XmlFsType.Clusters = mnxSb.s_zones > 0 ? mnxSb.s_zones : mnxSb.s_nzones; + } + else + { + SuperBlock mnxSb = littleEndian ? Marshal.ByteArrayToStructureLittleEndian(minixSbSector) + : Marshal.ByteArrayToStructureBigEndian(minixSbSector); + + sb.AppendLine(minixVersion); + sb.AppendFormat("{0} chars in filename", filenamesize).AppendLine(); + + if(mnxSb.s_zones > 0) // On V2 + sb.AppendFormat("{0} zones on volume ({1} bytes)", mnxSb.s_zones, mnxSb.s_zones * 1024). + AppendLine(); + else + sb.AppendFormat("{0} zones on volume ({1} bytes)", mnxSb.s_nzones, mnxSb.s_nzones * 1024). AppendLine(); - sb.AppendFormat("First data zone: {0}", mnxSb.s_firstdatazone).AppendLine(); + sb.AppendFormat("{0} inodes on volume", mnxSb.s_ninodes).AppendLine(); - //sb.AppendFormat("log2 of blocks/zone: {0}", mnx_sb.s_log_zone_size).AppendLine(); // Apparently 0 - sb.AppendFormat("{0} bytes maximum per file", mnxSb.s_max_size).AppendLine(); - sb.AppendFormat("Filesystem state: {0:X4}", mnxSb.s_state).AppendLine(); - XmlFsType.ClusterSize = 1024; - XmlFsType.Clusters = mnxSb.s_zones > 0 ? mnxSb.s_zones : mnxSb.s_nzones; - } + sb.AppendFormat("{0} blocks on inode map ({1} bytes)", mnxSb.s_imap_blocks, mnxSb.s_imap_blocks * 1024). + AppendLine(); - information = sb.ToString(); + sb.AppendFormat("{0} blocks on zone map ({1} bytes)", mnxSb.s_zmap_blocks, mnxSb.s_zmap_blocks * 1024). + AppendLine(); + + sb.AppendFormat("First data zone: {0}", mnxSb.s_firstdatazone).AppendLine(); + + //sb.AppendFormat("log2 of blocks/zone: {0}", mnx_sb.s_log_zone_size).AppendLine(); // Apparently 0 + sb.AppendFormat("{0} bytes maximum per file", mnxSb.s_max_size).AppendLine(); + sb.AppendFormat("Filesystem state: {0:X4}", mnxSb.s_state).AppendLine(); + XmlFsType.ClusterSize = 1024; + XmlFsType.Clusters = mnxSb.s_zones > 0 ? mnxSb.s_zones : mnxSb.s_nzones; } - /// Superblock for Minix v1 and V2 filesystems - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - /// 0x00, inodes on volume - public readonly ushort s_ninodes; - /// 0x02, zones on volume - public readonly ushort s_nzones; - /// 0x04, blocks on inode map - public readonly short s_imap_blocks; - /// 0x06, blocks on zone map - public readonly short s_zmap_blocks; - /// 0x08, first data zone - public readonly ushort s_firstdatazone; - /// 0x0A, log2 of blocks/zone - public readonly short s_log_zone_size; - /// 0x0C, max file size - public readonly uint s_max_size; - /// 0x10, magic - public readonly ushort s_magic; - /// 0x12, filesystem state - public readonly ushort s_state; - /// 0x14, number of zones - public readonly uint s_zones; - } + information = sb.ToString(); + } - /// Superblock for Minix v3 filesystems - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct SuperBlock3 - { - /// 0x00, inodes on volume - public readonly uint s_ninodes; - /// 0x02, old zones on volume - public readonly ushort s_nzones; - /// 0x06, blocks on inode map - public readonly ushort s_imap_blocks; - /// 0x08, blocks on zone map - public readonly ushort s_zmap_blocks; - /// 0x0A, first data zone - public readonly ushort s_firstdatazone; - /// 0x0C, log2 of blocks/zone - public readonly ushort s_log_zone_size; - /// 0x0E, padding - public readonly ushort s_pad1; - /// 0x10, max file size - public readonly uint s_max_size; - /// 0x14, number of zones - public readonly uint s_zones; - /// 0x18, magic - public readonly ushort s_magic; - /// 0x1A, padding - public readonly ushort s_pad2; - /// 0x1C, bytes in a block - public ushort s_blocksize; - /// 0x1E, on-disk structures version - public readonly byte s_disk_version; - } + /// Superblock for Minix v1 and V2 filesystems + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + /// 0x00, inodes on volume + public readonly ushort s_ninodes; + /// 0x02, zones on volume + public readonly ushort s_nzones; + /// 0x04, blocks on inode map + public readonly short s_imap_blocks; + /// 0x06, blocks on zone map + public readonly short s_zmap_blocks; + /// 0x08, first data zone + public readonly ushort s_firstdatazone; + /// 0x0A, log2 of blocks/zone + public readonly short s_log_zone_size; + /// 0x0C, max file size + public readonly uint s_max_size; + /// 0x10, magic + public readonly ushort s_magic; + /// 0x12, filesystem state + public readonly ushort s_state; + /// 0x14, number of zones + public readonly uint s_zones; + } + + /// Superblock for Minix v3 filesystems + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct SuperBlock3 + { + /// 0x00, inodes on volume + public readonly uint s_ninodes; + /// 0x02, old zones on volume + public readonly ushort s_nzones; + /// 0x06, blocks on inode map + public readonly ushort s_imap_blocks; + /// 0x08, blocks on zone map + public readonly ushort s_zmap_blocks; + /// 0x0A, first data zone + public readonly ushort s_firstdatazone; + /// 0x0C, log2 of blocks/zone + public readonly ushort s_log_zone_size; + /// 0x0E, padding + public readonly ushort s_pad1; + /// 0x10, max file size + public readonly uint s_max_size; + /// 0x14, number of zones + public readonly uint s_zones; + /// 0x18, magic + public readonly ushort s_magic; + /// 0x1A, padding + public readonly ushort s_pad2; + /// 0x1C, bytes in a block + public ushort s_blocksize; + /// 0x1E, on-disk structures version + public readonly byte s_disk_version; } } \ No newline at end of file diff --git a/Aaru.Filesystems/NILFS2.cs b/Aaru.Filesystems/NILFS2.cs index 8cd5049aa..7bfeeacce 100644 --- a/Aaru.Filesystems/NILFS2.cs +++ b/Aaru.Filesystems/NILFS2.cs @@ -42,186 +42,185 @@ using Marshal = Aaru.Helpers.Marshal; // ReSharper disable UnusedMember.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the New Implementation of a Log-structured File System v2 +public sealed class NILFS2 : IFilesystem { + const ushort NILFS2_MAGIC = 0x3434; + const uint NILFS2_SUPER_OFFSET = 1024; + /// - /// Implements detection of the New Implementation of a Log-structured File System v2 - public sealed class NILFS2 : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "NILFS2 Plugin"; + /// + public Guid Id => new("35224226-C5CC-48B5-8FFD-3781E91E86B6"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const ushort NILFS2_MAGIC = 0x3434; - const uint NILFS2_SUPER_OFFSET = 1024; + if(imagePlugin.Info.SectorSize < 512) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "NILFS2 Plugin"; - /// - public Guid Id => new("35224226-C5CC-48B5-8FFD-3781E91E86B6"); - /// - public string Author => "Natalia Portillo"; + uint sbAddr = NILFS2_SUPER_OFFSET / imagePlugin.Info.SectorSize; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(sbAddr == 0) + sbAddr = 1; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + if(partition.Start + sbAddr + sbSize >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < Marshal.SizeOf()) + return false; + + Superblock nilfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + return nilfsSb.magic == NILFS2_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.UTF8; + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + uint sbAddr = NILFS2_SUPER_OFFSET / imagePlugin.Info.SectorSize; + + if(sbAddr == 0) + sbAddr = 1; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < Marshal.SizeOf()) + return; + + Superblock nilfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(nilfsSb.magic != NILFS2_MAGIC) + return; + + var sb = new StringBuilder(); + + sb.AppendLine("NILFS2 filesystem"); + sb.AppendFormat("Version {0}.{1}", nilfsSb.rev_level, nilfsSb.minor_rev_level).AppendLine(); + sb.AppendFormat("{0} bytes per block", 1 << (int)(nilfsSb.log_block_size + 10)).AppendLine(); + sb.AppendFormat("{0} bytes in volume", nilfsSb.dev_size).AppendLine(); + sb.AppendFormat("{0} blocks per segment", nilfsSb.blocks_per_segment).AppendLine(); + sb.AppendFormat("{0} segments", nilfsSb.nsegments).AppendLine(); + + if(nilfsSb.creator_os == 0) + sb.AppendLine("Filesystem created on Linux"); + else + sb.AppendFormat("Creator OS code: {0}", nilfsSb.creator_os).AppendLine(); + + sb.AppendFormat("{0} bytes per inode", nilfsSb.inode_size).AppendLine(); + sb.AppendFormat("Volume UUID: {0}", nilfsSb.uuid).AppendLine(); + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(nilfsSb.volume_name, Encoding)).AppendLine(); + sb.AppendFormat("Volume created on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.ctime)).AppendLine(); + + sb.AppendFormat("Volume last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.mtime)). + AppendLine(); + + sb.AppendFormat("Volume last written on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.wtime)). + AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - if(imagePlugin.Info.SectorSize < 512) - return false; + Type = "NILFS2 filesystem", + ClusterSize = (uint)(1 << (int)(nilfsSb.log_block_size + 10)), + VolumeName = StringHandlers.CToString(nilfsSb.volume_name, Encoding), + VolumeSerial = nilfsSb.uuid.ToString(), + CreationDate = DateHandlers.UnixUnsignedToDateTime(nilfsSb.ctime), + CreationDateSpecified = true, + ModificationDate = DateHandlers.UnixUnsignedToDateTime(nilfsSb.wtime), + ModificationDateSpecified = true + }; - uint sbAddr = NILFS2_SUPER_OFFSET / imagePlugin.Info.SectorSize; + if(nilfsSb.creator_os == 0) + XmlFsType.SystemIdentifier = "Linux"; - if(sbAddr == 0) - sbAddr = 1; + XmlFsType.Clusters = nilfsSb.dev_size / XmlFsType.ClusterSize; + } - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + enum State : ushort + { + Valid = 0x0001, Error = 0x0002, Resize = 0x0004 + } - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - if(partition.Start + sbAddr + sbSize >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - Superblock nilfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - return nilfsSb.magic == NILFS2_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.UTF8; - information = ""; - - if(imagePlugin.Info.SectorSize < 512) - return; - - uint sbAddr = NILFS2_SUPER_OFFSET / imagePlugin.Info.SectorSize; - - if(sbAddr == 0) - sbAddr = 1; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < Marshal.SizeOf()) - return; - - Superblock nilfsSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - if(nilfsSb.magic != NILFS2_MAGIC) - return; - - var sb = new StringBuilder(); - - sb.AppendLine("NILFS2 filesystem"); - sb.AppendFormat("Version {0}.{1}", nilfsSb.rev_level, nilfsSb.minor_rev_level).AppendLine(); - sb.AppendFormat("{0} bytes per block", 1 << (int)(nilfsSb.log_block_size + 10)).AppendLine(); - sb.AppendFormat("{0} bytes in volume", nilfsSb.dev_size).AppendLine(); - sb.AppendFormat("{0} blocks per segment", nilfsSb.blocks_per_segment).AppendLine(); - sb.AppendFormat("{0} segments", nilfsSb.nsegments).AppendLine(); - - if(nilfsSb.creator_os == 0) - sb.AppendLine("Filesystem created on Linux"); - else - sb.AppendFormat("Creator OS code: {0}", nilfsSb.creator_os).AppendLine(); - - sb.AppendFormat("{0} bytes per inode", nilfsSb.inode_size).AppendLine(); - sb.AppendFormat("Volume UUID: {0}", nilfsSb.uuid).AppendLine(); - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(nilfsSb.volume_name, Encoding)).AppendLine(); - sb.AppendFormat("Volume created on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.ctime)).AppendLine(); - - sb.AppendFormat("Volume last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.mtime)). - AppendLine(); - - sb.AppendFormat("Volume last written on {0}", DateHandlers.UnixUnsignedToDateTime(nilfsSb.wtime)). - AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "NILFS2 filesystem", - ClusterSize = (uint)(1 << (int)(nilfsSb.log_block_size + 10)), - VolumeName = StringHandlers.CToString(nilfsSb.volume_name, Encoding), - VolumeSerial = nilfsSb.uuid.ToString(), - CreationDate = DateHandlers.UnixUnsignedToDateTime(nilfsSb.ctime), - CreationDateSpecified = true, - ModificationDate = DateHandlers.UnixUnsignedToDateTime(nilfsSb.wtime), - ModificationDateSpecified = true - }; - - if(nilfsSb.creator_os == 0) - XmlFsType.SystemIdentifier = "Linux"; - - XmlFsType.Clusters = nilfsSb.dev_size / XmlFsType.ClusterSize; - } - - enum State : ushort - { - Valid = 0x0001, Error = 0x0002, Resize = 0x0004 - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Superblock - { - public readonly uint rev_level; - public readonly ushort minor_rev_level; - public readonly ushort magic; - public readonly ushort bytes; - public readonly ushort flags; - public readonly uint crc_seed; - public readonly uint sum; - public readonly uint log_block_size; - public readonly ulong nsegments; - public readonly ulong dev_size; - public readonly ulong first_data_block; - public readonly uint blocks_per_segment; - public readonly uint r_segments_percentage; - public readonly ulong last_cno; - public readonly ulong last_pseg; - public readonly ulong last_seq; - public readonly ulong free_blocks_count; - public readonly ulong ctime; - public readonly ulong mtime; - public readonly ulong wtime; - public readonly ushort mnt_count; - public readonly ushort max_mnt_count; - public readonly State state; - public readonly ushort errors; - public readonly ulong lastcheck; - public readonly uint checkinterval; - public readonly uint creator_os; - public readonly ushort def_resuid; - public readonly ushort def_resgid; - public readonly uint first_ino; - public readonly ushort inode_size; - public readonly ushort dat_entry_size; - public readonly ushort checkpoint_size; - public readonly ushort segment_usage_size; - public readonly Guid uuid; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)] - public readonly byte[] volume_name; - public readonly uint c_interval; - public readonly uint c_block_max; - public readonly ulong feature_compat; - public readonly ulong feature_compat_ro; - public readonly ulong feature_incompat; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Superblock + { + public readonly uint rev_level; + public readonly ushort minor_rev_level; + public readonly ushort magic; + public readonly ushort bytes; + public readonly ushort flags; + public readonly uint crc_seed; + public readonly uint sum; + public readonly uint log_block_size; + public readonly ulong nsegments; + public readonly ulong dev_size; + public readonly ulong first_data_block; + public readonly uint blocks_per_segment; + public readonly uint r_segments_percentage; + public readonly ulong last_cno; + public readonly ulong last_pseg; + public readonly ulong last_seq; + public readonly ulong free_blocks_count; + public readonly ulong ctime; + public readonly ulong mtime; + public readonly ulong wtime; + public readonly ushort mnt_count; + public readonly ushort max_mnt_count; + public readonly State state; + public readonly ushort errors; + public readonly ulong lastcheck; + public readonly uint checkinterval; + public readonly uint creator_os; + public readonly ushort def_resuid; + public readonly ushort def_resgid; + public readonly uint first_ino; + public readonly ushort inode_size; + public readonly ushort dat_entry_size; + public readonly ushort checkpoint_size; + public readonly ushort segment_usage_size; + public readonly Guid uuid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)] + public readonly byte[] volume_name; + public readonly uint c_interval; + public readonly uint c_block_max; + public readonly ulong feature_compat; + public readonly ulong feature_compat_ro; + public readonly ulong feature_incompat; } } \ No newline at end of file diff --git a/Aaru.Filesystems/NTFS.cs b/Aaru.Filesystems/NTFS.cs index 20468c0b2..06e5947c6 100644 --- a/Aaru.Filesystems/NTFS.cs +++ b/Aaru.Filesystems/NTFS.cs @@ -41,206 +41,205 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Inside Windows NT +/// +/// Implements detection of the New Technology File System (NTFS) +public sealed class NTFS : IFilesystem { - // Information from Inside Windows NT /// - /// Implements detection of the New Technology File System (NTFS) - public sealed class NTFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "New Technology File System (NTFS)"; + /// + public Guid Id => new("33513B2C-1e6d-4d21-a660-0bbc789c3871"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "New Technology File System (NTFS)"; - /// - public Guid Id => new("33513B2C-1e6d-4d21-a660-0bbc789c3871"); - /// - public string Author => "Natalia Portillo"; + if(2 + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + byte[] eigthBytes = new byte[8]; + + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] ntfsBpb); + + if(errno != ErrorNumber.NoError) + return false; + + Array.Copy(ntfsBpb, 0x003, eigthBytes, 0, 8); + string oemName = StringHandlers.CToString(eigthBytes); + + if(oemName != "NTFS ") + return false; + + byte fatsNo = ntfsBpb[0x010]; + ushort spFat = BitConverter.ToUInt16(ntfsBpb, 0x016); + ushort signature = BitConverter.ToUInt16(ntfsBpb, 0x1FE); + + if(fatsNo != 0) + return false; + + if(spFat != 0) + return false; + + return signature == 0xAA55; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = Encoding.Unicode; + information = ""; + + var sb = new StringBuilder(); + + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] ntfsBpb); + + if(errno != ErrorNumber.NoError) + return; + + BiosParameterBlock ntfsBb = Marshal.ByteArrayToStructureLittleEndian(ntfsBpb); + + sb.AppendFormat("{0} bytes per sector", ntfsBb.bps).AppendLine(); + sb.AppendFormat("{0} sectors per cluster ({1} bytes)", ntfsBb.spc, ntfsBb.spc * ntfsBb.bps).AppendLine(); + + // sb.AppendFormat("{0} reserved sectors", ntfs_bb.rsectors).AppendLine(); + // sb.AppendFormat("{0} FATs", ntfs_bb.fats_no).AppendLine(); + // sb.AppendFormat("{0} entries in the root folder", ntfs_bb.root_ent).AppendLine(); + // sb.AppendFormat("{0} sectors on volume (small)", ntfs_bb.sml_sectors).AppendLine(); + sb.AppendFormat("Media descriptor: 0x{0:X2}", ntfsBb.media).AppendLine(); + + // sb.AppendFormat("{0} sectors per FAT", ntfs_bb.spfat).AppendLine(); + sb.AppendFormat("{0} sectors per track", ntfsBb.sptrk).AppendLine(); + sb.AppendFormat("{0} heads", ntfsBb.heads).AppendLine(); + sb.AppendFormat("{0} hidden sectors before filesystem", ntfsBb.hsectors).AppendLine(); + + // sb.AppendFormat("{0} sectors on volume (big)", ntfs_bb.big_sectors).AppendLine(); + sb.AppendFormat("BIOS drive number: 0x{0:X2}", ntfsBb.drive_no).AppendLine(); + + // sb.AppendFormat("NT flags: 0x{0:X2}", ntfs_bb.nt_flags).AppendLine(); + // sb.AppendFormat("Signature 1: 0x{0:X2}", ntfs_bb.signature1).AppendLine(); + sb.AppendFormat("{0} sectors on volume ({1} bytes)", ntfsBb.sectors, ntfsBb.sectors * ntfsBb.bps). + AppendLine(); + + sb.AppendFormat("Cluster where $MFT starts: {0}", ntfsBb.mft_lsn).AppendLine(); + sb.AppendFormat("Cluster where $MFTMirr starts: {0}", ntfsBb.mftmirror_lsn).AppendLine(); + + if(ntfsBb.mft_rc_clusters > 0) + sb.AppendFormat("{0} clusters per MFT record ({1} bytes)", ntfsBb.mft_rc_clusters, + ntfsBb.mft_rc_clusters * ntfsBb.bps * ntfsBb.spc).AppendLine(); + else + sb.AppendFormat("{0} bytes per MFT record", 1 << -ntfsBb.mft_rc_clusters).AppendLine(); + + if(ntfsBb.index_blk_cts > 0) + sb.AppendFormat("{0} clusters per Index block ({1} bytes)", ntfsBb.index_blk_cts, + ntfsBb.index_blk_cts * ntfsBb.bps * ntfsBb.spc).AppendLine(); + else + sb.AppendFormat("{0} bytes per Index block", 1 << -ntfsBb.index_blk_cts).AppendLine(); + + sb.AppendFormat("Volume serial number: {0:X16}", ntfsBb.serial_no).AppendLine(); + + // sb.AppendFormat("Signature 2: 0x{0:X4}", ntfs_bb.signature2).AppendLine(); + + XmlFsType = new FileSystemType(); + + if(ntfsBb.jump[0] == 0xEB && + ntfsBb.jump[1] > 0x4E && + ntfsBb.jump[1] < 0x80 && + ntfsBb.signature2 == 0xAA55) { - if(2 + partition.Start >= partition.End) - return false; - - byte[] eigthBytes = new byte[8]; - - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] ntfsBpb); - - if(errno != ErrorNumber.NoError) - return false; - - Array.Copy(ntfsBpb, 0x003, eigthBytes, 0, 8); - string oemName = StringHandlers.CToString(eigthBytes); - - if(oemName != "NTFS ") - return false; - - byte fatsNo = ntfsBpb[0x010]; - ushort spFat = BitConverter.ToUInt16(ntfsBpb, 0x016); - ushort signature = BitConverter.ToUInt16(ntfsBpb, 0x1FE); - - if(fatsNo != 0) - return false; - - if(spFat != 0) - return false; - - return signature == 0xAA55; + XmlFsType.Bootable = true; + string bootChk = Sha1Context.Data(ntfsBb.boot_code, out _); + sb.AppendLine("Volume is bootable"); + sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine(); } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = Encoding.Unicode; - information = ""; + XmlFsType.ClusterSize = (uint)(ntfsBb.spc * ntfsBb.bps); + XmlFsType.Clusters = (ulong)(ntfsBb.sectors / ntfsBb.spc); + XmlFsType.VolumeSerial = $"{ntfsBb.serial_no:X16}"; + XmlFsType.Type = "NTFS"; - var sb = new StringBuilder(); + information = sb.ToString(); + } - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] ntfsBpb); + /// NTFS $BOOT + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BiosParameterBlock + { + // Start of BIOS Parameter Block + /// 0x000, Jump to boot code + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + /// 0x003, OEM Name, 8 bytes, space-padded, must be "NTFS " + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] oem_name; + /// 0x00B, Bytes per sector + public readonly ushort bps; + /// 0x00D, Sectors per cluster + public readonly byte spc; + /// 0x00E, Reserved sectors, seems 0 + public readonly ushort rsectors; + /// 0x010, Number of FATs... obviously, 0 + public readonly byte fats_no; + /// 0x011, Number of entries on root directory... 0 + public readonly ushort root_ent; + /// 0x013, Sectors in volume... 0 + public readonly ushort sml_sectors; + /// 0x015, Media descriptor + public readonly byte media; + /// 0x016, Sectors per FAT... 0 + public readonly ushort spfat; + /// 0x018, Sectors per track, required to boot + public readonly ushort sptrk; + /// 0x01A, Heads... required to boot + public readonly ushort heads; + /// 0x01C, Hidden sectors before BPB + public readonly uint hsectors; + /// 0x020, Sectors in volume if > 65535... 0 + public readonly uint big_sectors; + /// 0x024, Drive number + public readonly byte drive_no; + /// 0x025, 0 + public readonly byte nt_flags; + /// 0x026, EPB signature, 0x80 + public readonly byte signature1; + /// 0x027, Alignment + public readonly byte dummy; - if(errno != ErrorNumber.NoError) - return; + // End of BIOS Parameter Block - BiosParameterBlock ntfsBb = Marshal.ByteArrayToStructureLittleEndian(ntfsBpb); - - sb.AppendFormat("{0} bytes per sector", ntfsBb.bps).AppendLine(); - sb.AppendFormat("{0} sectors per cluster ({1} bytes)", ntfsBb.spc, ntfsBb.spc * ntfsBb.bps).AppendLine(); - - // sb.AppendFormat("{0} reserved sectors", ntfs_bb.rsectors).AppendLine(); - // sb.AppendFormat("{0} FATs", ntfs_bb.fats_no).AppendLine(); - // sb.AppendFormat("{0} entries in the root folder", ntfs_bb.root_ent).AppendLine(); - // sb.AppendFormat("{0} sectors on volume (small)", ntfs_bb.sml_sectors).AppendLine(); - sb.AppendFormat("Media descriptor: 0x{0:X2}", ntfsBb.media).AppendLine(); - - // sb.AppendFormat("{0} sectors per FAT", ntfs_bb.spfat).AppendLine(); - sb.AppendFormat("{0} sectors per track", ntfsBb.sptrk).AppendLine(); - sb.AppendFormat("{0} heads", ntfsBb.heads).AppendLine(); - sb.AppendFormat("{0} hidden sectors before filesystem", ntfsBb.hsectors).AppendLine(); - - // sb.AppendFormat("{0} sectors on volume (big)", ntfs_bb.big_sectors).AppendLine(); - sb.AppendFormat("BIOS drive number: 0x{0:X2}", ntfsBb.drive_no).AppendLine(); - - // sb.AppendFormat("NT flags: 0x{0:X2}", ntfs_bb.nt_flags).AppendLine(); - // sb.AppendFormat("Signature 1: 0x{0:X2}", ntfs_bb.signature1).AppendLine(); - sb.AppendFormat("{0} sectors on volume ({1} bytes)", ntfsBb.sectors, ntfsBb.sectors * ntfsBb.bps). - AppendLine(); - - sb.AppendFormat("Cluster where $MFT starts: {0}", ntfsBb.mft_lsn).AppendLine(); - sb.AppendFormat("Cluster where $MFTMirr starts: {0}", ntfsBb.mftmirror_lsn).AppendLine(); - - if(ntfsBb.mft_rc_clusters > 0) - sb.AppendFormat("{0} clusters per MFT record ({1} bytes)", ntfsBb.mft_rc_clusters, - ntfsBb.mft_rc_clusters * ntfsBb.bps * ntfsBb.spc).AppendLine(); - else - sb.AppendFormat("{0} bytes per MFT record", 1 << -ntfsBb.mft_rc_clusters).AppendLine(); - - if(ntfsBb.index_blk_cts > 0) - sb.AppendFormat("{0} clusters per Index block ({1} bytes)", ntfsBb.index_blk_cts, - ntfsBb.index_blk_cts * ntfsBb.bps * ntfsBb.spc).AppendLine(); - else - sb.AppendFormat("{0} bytes per Index block", 1 << -ntfsBb.index_blk_cts).AppendLine(); - - sb.AppendFormat("Volume serial number: {0:X16}", ntfsBb.serial_no).AppendLine(); - - // sb.AppendFormat("Signature 2: 0x{0:X4}", ntfs_bb.signature2).AppendLine(); - - XmlFsType = new FileSystemType(); - - if(ntfsBb.jump[0] == 0xEB && - ntfsBb.jump[1] > 0x4E && - ntfsBb.jump[1] < 0x80 && - ntfsBb.signature2 == 0xAA55) - { - XmlFsType.Bootable = true; - string bootChk = Sha1Context.Data(ntfsBb.boot_code, out _); - sb.AppendLine("Volume is bootable"); - sb.AppendFormat("Boot code's SHA1: {0}", bootChk).AppendLine(); - } - - XmlFsType.ClusterSize = (uint)(ntfsBb.spc * ntfsBb.bps); - XmlFsType.Clusters = (ulong)(ntfsBb.sectors / ntfsBb.spc); - XmlFsType.VolumeSerial = $"{ntfsBb.serial_no:X16}"; - XmlFsType.Type = "NTFS"; - - information = sb.ToString(); - } - - /// NTFS $BOOT - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BiosParameterBlock - { - // Start of BIOS Parameter Block - /// 0x000, Jump to boot code - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - /// 0x003, OEM Name, 8 bytes, space-padded, must be "NTFS " - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] oem_name; - /// 0x00B, Bytes per sector - public readonly ushort bps; - /// 0x00D, Sectors per cluster - public readonly byte spc; - /// 0x00E, Reserved sectors, seems 0 - public readonly ushort rsectors; - /// 0x010, Number of FATs... obviously, 0 - public readonly byte fats_no; - /// 0x011, Number of entries on root directory... 0 - public readonly ushort root_ent; - /// 0x013, Sectors in volume... 0 - public readonly ushort sml_sectors; - /// 0x015, Media descriptor - public readonly byte media; - /// 0x016, Sectors per FAT... 0 - public readonly ushort spfat; - /// 0x018, Sectors per track, required to boot - public readonly ushort sptrk; - /// 0x01A, Heads... required to boot - public readonly ushort heads; - /// 0x01C, Hidden sectors before BPB - public readonly uint hsectors; - /// 0x020, Sectors in volume if > 65535... 0 - public readonly uint big_sectors; - /// 0x024, Drive number - public readonly byte drive_no; - /// 0x025, 0 - public readonly byte nt_flags; - /// 0x026, EPB signature, 0x80 - public readonly byte signature1; - /// 0x027, Alignment - public readonly byte dummy; - - // End of BIOS Parameter Block - - // Start of NTFS real superblock - /// 0x028, Sectors on volume - public readonly long sectors; - /// 0x030, LSN of $MFT - public readonly long mft_lsn; - /// 0x038, LSN of $MFTMirror - public readonly long mftmirror_lsn; - /// 0x040, Clusters per MFT record - public readonly sbyte mft_rc_clusters; - /// 0x041, Alignment - public readonly byte dummy2; - /// 0x042, Alignment - public readonly ushort dummy3; - /// 0x044, Clusters per index block - public readonly sbyte index_blk_cts; - /// 0x045, Alignment - public readonly byte dummy4; - /// 0x046, Alignment - public readonly ushort dummy5; - /// 0x048, Volume serial number - public readonly ulong serial_no; - /// Boot code. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 430)] - public readonly byte[] boot_code; - /// 0x1FE, 0xAA55 - public readonly ushort signature2; - } + // Start of NTFS real superblock + /// 0x028, Sectors on volume + public readonly long sectors; + /// 0x030, LSN of $MFT + public readonly long mft_lsn; + /// 0x038, LSN of $MFTMirror + public readonly long mftmirror_lsn; + /// 0x040, Clusters per MFT record + public readonly sbyte mft_rc_clusters; + /// 0x041, Alignment + public readonly byte dummy2; + /// 0x042, Alignment + public readonly ushort dummy3; + /// 0x044, Clusters per index block + public readonly sbyte index_blk_cts; + /// 0x045, Alignment + public readonly byte dummy4; + /// 0x046, Alignment + public readonly ushort dummy5; + /// 0x048, Volume serial number + public readonly ulong serial_no; + /// Boot code. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 430)] + public readonly byte[] boot_code; + /// 0x1FE, 0xAA55 + public readonly ushort signature2; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Nintendo.cs b/Aaru.Filesystems/Nintendo.cs index e49ebfde1..a4bce5f0f 100644 --- a/Aaru.Filesystems/Nintendo.cs +++ b/Aaru.Filesystems/Nintendo.cs @@ -39,444 +39,443 @@ using Aaru.Console; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the filesystem used by Nintendo Gamecube and Wii discs +public sealed class NintendoPlugin : IFilesystem { /// - /// Implements detection of the filesystem used by Nintendo Gamecube and Wii discs - public sealed class NintendoPlugin : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Nintendo optical filesystems"; + /// + public Guid Id => new("4675fcb4-4418-4288-9e4a-33d6a4ac1126"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Nintendo optical filesystems"; - /// - public Guid Id => new("4675fcb4-4418-4288-9e4a-33d6a4ac1126"); - /// - public string Author => "Natalia Portillo"; + if(partition.Start != 0) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize < 0x50000) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(0, 0x50000 / imagePlugin.Info.SectorSize, out byte[] header); + + if(errno != ErrorNumber.NoError) + return false; + + uint magicGc = BigEndianBitConverter.ToUInt32(header, 0x1C); + uint magicWii = BigEndianBitConverter.ToUInt32(header, 0x18); + + return magicGc == 0xC2339F3D || magicWii == 0x5D1C9EA3; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("shift_jis"); + var sbInformation = new StringBuilder(); + information = ""; + XmlFsType = new FileSystemType(); + + var fields = new NintendoFields(); + + ErrorNumber errno = imagePlugin.ReadSectors(0, 0x50000 / imagePlugin.Info.SectorSize, out byte[] header); + + if(errno != ErrorNumber.NoError) + return; + + bool wii = false; + + uint magicGc = BigEndianBitConverter.ToUInt32(header, 0x1C); + uint magicWii = BigEndianBitConverter.ToUInt32(header, 0x18); + + if(magicWii == 0x5D1C9EA3) + wii = true; + else if(magicGc != 0xC2339F3D) + return; + + fields.DiscType = Encoding.ASCII.GetString(header, 0, 1); + fields.GameCode = Encoding.ASCII.GetString(header, 1, 2); + fields.RegionCode = Encoding.ASCII.GetString(header, 3, 1); + fields.PublisherCode = Encoding.ASCII.GetString(header, 4, 2); + fields.DiscId = Encoding.ASCII.GetString(header, 0, 6); + fields.DiscNumber = header[6]; + fields.DiscVersion = header[7]; + fields.Streaming |= header[8] > 0; + fields.StreamBufferSize = header[9]; + byte[] temp = new byte[64]; + Array.Copy(header, 0x20, temp, 0, 64); + fields.Title = StringHandlers.CToString(temp, Encoding); + + if(!wii) { - if(partition.Start != 0) - return false; - - if(imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize < 0x50000) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(0, 0x50000 / imagePlugin.Info.SectorSize, out byte[] header); - - if(errno != ErrorNumber.NoError) - return false; - - uint magicGc = BigEndianBitConverter.ToUInt32(header, 0x1C); - uint magicWii = BigEndianBitConverter.ToUInt32(header, 0x18); - - return magicGc == 0xC2339F3D || magicWii == 0x5D1C9EA3; + fields.DebugOff = BigEndianBitConverter.ToUInt32(header, 0x0400); + fields.DebugAddr = BigEndianBitConverter.ToUInt32(header, 0x0404); + fields.DolOff = BigEndianBitConverter.ToUInt32(header, 0x0420); + fields.FstOff = BigEndianBitConverter.ToUInt32(header, 0x0424); + fields.FstSize = BigEndianBitConverter.ToUInt32(header, 0x0428); + fields.FstMax = BigEndianBitConverter.ToUInt32(header, 0x042C); } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + if(wii) { - Encoding = encoding ?? Encoding.GetEncoding("shift_jis"); - var sbInformation = new StringBuilder(); - information = ""; - XmlFsType = new FileSystemType(); + uint offset1 = BigEndianBitConverter.ToUInt32(header, 0x40004) << 2; + uint offset2 = BigEndianBitConverter.ToUInt32(header, 0x4000C) << 2; + uint offset3 = BigEndianBitConverter.ToUInt32(header, 0x40014) << 2; + uint offset4 = BigEndianBitConverter.ToUInt32(header, 0x4001C) << 2; - var fields = new NintendoFields(); - - ErrorNumber errno = imagePlugin.ReadSectors(0, 0x50000 / imagePlugin.Info.SectorSize, out byte[] header); - - if(errno != ErrorNumber.NoError) - return; - - bool wii = false; - - uint magicGc = BigEndianBitConverter.ToUInt32(header, 0x1C); - uint magicWii = BigEndianBitConverter.ToUInt32(header, 0x18); - - if(magicWii == 0x5D1C9EA3) - wii = true; - else if(magicGc != 0xC2339F3D) - return; - - fields.DiscType = Encoding.ASCII.GetString(header, 0, 1); - fields.GameCode = Encoding.ASCII.GetString(header, 1, 2); - fields.RegionCode = Encoding.ASCII.GetString(header, 3, 1); - fields.PublisherCode = Encoding.ASCII.GetString(header, 4, 2); - fields.DiscId = Encoding.ASCII.GetString(header, 0, 6); - fields.DiscNumber = header[6]; - fields.DiscVersion = header[7]; - fields.Streaming |= header[8] > 0; - fields.StreamBufferSize = header[9]; - byte[] temp = new byte[64]; - Array.Copy(header, 0x20, temp, 0, 64); - fields.Title = StringHandlers.CToString(temp, Encoding); - - if(!wii) - { - fields.DebugOff = BigEndianBitConverter.ToUInt32(header, 0x0400); - fields.DebugAddr = BigEndianBitConverter.ToUInt32(header, 0x0404); - fields.DolOff = BigEndianBitConverter.ToUInt32(header, 0x0420); - fields.FstOff = BigEndianBitConverter.ToUInt32(header, 0x0424); - fields.FstSize = BigEndianBitConverter.ToUInt32(header, 0x0428); - fields.FstMax = BigEndianBitConverter.ToUInt32(header, 0x042C); - } - - if(wii) - { - uint offset1 = BigEndianBitConverter.ToUInt32(header, 0x40004) << 2; - uint offset2 = BigEndianBitConverter.ToUInt32(header, 0x4000C) << 2; - uint offset3 = BigEndianBitConverter.ToUInt32(header, 0x40014) << 2; - uint offset4 = BigEndianBitConverter.ToUInt32(header, 0x4001C) << 2; - - fields.FirstPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40000)]; - fields.SecondPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40008)]; - fields.ThirdPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40010)]; - fields.FourthPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40018)]; - - for(int i = 0; i < fields.FirstPartitions.Length; i++) - if(offset1 + (i * 8) + 8 < 0x50000) - { - fields.FirstPartitions[i].Offset = - BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 0)) << 2; - - fields.FirstPartitions[i].Type = - BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 4)); - } - - for(int i = 0; i < fields.SecondPartitions.Length; i++) - if(offset1 + (i * 8) + 8 < 0x50000) - { - fields.FirstPartitions[i].Offset = - BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 0)) << 2; - - fields.FirstPartitions[i].Type = - BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 4)); - } - - for(int i = 0; i < fields.ThirdPartitions.Length; i++) - if(offset1 + (i * 8) + 8 < 0x50000) - { - fields.FirstPartitions[i].Offset = - BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 0)) << 2; - - fields.FirstPartitions[i].Type = - BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 4)); - } - - for(int i = 0; i < fields.FourthPartitions.Length; i++) - if(offset1 + (i * 8) + 8 < 0x50000) - { - fields.FirstPartitions[i].Offset = - BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 0)) << 2; - - fields.FirstPartitions[i].Type = - BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 4)); - } - - fields.Region = header[0x4E000]; - fields.JapanAge = header[0x4E010]; - fields.UsaAge = header[0x4E011]; - fields.GermanAge = header[0x4E013]; - fields.PegiAge = header[0x4E014]; - fields.FinlandAge = header[0x4E015]; - fields.PortugalAge = header[0x4E016]; - fields.UkAge = header[0x4E017]; - fields.AustraliaAge = header[0x4E018]; - fields.KoreaAge = header[0x4E019]; - } - else - { - fields.FirstPartitions = Array.Empty(); - fields.SecondPartitions = Array.Empty(); - fields.ThirdPartitions = Array.Empty(); - fields.FourthPartitions = Array.Empty(); - } - - AaruConsole.DebugWriteLine("Nintendo plugin", "discType = {0}", fields.DiscType); - AaruConsole.DebugWriteLine("Nintendo plugin", "gameCode = {0}", fields.GameCode); - AaruConsole.DebugWriteLine("Nintendo plugin", "regionCode = {0}", fields.RegionCode); - AaruConsole.DebugWriteLine("Nintendo plugin", "publisherCode = {0}", fields.PublisherCode); - AaruConsole.DebugWriteLine("Nintendo plugin", "discID = {0}", fields.DiscId); - AaruConsole.DebugWriteLine("Nintendo plugin", "discNumber = {0}", fields.DiscNumber); - AaruConsole.DebugWriteLine("Nintendo plugin", "discVersion = {0}", fields.DiscVersion); - AaruConsole.DebugWriteLine("Nintendo plugin", "streaming = {0}", fields.Streaming); - AaruConsole.DebugWriteLine("Nintendo plugin", "streamBufferSize = {0}", fields.StreamBufferSize); - AaruConsole.DebugWriteLine("Nintendo plugin", "title = \"{0}\"", fields.Title); - AaruConsole.DebugWriteLine("Nintendo plugin", "debugOff = 0x{0:X8}", fields.DebugOff); - AaruConsole.DebugWriteLine("Nintendo plugin", "debugAddr = 0x{0:X8}", fields.DebugAddr); - AaruConsole.DebugWriteLine("Nintendo plugin", "dolOff = 0x{0:X8}", fields.DolOff); - AaruConsole.DebugWriteLine("Nintendo plugin", "fstOff = 0x{0:X8}", fields.FstOff); - AaruConsole.DebugWriteLine("Nintendo plugin", "fstSize = {0}", fields.FstSize); - AaruConsole.DebugWriteLine("Nintendo plugin", "fstMax = {0}", fields.FstMax); + fields.FirstPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40000)]; + fields.SecondPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40008)]; + fields.ThirdPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40010)]; + fields.FourthPartitions = new NintendoPartition[BigEndianBitConverter.ToUInt32(header, 0x40018)]; for(int i = 0; i < fields.FirstPartitions.Length; i++) - { - AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].offset = {0}", - fields.FirstPartitions[i].Offset, i); + if(offset1 + (i * 8) + 8 < 0x50000) + { + fields.FirstPartitions[i].Offset = + BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 0)) << 2; - AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].type = {0}", - fields.FirstPartitions[i].Type, i); - } + fields.FirstPartitions[i].Type = + BigEndianBitConverter.ToUInt32(header, (int)(offset1 + (i * 8) + 4)); + } for(int i = 0; i < fields.SecondPartitions.Length; i++) - { - AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].offset = {0}", - fields.SecondPartitions[i].Offset, i); + if(offset1 + (i * 8) + 8 < 0x50000) + { + fields.FirstPartitions[i].Offset = + BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 0)) << 2; - AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].type = {0}", - fields.SecondPartitions[i].Type, i); - } + fields.FirstPartitions[i].Type = + BigEndianBitConverter.ToUInt32(header, (int)(offset2 + (i * 8) + 4)); + } for(int i = 0; i < fields.ThirdPartitions.Length; i++) - { - AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].offset = {0}", - fields.ThirdPartitions[i].Offset, i); + if(offset1 + (i * 8) + 8 < 0x50000) + { + fields.FirstPartitions[i].Offset = + BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 0)) << 2; - AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].type = {0}", - fields.ThirdPartitions[i].Type, i); - } + fields.FirstPartitions[i].Type = + BigEndianBitConverter.ToUInt32(header, (int)(offset3 + (i * 8) + 4)); + } for(int i = 0; i < fields.FourthPartitions.Length; i++) - { - AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].offset = {0}", - fields.FourthPartitions[i].Offset, i); + if(offset1 + (i * 8) + 8 < 0x50000) + { + fields.FirstPartitions[i].Offset = + BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 0)) << 2; - AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].type = {0}", - fields.FourthPartitions[i].Type, i); - } + fields.FirstPartitions[i].Type = + BigEndianBitConverter.ToUInt32(header, (int)(offset4 + (i * 8) + 4)); + } - AaruConsole.DebugWriteLine("Nintendo plugin", "region = {0}", fields.Region); - AaruConsole.DebugWriteLine("Nintendo plugin", "japanAge = {0}", fields.JapanAge); - AaruConsole.DebugWriteLine("Nintendo plugin", "usaAge = {0}", fields.UsaAge); - AaruConsole.DebugWriteLine("Nintendo plugin", "germanAge = {0}", fields.GermanAge); - AaruConsole.DebugWriteLine("Nintendo plugin", "pegiAge = {0}", fields.PegiAge); - AaruConsole.DebugWriteLine("Nintendo plugin", "finlandAge = {0}", fields.FinlandAge); - AaruConsole.DebugWriteLine("Nintendo plugin", "portugalAge = {0}", fields.PortugalAge); - AaruConsole.DebugWriteLine("Nintendo plugin", "ukAge = {0}", fields.UkAge); - AaruConsole.DebugWriteLine("Nintendo plugin", "australiaAge = {0}", fields.AustraliaAge); - AaruConsole.DebugWriteLine("Nintendo plugin", "koreaAge = {0}", fields.KoreaAge); - - sbInformation.AppendLine("Nintendo optical filesystem"); - sbInformation.AppendLine(wii ? "Nintendo Wii Optical Disc" : "Nintendo GameCube Optical Disc"); - sbInformation.AppendFormat("Disc ID is {0}", fields.DiscId).AppendLine(); - sbInformation.AppendFormat("Disc is a {0} disc", DiscTypeToString(fields.DiscType)).AppendLine(); - sbInformation.AppendFormat("Disc region is {0}", RegionCodeToString(fields.RegionCode)).AppendLine(); - sbInformation.AppendFormat("Published by {0}", PublisherCodeToString(fields.PublisherCode)).AppendLine(); - - if(fields.DiscNumber > 0) - sbInformation.AppendFormat("Disc number {0} of a multi-disc set", fields.DiscNumber + 1).AppendLine(); - - if(fields.Streaming) - sbInformation.AppendLine("Disc is prepared for audio streaming"); - - if(fields.StreamBufferSize > 0) - sbInformation.AppendFormat("Audio streaming buffer size is {0} bytes", fields.StreamBufferSize). - AppendLine(); - - sbInformation.AppendFormat("Title: {0}", fields.Title).AppendLine(); - - if(wii) - { - for(int i = 0; i < fields.FirstPartitions.Length; i++) - sbInformation.AppendFormat("First {0} partition starts at sector {1}", - PartitionTypeToString(fields.FirstPartitions[i].Type), - fields.FirstPartitions[i].Offset / 2048).AppendLine(); - - for(int i = 0; i < fields.SecondPartitions.Length; i++) - sbInformation.AppendFormat("Second {0} partition starts at sector {1}", - PartitionTypeToString(fields.SecondPartitions[i].Type), - fields.SecondPartitions[i].Offset / 2048).AppendLine(); - - for(int i = 0; i < fields.ThirdPartitions.Length; i++) - sbInformation.AppendFormat("Third {0} partition starts at sector {1}", - PartitionTypeToString(fields.ThirdPartitions[i].Type), - fields.ThirdPartitions[i].Offset / 2048).AppendLine(); - - for(int i = 0; i < fields.FourthPartitions.Length; i++) - sbInformation.AppendFormat("Fourth {0} partition starts at sector {1}", - PartitionTypeToString(fields.FourthPartitions[i].Type), - fields.FourthPartitions[i].Offset / 2048).AppendLine(); - - // sbInformation.AppendFormat("Region byte is {0}", fields.region).AppendLine(); - if((fields.JapanAge & 0x80) != 0x80) - sbInformation.AppendFormat("Japan age rating is {0}", fields.JapanAge).AppendLine(); - - if((fields.UsaAge & 0x80) != 0x80) - sbInformation.AppendFormat("ESRB age rating is {0}", fields.UsaAge).AppendLine(); - - if((fields.GermanAge & 0x80) != 0x80) - sbInformation.AppendFormat("German age rating is {0}", fields.GermanAge).AppendLine(); - - if((fields.PegiAge & 0x80) != 0x80) - sbInformation.AppendFormat("PEGI age rating is {0}", fields.PegiAge).AppendLine(); - - if((fields.FinlandAge & 0x80) != 0x80) - sbInformation.AppendFormat("Finland age rating is {0}", fields.FinlandAge).AppendLine(); - - if((fields.PortugalAge & 0x80) != 0x80) - sbInformation.AppendFormat("Portugal age rating is {0}", fields.PortugalAge).AppendLine(); - - if((fields.UkAge & 0x80) != 0x80) - sbInformation.AppendFormat("UK age rating is {0}", fields.UkAge).AppendLine(); - - if((fields.AustraliaAge & 0x80) != 0x80) - sbInformation.AppendFormat("Australia age rating is {0}", fields.AustraliaAge).AppendLine(); - - if((fields.KoreaAge & 0x80) != 0x80) - sbInformation.AppendFormat("Korea age rating is {0}", fields.KoreaAge).AppendLine(); - } - else - sbInformation.AppendFormat("FST starts at {0} and has {1} bytes", fields.FstOff, fields.FstSize). - AppendLine(); - - information = sbInformation.ToString(); - XmlFsType.Bootable = true; - XmlFsType.Clusters = imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize / 2048; - XmlFsType.ClusterSize = 2048; - XmlFsType.Type = wii ? "Nintendo Wii filesystem" : "Nintendo Gamecube filesystem"; - XmlFsType.VolumeName = fields.Title; - XmlFsType.VolumeSerial = fields.DiscId; + fields.Region = header[0x4E000]; + fields.JapanAge = header[0x4E010]; + fields.UsaAge = header[0x4E011]; + fields.GermanAge = header[0x4E013]; + fields.PegiAge = header[0x4E014]; + fields.FinlandAge = header[0x4E015]; + fields.PortugalAge = header[0x4E016]; + fields.UkAge = header[0x4E017]; + fields.AustraliaAge = header[0x4E018]; + fields.KoreaAge = header[0x4E019]; } - - static string DiscTypeToString(string discType) + else { - switch(discType) - { - case "C": return "Commodore 64 Virtual Console"; - case "D": return "Demo"; - case "E": return "Neo-Geo Virtual Console"; - case "F": return "NES Virtual Console"; - case "G": return "Gamecube"; - case "H": return "Wii channel"; - case "J": return "Super Nintendo Virtual Console"; - case "L": return "Master System Virtual Console"; - case "M": return "Megadrive Virtual Console"; - case "N": return "Nintendo 64 Virtual Console"; - case "P": return "Promotional or TurboGrafx Virtual Console"; - case "Q": return "TurboGrafx CD Virtual Console"; - case "R": - case "S": return "Wii"; - case "U": return "Utility"; - case "W": return "WiiWare"; - case "X": return "MSX Virtual Console or WiiWare demo"; - case "0": - case "1": return "Diagnostic"; - case "4": return "Wii Backup"; - case "_": return "WiiFit"; - } - - return $"unknown type '{discType}'"; + fields.FirstPartitions = Array.Empty(); + fields.SecondPartitions = Array.Empty(); + fields.ThirdPartitions = Array.Empty(); + fields.FourthPartitions = Array.Empty(); } - static string RegionCodeToString(string regionCode) + AaruConsole.DebugWriteLine("Nintendo plugin", "discType = {0}", fields.DiscType); + AaruConsole.DebugWriteLine("Nintendo plugin", "gameCode = {0}", fields.GameCode); + AaruConsole.DebugWriteLine("Nintendo plugin", "regionCode = {0}", fields.RegionCode); + AaruConsole.DebugWriteLine("Nintendo plugin", "publisherCode = {0}", fields.PublisherCode); + AaruConsole.DebugWriteLine("Nintendo plugin", "discID = {0}", fields.DiscId); + AaruConsole.DebugWriteLine("Nintendo plugin", "discNumber = {0}", fields.DiscNumber); + AaruConsole.DebugWriteLine("Nintendo plugin", "discVersion = {0}", fields.DiscVersion); + AaruConsole.DebugWriteLine("Nintendo plugin", "streaming = {0}", fields.Streaming); + AaruConsole.DebugWriteLine("Nintendo plugin", "streamBufferSize = {0}", fields.StreamBufferSize); + AaruConsole.DebugWriteLine("Nintendo plugin", "title = \"{0}\"", fields.Title); + AaruConsole.DebugWriteLine("Nintendo plugin", "debugOff = 0x{0:X8}", fields.DebugOff); + AaruConsole.DebugWriteLine("Nintendo plugin", "debugAddr = 0x{0:X8}", fields.DebugAddr); + AaruConsole.DebugWriteLine("Nintendo plugin", "dolOff = 0x{0:X8}", fields.DolOff); + AaruConsole.DebugWriteLine("Nintendo plugin", "fstOff = 0x{0:X8}", fields.FstOff); + AaruConsole.DebugWriteLine("Nintendo plugin", "fstSize = {0}", fields.FstSize); + AaruConsole.DebugWriteLine("Nintendo plugin", "fstMax = {0}", fields.FstMax); + + for(int i = 0; i < fields.FirstPartitions.Length; i++) { - switch(regionCode) - { - case "A": return "any region"; - case "D": return "Germany"; - case "N": - case "E": return "USA"; - case "F": return "France"; - case "I": return "Italy"; - case "J": return "Japan"; - case "K": - case "Q": return "Korea"; - case "L": - case "M": - case "P": return "PAL"; - case "R": return "Russia"; - case "S": return "Spain"; - case "T": return "Taiwan"; - case "U": return "Australia"; - } + AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].offset = {0}", + fields.FirstPartitions[i].Offset, i); - return $"unknown code '{regionCode}'"; + AaruConsole.DebugWriteLine("Nintendo plugin", "firstPartitions[{1}].type = {0}", + fields.FirstPartitions[i].Type, i); } - static string PublisherCodeToString(string publisherCode) + for(int i = 0; i < fields.SecondPartitions.Length; i++) { - switch(publisherCode) - { - case "01": return "Nintendo"; - case "08": return "CAPCOM"; - case "41": return "Ubisoft"; - case "4F": return "Eidos"; - case "51": return "Acclaim"; - case "52": return "Activision"; - case "5D": return "Midway"; - case "5G": return "Hudson"; - case "64": return "LucasArts"; - case "69": return "Electronic Arts"; - case "6S": return "TDK Mediactive"; - case "8P": return "SEGA"; - case "A4": return "Mirage Studios"; - case "AF": return "Namco"; - case "B2": return "Bandai"; - case "DA": return "Tomy"; - case "EM": return "Konami"; - case "70": return "Atari"; - case "4Q": return "Disney Interactive"; - case "GD": return "Square Enix"; - case "7D": return "Sierra"; - } + AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].offset = {0}", + fields.SecondPartitions[i].Offset, i); - return $"Unknown publisher '{publisherCode}'"; + AaruConsole.DebugWriteLine("Nintendo plugin", "secondPartitions[{1}].type = {0}", + fields.SecondPartitions[i].Type, i); } - static string PartitionTypeToString(uint type) + for(int i = 0; i < fields.ThirdPartitions.Length; i++) { - switch(type) - { - case 0: return "data"; - case 1: return "update"; - case 2: return "channel"; - } + AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].offset = {0}", + fields.ThirdPartitions[i].Offset, i); - return $"unknown type {type}"; + AaruConsole.DebugWriteLine("Nintendo plugin", "thirdPartitions[{1}].type = {0}", + fields.ThirdPartitions[i].Type, i); } - struct NintendoFields + for(int i = 0; i < fields.FourthPartitions.Length; i++) { - public string DiscType; - public string GameCode; - public string RegionCode; - public string PublisherCode; - public string DiscId; - public byte DiscNumber; - public byte DiscVersion; - public bool Streaming; - public byte StreamBufferSize; - public string Title; - public uint DebugOff; - public uint DebugAddr; - public uint DolOff; - public uint FstOff; - public uint FstSize; - public uint FstMax; - public NintendoPartition[] FirstPartitions; - public NintendoPartition[] SecondPartitions; - public NintendoPartition[] ThirdPartitions; - public NintendoPartition[] FourthPartitions; - public byte Region; - public byte JapanAge; - public byte UsaAge; - public byte GermanAge; - public byte PegiAge; - public byte FinlandAge; - public byte PortugalAge; - public byte UkAge; - public byte AustraliaAge; - public byte KoreaAge; + AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].offset = {0}", + fields.FourthPartitions[i].Offset, i); + + AaruConsole.DebugWriteLine("Nintendo plugin", "fourthPartitions[{1}].type = {0}", + fields.FourthPartitions[i].Type, i); } - struct NintendoPartition + AaruConsole.DebugWriteLine("Nintendo plugin", "region = {0}", fields.Region); + AaruConsole.DebugWriteLine("Nintendo plugin", "japanAge = {0}", fields.JapanAge); + AaruConsole.DebugWriteLine("Nintendo plugin", "usaAge = {0}", fields.UsaAge); + AaruConsole.DebugWriteLine("Nintendo plugin", "germanAge = {0}", fields.GermanAge); + AaruConsole.DebugWriteLine("Nintendo plugin", "pegiAge = {0}", fields.PegiAge); + AaruConsole.DebugWriteLine("Nintendo plugin", "finlandAge = {0}", fields.FinlandAge); + AaruConsole.DebugWriteLine("Nintendo plugin", "portugalAge = {0}", fields.PortugalAge); + AaruConsole.DebugWriteLine("Nintendo plugin", "ukAge = {0}", fields.UkAge); + AaruConsole.DebugWriteLine("Nintendo plugin", "australiaAge = {0}", fields.AustraliaAge); + AaruConsole.DebugWriteLine("Nintendo plugin", "koreaAge = {0}", fields.KoreaAge); + + sbInformation.AppendLine("Nintendo optical filesystem"); + sbInformation.AppendLine(wii ? "Nintendo Wii Optical Disc" : "Nintendo GameCube Optical Disc"); + sbInformation.AppendFormat("Disc ID is {0}", fields.DiscId).AppendLine(); + sbInformation.AppendFormat("Disc is a {0} disc", DiscTypeToString(fields.DiscType)).AppendLine(); + sbInformation.AppendFormat("Disc region is {0}", RegionCodeToString(fields.RegionCode)).AppendLine(); + sbInformation.AppendFormat("Published by {0}", PublisherCodeToString(fields.PublisherCode)).AppendLine(); + + if(fields.DiscNumber > 0) + sbInformation.AppendFormat("Disc number {0} of a multi-disc set", fields.DiscNumber + 1).AppendLine(); + + if(fields.Streaming) + sbInformation.AppendLine("Disc is prepared for audio streaming"); + + if(fields.StreamBufferSize > 0) + sbInformation.AppendFormat("Audio streaming buffer size is {0} bytes", fields.StreamBufferSize). + AppendLine(); + + sbInformation.AppendFormat("Title: {0}", fields.Title).AppendLine(); + + if(wii) { - public uint Offset; - public uint Type; + for(int i = 0; i < fields.FirstPartitions.Length; i++) + sbInformation.AppendFormat("First {0} partition starts at sector {1}", + PartitionTypeToString(fields.FirstPartitions[i].Type), + fields.FirstPartitions[i].Offset / 2048).AppendLine(); + + for(int i = 0; i < fields.SecondPartitions.Length; i++) + sbInformation.AppendFormat("Second {0} partition starts at sector {1}", + PartitionTypeToString(fields.SecondPartitions[i].Type), + fields.SecondPartitions[i].Offset / 2048).AppendLine(); + + for(int i = 0; i < fields.ThirdPartitions.Length; i++) + sbInformation.AppendFormat("Third {0} partition starts at sector {1}", + PartitionTypeToString(fields.ThirdPartitions[i].Type), + fields.ThirdPartitions[i].Offset / 2048).AppendLine(); + + for(int i = 0; i < fields.FourthPartitions.Length; i++) + sbInformation.AppendFormat("Fourth {0} partition starts at sector {1}", + PartitionTypeToString(fields.FourthPartitions[i].Type), + fields.FourthPartitions[i].Offset / 2048).AppendLine(); + + // sbInformation.AppendFormat("Region byte is {0}", fields.region).AppendLine(); + if((fields.JapanAge & 0x80) != 0x80) + sbInformation.AppendFormat("Japan age rating is {0}", fields.JapanAge).AppendLine(); + + if((fields.UsaAge & 0x80) != 0x80) + sbInformation.AppendFormat("ESRB age rating is {0}", fields.UsaAge).AppendLine(); + + if((fields.GermanAge & 0x80) != 0x80) + sbInformation.AppendFormat("German age rating is {0}", fields.GermanAge).AppendLine(); + + if((fields.PegiAge & 0x80) != 0x80) + sbInformation.AppendFormat("PEGI age rating is {0}", fields.PegiAge).AppendLine(); + + if((fields.FinlandAge & 0x80) != 0x80) + sbInformation.AppendFormat("Finland age rating is {0}", fields.FinlandAge).AppendLine(); + + if((fields.PortugalAge & 0x80) != 0x80) + sbInformation.AppendFormat("Portugal age rating is {0}", fields.PortugalAge).AppendLine(); + + if((fields.UkAge & 0x80) != 0x80) + sbInformation.AppendFormat("UK age rating is {0}", fields.UkAge).AppendLine(); + + if((fields.AustraliaAge & 0x80) != 0x80) + sbInformation.AppendFormat("Australia age rating is {0}", fields.AustraliaAge).AppendLine(); + + if((fields.KoreaAge & 0x80) != 0x80) + sbInformation.AppendFormat("Korea age rating is {0}", fields.KoreaAge).AppendLine(); } + else + sbInformation.AppendFormat("FST starts at {0} and has {1} bytes", fields.FstOff, fields.FstSize). + AppendLine(); + + information = sbInformation.ToString(); + XmlFsType.Bootable = true; + XmlFsType.Clusters = imagePlugin.Info.Sectors * imagePlugin.Info.SectorSize / 2048; + XmlFsType.ClusterSize = 2048; + XmlFsType.Type = wii ? "Nintendo Wii filesystem" : "Nintendo Gamecube filesystem"; + XmlFsType.VolumeName = fields.Title; + XmlFsType.VolumeSerial = fields.DiscId; + } + + static string DiscTypeToString(string discType) + { + switch(discType) + { + case "C": return "Commodore 64 Virtual Console"; + case "D": return "Demo"; + case "E": return "Neo-Geo Virtual Console"; + case "F": return "NES Virtual Console"; + case "G": return "Gamecube"; + case "H": return "Wii channel"; + case "J": return "Super Nintendo Virtual Console"; + case "L": return "Master System Virtual Console"; + case "M": return "Megadrive Virtual Console"; + case "N": return "Nintendo 64 Virtual Console"; + case "P": return "Promotional or TurboGrafx Virtual Console"; + case "Q": return "TurboGrafx CD Virtual Console"; + case "R": + case "S": return "Wii"; + case "U": return "Utility"; + case "W": return "WiiWare"; + case "X": return "MSX Virtual Console or WiiWare demo"; + case "0": + case "1": return "Diagnostic"; + case "4": return "Wii Backup"; + case "_": return "WiiFit"; + } + + return $"unknown type '{discType}'"; + } + + static string RegionCodeToString(string regionCode) + { + switch(regionCode) + { + case "A": return "any region"; + case "D": return "Germany"; + case "N": + case "E": return "USA"; + case "F": return "France"; + case "I": return "Italy"; + case "J": return "Japan"; + case "K": + case "Q": return "Korea"; + case "L": + case "M": + case "P": return "PAL"; + case "R": return "Russia"; + case "S": return "Spain"; + case "T": return "Taiwan"; + case "U": return "Australia"; + } + + return $"unknown code '{regionCode}'"; + } + + static string PublisherCodeToString(string publisherCode) + { + switch(publisherCode) + { + case "01": return "Nintendo"; + case "08": return "CAPCOM"; + case "41": return "Ubisoft"; + case "4F": return "Eidos"; + case "51": return "Acclaim"; + case "52": return "Activision"; + case "5D": return "Midway"; + case "5G": return "Hudson"; + case "64": return "LucasArts"; + case "69": return "Electronic Arts"; + case "6S": return "TDK Mediactive"; + case "8P": return "SEGA"; + case "A4": return "Mirage Studios"; + case "AF": return "Namco"; + case "B2": return "Bandai"; + case "DA": return "Tomy"; + case "EM": return "Konami"; + case "70": return "Atari"; + case "4Q": return "Disney Interactive"; + case "GD": return "Square Enix"; + case "7D": return "Sierra"; + } + + return $"Unknown publisher '{publisherCode}'"; + } + + static string PartitionTypeToString(uint type) + { + switch(type) + { + case 0: return "data"; + case 1: return "update"; + case 2: return "channel"; + } + + return $"unknown type {type}"; + } + + struct NintendoFields + { + public string DiscType; + public string GameCode; + public string RegionCode; + public string PublisherCode; + public string DiscId; + public byte DiscNumber; + public byte DiscVersion; + public bool Streaming; + public byte StreamBufferSize; + public string Title; + public uint DebugOff; + public uint DebugAddr; + public uint DolOff; + public uint FstOff; + public uint FstSize; + public uint FstMax; + public NintendoPartition[] FirstPartitions; + public NintendoPartition[] SecondPartitions; + public NintendoPartition[] ThirdPartitions; + public NintendoPartition[] FourthPartitions; + public byte Region; + public byte JapanAge; + public byte UsaAge; + public byte GermanAge; + public byte PegiAge; + public byte FinlandAge; + public byte PortugalAge; + public byte UkAge; + public byte AustraliaAge; + public byte KoreaAge; + } + + struct NintendoPartition + { + public uint Offset; + public uint Type; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ODS.cs b/Aaru.Filesystems/ODS.cs index fafac18c3..b6fa58310 100644 --- a/Aaru.Filesystems/ODS.cs +++ b/Aaru.Filesystems/ODS.cs @@ -41,361 +41,360 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from VMS File System Internals by Kirby McCoy +// ISBN: 1-55558-056-4 +// With some hints from http://www.decuslib.com/DECUS/vmslt97b/gnusoftware/gccaxp/7_1/vms/hm2def.h +// Expects the home block to be always in sector #1 (does not check deltas) +// Assumes a sector size of 512 bytes (VMS does on HDDs and optical drives, dunno about M.O.) +// Book only describes ODS-2. Need to test ODS-1 and ODS-5 +// There is an ODS with signature "DECFILES11A", yet to be seen +// Time is a 64 bit unsigned integer, tenths of microseconds since 1858/11/17 00:00:00. +// TODO: Implement checksum +/// +/// Implements detection of DEC's On-Disk Structure, aka the ODS filesystem +public sealed class ODS : IFilesystem { - // Information from VMS File System Internals by Kirby McCoy - // ISBN: 1-55558-056-4 - // With some hints from http://www.decuslib.com/DECUS/vmslt97b/gnusoftware/gccaxp/7_1/vms/hm2def.h - // Expects the home block to be always in sector #1 (does not check deltas) - // Assumes a sector size of 512 bytes (VMS does on HDDs and optical drives, dunno about M.O.) - // Book only describes ODS-2. Need to test ODS-1 and ODS-5 - // There is an ODS with signature "DECFILES11A", yet to be seen - // Time is a 64 bit unsigned integer, tenths of microseconds since 1858/11/17 00:00:00. - // TODO: Implement checksum /// - /// Implements detection of DEC's On-Disk Structure, aka the ODS filesystem - public sealed class ODS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Files-11 On-Disk Structure"; + /// + public Guid Id => new("de20633c-8021-4384-aeb0-83b0df14491f"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Files-11 On-Disk Structure"; - /// - public Guid Id => new("de20633c-8021-4384-aeb0-83b0df14491f"); - /// - public string Author => "Natalia Portillo"; + if(2 + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(imagePlugin.Info.SectorSize < 512) + return false; + + byte[] magicB = new byte[12]; + ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] hbSector); + + if(errno != ErrorNumber.NoError) + return false; + + Array.Copy(hbSector, 0x1F0, magicB, 0, 12); + string magic = Encoding.ASCII.GetString(magicB); + + AaruConsole.DebugWriteLine("Files-11 plugin", "magic: \"{0}\"", magic); + + if(magic == "DECFILE11A " || + magic == "DECFILE11B ") + return true; + + // Optical disc + if(imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) + return false; + + if(hbSector.Length < 0x400) + return false; + + errno = imagePlugin.ReadSector(partition.Start, out hbSector); + + if(errno != ErrorNumber.NoError) + return false; + + Array.Copy(hbSector, 0x3F0, magicB, 0, 12); + magic = Encoding.ASCII.GetString(magicB); + + AaruConsole.DebugWriteLine("Files-11 plugin", "unaligned magic: \"{0}\"", magic); + + return magic == "DECFILE11A " || magic == "DECFILE11B "; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); + information = ""; + + var sb = new StringBuilder(); + + ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] hbSector); + + if(errno != ErrorNumber.NoError) + return; + + HomeBlock homeblock = Marshal.ByteArrayToStructureLittleEndian(hbSector); + + // Optical disc + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc && + StringHandlers.CToString(homeblock.format) != "DECFILE11A " && + StringHandlers.CToString(homeblock.format) != "DECFILE11B ") { - if(2 + partition.Start >= partition.End) - return false; - - if(imagePlugin.Info.SectorSize < 512) - return false; - - byte[] magicB = new byte[12]; - ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] hbSector); - - if(errno != ErrorNumber.NoError) - return false; - - Array.Copy(hbSector, 0x1F0, magicB, 0, 12); - string magic = Encoding.ASCII.GetString(magicB); - - AaruConsole.DebugWriteLine("Files-11 plugin", "magic: \"{0}\"", magic); - - if(magic == "DECFILE11A " || - magic == "DECFILE11B ") - return true; - - // Optical disc - if(imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) - return false; - if(hbSector.Length < 0x400) - return false; + return; - errno = imagePlugin.ReadSector(partition.Start, out hbSector); - - if(errno != ErrorNumber.NoError) - return false; - - Array.Copy(hbSector, 0x3F0, magicB, 0, 12); - magic = Encoding.ASCII.GetString(magicB); - - AaruConsole.DebugWriteLine("Files-11 plugin", "unaligned magic: \"{0}\"", magic); - - return magic == "DECFILE11A " || magic == "DECFILE11B "; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); - information = ""; - - var sb = new StringBuilder(); - - ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] hbSector); + errno = imagePlugin.ReadSector(partition.Start, out byte[] tmp); if(errno != ErrorNumber.NoError) return; - HomeBlock homeblock = Marshal.ByteArrayToStructureLittleEndian(hbSector); + hbSector = new byte[0x200]; + Array.Copy(tmp, 0x200, hbSector, 0, 0x200); - // Optical disc - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc && - StringHandlers.CToString(homeblock.format) != "DECFILE11A " && + homeblock = Marshal.ByteArrayToStructureLittleEndian(hbSector); + + if(StringHandlers.CToString(homeblock.format) != "DECFILE11A " && StringHandlers.CToString(homeblock.format) != "DECFILE11B ") - { - if(hbSector.Length < 0x400) - return; - - errno = imagePlugin.ReadSector(partition.Start, out byte[] tmp); - - if(errno != ErrorNumber.NoError) - return; - - hbSector = new byte[0x200]; - Array.Copy(tmp, 0x200, hbSector, 0, 0x200); - - homeblock = Marshal.ByteArrayToStructureLittleEndian(hbSector); - - if(StringHandlers.CToString(homeblock.format) != "DECFILE11A " && - StringHandlers.CToString(homeblock.format) != "DECFILE11B ") - return; - } - - if((homeblock.struclev & 0xFF00) != 0x0200 || - (homeblock.struclev & 0xFF) != 1 || - StringHandlers.CToString(homeblock.format) != "DECFILE11B ") - sb.AppendLine("The following information may be incorrect for this volume."); - - if(homeblock.resfiles < 5 || - homeblock.devtype != 0) - sb.AppendLine("This volume may be corrupted."); - - sb.AppendFormat("Volume format is {0}", StringHandlers.SpacePaddedToString(homeblock.format, Encoding)). - AppendLine(); - - sb.AppendFormat("Volume is Level {0} revision {1}", (homeblock.struclev & 0xFF00) >> 8, - homeblock.struclev & 0xFF).AppendLine(); - - sb.AppendFormat("Lowest structure in the volume is Level {0}, revision {1}", - (homeblock.lowstruclev & 0xFF00) >> 8, homeblock.lowstruclev & 0xFF).AppendLine(); - - sb.AppendFormat("Highest structure in the volume is Level {0}, revision {1}", - (homeblock.highstruclev & 0xFF00) >> 8, homeblock.highstruclev & 0xFF).AppendLine(); - - sb.AppendFormat("{0} sectors per cluster ({1} bytes)", homeblock.cluster, homeblock.cluster * 512). - AppendLine(); - - sb.AppendFormat("This home block is on sector {0} (VBN {1})", homeblock.homelbn, homeblock.homevbn). - AppendLine(); - - sb.AppendFormat("Secondary home block is on sector {0} (VBN {1})", homeblock.alhomelbn, - homeblock.alhomevbn).AppendLine(); - - sb.AppendFormat("Volume bitmap starts in sector {0} (VBN {1})", homeblock.ibmaplbn, homeblock.ibmapvbn). - AppendLine(); - - sb.AppendFormat("Volume bitmap runs for {0} sectors ({1} bytes)", homeblock.ibmapsize, - homeblock.ibmapsize * 512).AppendLine(); - - sb.AppendFormat("Backup INDEXF.SYS;1 is in sector {0} (VBN {1})", homeblock.altidxlbn, homeblock.altidxvbn). - AppendLine(); - - sb.AppendFormat("{0} maximum files on the volume", homeblock.maxfiles).AppendLine(); - sb.AppendFormat("{0} reserved files", homeblock.resfiles).AppendLine(); - - if(homeblock.rvn > 0 && - homeblock.setcount > 0 && - StringHandlers.CToString(homeblock.strucname) != " ") - sb.AppendFormat("Volume is {0} of {1} in set \"{2}\".", homeblock.rvn, homeblock.setcount, - StringHandlers.SpacePaddedToString(homeblock.strucname, Encoding)).AppendLine(); - - sb.AppendFormat("Volume owner is \"{0}\" (ID 0x{1:X8})", - StringHandlers.SpacePaddedToString(homeblock.ownername, Encoding), homeblock.volowner). - AppendLine(); - - sb.AppendFormat("Volume label: \"{0}\"", StringHandlers.SpacePaddedToString(homeblock.volname, Encoding)). - AppendLine(); - - sb.AppendFormat("Drive serial number: 0x{0:X8}", homeblock.serialnum).AppendLine(); - sb.AppendFormat("Volume was created on {0}", DateHandlers.VmsToDateTime(homeblock.credate)).AppendLine(); - - if(homeblock.revdate > 0) - sb.AppendFormat("Volume was last modified on {0}", DateHandlers.VmsToDateTime(homeblock.revdate)). - AppendLine(); - - if(homeblock.copydate > 0) - sb.AppendFormat("Volume copied on {0}", DateHandlers.VmsToDateTime(homeblock.copydate)).AppendLine(); - - sb.AppendFormat("Checksums: 0x{0:X4} and 0x{1:X4}", homeblock.checksum1, homeblock.checksum2).AppendLine(); - sb.AppendLine("Flags:"); - sb.AppendFormat("Window: {0}", homeblock.window).AppendLine(); - sb.AppendFormat("Cached directores: {0}", homeblock.lru_lim).AppendLine(); - sb.AppendFormat("Default allocation: {0} blocks", homeblock.extend).AppendLine(); - - if((homeblock.volchar & 0x01) == 0x01) - sb.AppendLine("Readings should be verified"); - - if((homeblock.volchar & 0x02) == 0x02) - sb.AppendLine("Writings should be verified"); - - if((homeblock.volchar & 0x04) == 0x04) - sb.AppendLine("Files should be erased or overwritten when deleted"); - - if((homeblock.volchar & 0x08) == 0x08) - sb.AppendLine("Highwater mark is to be disabled"); - - if((homeblock.volchar & 0x10) == 0x10) - sb.AppendLine("Classification checks are enabled"); - - sb.AppendLine("Volume permissions (r = read, w = write, c = create, d = delete)"); - sb.AppendLine("System, owner, group, world"); - - // System - sb.Append((homeblock.protect & 0x1000) == 0x1000 ? "-" : "r"); - sb.Append((homeblock.protect & 0x2000) == 0x2000 ? "-" : "w"); - sb.Append((homeblock.protect & 0x4000) == 0x4000 ? "-" : "c"); - sb.Append((homeblock.protect & 0x8000) == 0x8000 ? "-" : "d"); - - // Owner - sb.Append((homeblock.protect & 0x100) == 0x100 ? "-" : "r"); - sb.Append((homeblock.protect & 0x200) == 0x200 ? "-" : "w"); - sb.Append((homeblock.protect & 0x400) == 0x400 ? "-" : "c"); - sb.Append((homeblock.protect & 0x800) == 0x800 ? "-" : "d"); - - // Group - sb.Append((homeblock.protect & 0x10) == 0x10 ? "-" : "r"); - sb.Append((homeblock.protect & 0x20) == 0x20 ? "-" : "w"); - sb.Append((homeblock.protect & 0x40) == 0x40 ? "-" : "c"); - sb.Append((homeblock.protect & 0x80) == 0x80 ? "-" : "d"); - - // World (other) - sb.Append((homeblock.protect & 0x1) == 0x1 ? "-" : "r"); - sb.Append((homeblock.protect & 0x2) == 0x2 ? "-" : "w"); - sb.Append((homeblock.protect & 0x4) == 0x4 ? "-" : "c"); - sb.Append((homeblock.protect & 0x8) == 0x8 ? "-" : "d"); - - sb.AppendLine(); - - sb.AppendLine("Unknown structures:"); - sb.AppendFormat("Security mask: 0x{0:X8}", homeblock.sec_mask).AppendLine(); - sb.AppendFormat("File protection: 0x{0:X4}", homeblock.fileprot).AppendLine(); - sb.AppendFormat("Record protection: 0x{0:X4}", homeblock.recprot).AppendLine(); - - XmlFsType = new FileSystemType - { - Type = "FILES-11", - ClusterSize = (uint)(homeblock.cluster * 512), - Clusters = partition.Size / (ulong)(homeblock.cluster * 512), - VolumeName = StringHandlers.SpacePaddedToString(homeblock.volname, Encoding), - VolumeSerial = $"{homeblock.serialnum:X8}" - }; - - if(homeblock.credate > 0) - { - XmlFsType.CreationDate = DateHandlers.VmsToDateTime(homeblock.credate); - XmlFsType.CreationDateSpecified = true; - } - - if(homeblock.revdate > 0) - { - XmlFsType.ModificationDate = DateHandlers.VmsToDateTime(homeblock.revdate); - XmlFsType.ModificationDateSpecified = true; - } - - information = sb.ToString(); + return; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HomeBlock + if((homeblock.struclev & 0xFF00) != 0x0200 || + (homeblock.struclev & 0xFF) != 1 || + StringHandlers.CToString(homeblock.format) != "DECFILE11B ") + sb.AppendLine("The following information may be incorrect for this volume."); + + if(homeblock.resfiles < 5 || + homeblock.devtype != 0) + sb.AppendLine("This volume may be corrupted."); + + sb.AppendFormat("Volume format is {0}", StringHandlers.SpacePaddedToString(homeblock.format, Encoding)). + AppendLine(); + + sb.AppendFormat("Volume is Level {0} revision {1}", (homeblock.struclev & 0xFF00) >> 8, + homeblock.struclev & 0xFF).AppendLine(); + + sb.AppendFormat("Lowest structure in the volume is Level {0}, revision {1}", + (homeblock.lowstruclev & 0xFF00) >> 8, homeblock.lowstruclev & 0xFF).AppendLine(); + + sb.AppendFormat("Highest structure in the volume is Level {0}, revision {1}", + (homeblock.highstruclev & 0xFF00) >> 8, homeblock.highstruclev & 0xFF).AppendLine(); + + sb.AppendFormat("{0} sectors per cluster ({1} bytes)", homeblock.cluster, homeblock.cluster * 512). + AppendLine(); + + sb.AppendFormat("This home block is on sector {0} (VBN {1})", homeblock.homelbn, homeblock.homevbn). + AppendLine(); + + sb.AppendFormat("Secondary home block is on sector {0} (VBN {1})", homeblock.alhomelbn, + homeblock.alhomevbn).AppendLine(); + + sb.AppendFormat("Volume bitmap starts in sector {0} (VBN {1})", homeblock.ibmaplbn, homeblock.ibmapvbn). + AppendLine(); + + sb.AppendFormat("Volume bitmap runs for {0} sectors ({1} bytes)", homeblock.ibmapsize, + homeblock.ibmapsize * 512).AppendLine(); + + sb.AppendFormat("Backup INDEXF.SYS;1 is in sector {0} (VBN {1})", homeblock.altidxlbn, homeblock.altidxvbn). + AppendLine(); + + sb.AppendFormat("{0} maximum files on the volume", homeblock.maxfiles).AppendLine(); + sb.AppendFormat("{0} reserved files", homeblock.resfiles).AppendLine(); + + if(homeblock.rvn > 0 && + homeblock.setcount > 0 && + StringHandlers.CToString(homeblock.strucname) != " ") + sb.AppendFormat("Volume is {0} of {1} in set \"{2}\".", homeblock.rvn, homeblock.setcount, + StringHandlers.SpacePaddedToString(homeblock.strucname, Encoding)).AppendLine(); + + sb.AppendFormat("Volume owner is \"{0}\" (ID 0x{1:X8})", + StringHandlers.SpacePaddedToString(homeblock.ownername, Encoding), homeblock.volowner). + AppendLine(); + + sb.AppendFormat("Volume label: \"{0}\"", StringHandlers.SpacePaddedToString(homeblock.volname, Encoding)). + AppendLine(); + + sb.AppendFormat("Drive serial number: 0x{0:X8}", homeblock.serialnum).AppendLine(); + sb.AppendFormat("Volume was created on {0}", DateHandlers.VmsToDateTime(homeblock.credate)).AppendLine(); + + if(homeblock.revdate > 0) + sb.AppendFormat("Volume was last modified on {0}", DateHandlers.VmsToDateTime(homeblock.revdate)). + AppendLine(); + + if(homeblock.copydate > 0) + sb.AppendFormat("Volume copied on {0}", DateHandlers.VmsToDateTime(homeblock.copydate)).AppendLine(); + + sb.AppendFormat("Checksums: 0x{0:X4} and 0x{1:X4}", homeblock.checksum1, homeblock.checksum2).AppendLine(); + sb.AppendLine("Flags:"); + sb.AppendFormat("Window: {0}", homeblock.window).AppendLine(); + sb.AppendFormat("Cached directores: {0}", homeblock.lru_lim).AppendLine(); + sb.AppendFormat("Default allocation: {0} blocks", homeblock.extend).AppendLine(); + + if((homeblock.volchar & 0x01) == 0x01) + sb.AppendLine("Readings should be verified"); + + if((homeblock.volchar & 0x02) == 0x02) + sb.AppendLine("Writings should be verified"); + + if((homeblock.volchar & 0x04) == 0x04) + sb.AppendLine("Files should be erased or overwritten when deleted"); + + if((homeblock.volchar & 0x08) == 0x08) + sb.AppendLine("Highwater mark is to be disabled"); + + if((homeblock.volchar & 0x10) == 0x10) + sb.AppendLine("Classification checks are enabled"); + + sb.AppendLine("Volume permissions (r = read, w = write, c = create, d = delete)"); + sb.AppendLine("System, owner, group, world"); + + // System + sb.Append((homeblock.protect & 0x1000) == 0x1000 ? "-" : "r"); + sb.Append((homeblock.protect & 0x2000) == 0x2000 ? "-" : "w"); + sb.Append((homeblock.protect & 0x4000) == 0x4000 ? "-" : "c"); + sb.Append((homeblock.protect & 0x8000) == 0x8000 ? "-" : "d"); + + // Owner + sb.Append((homeblock.protect & 0x100) == 0x100 ? "-" : "r"); + sb.Append((homeblock.protect & 0x200) == 0x200 ? "-" : "w"); + sb.Append((homeblock.protect & 0x400) == 0x400 ? "-" : "c"); + sb.Append((homeblock.protect & 0x800) == 0x800 ? "-" : "d"); + + // Group + sb.Append((homeblock.protect & 0x10) == 0x10 ? "-" : "r"); + sb.Append((homeblock.protect & 0x20) == 0x20 ? "-" : "w"); + sb.Append((homeblock.protect & 0x40) == 0x40 ? "-" : "c"); + sb.Append((homeblock.protect & 0x80) == 0x80 ? "-" : "d"); + + // World (other) + sb.Append((homeblock.protect & 0x1) == 0x1 ? "-" : "r"); + sb.Append((homeblock.protect & 0x2) == 0x2 ? "-" : "w"); + sb.Append((homeblock.protect & 0x4) == 0x4 ? "-" : "c"); + sb.Append((homeblock.protect & 0x8) == 0x8 ? "-" : "d"); + + sb.AppendLine(); + + sb.AppendLine("Unknown structures:"); + sb.AppendFormat("Security mask: 0x{0:X8}", homeblock.sec_mask).AppendLine(); + sb.AppendFormat("File protection: 0x{0:X4}", homeblock.fileprot).AppendLine(); + sb.AppendFormat("Record protection: 0x{0:X4}", homeblock.recprot).AppendLine(); + + XmlFsType = new FileSystemType { - /// 0x000, LBN of THIS home block - public readonly uint homelbn; - /// 0x004, LBN of the secondary home block - public readonly uint alhomelbn; - /// 0x008, LBN of backup INDEXF.SYS;1 - public readonly uint altidxlbn; - /// 0x00C, High byte contains filesystem version (1, 2 or 5), low byte contains revision (1) - public readonly ushort struclev; - /// 0x00E, Number of blocks each bit of the volume bitmap represents - public readonly ushort cluster; - /// 0x010, VBN of THIS home block - public readonly ushort homevbn; - /// 0x012, VBN of the secondary home block - public readonly ushort alhomevbn; - /// 0x014, VBN of backup INDEXF.SYS;1 - public readonly ushort altidxvbn; - /// 0x016, VBN of the bitmap - public readonly ushort ibmapvbn; - /// 0x018, LBN of the bitmap - public readonly uint ibmaplbn; - /// 0x01C, Max files on volume - public readonly uint maxfiles; - /// 0x020, Bitmap size in sectors - public readonly ushort ibmapsize; - /// 0x022, Reserved files, 5 at minimum - public readonly ushort resfiles; - /// 0x024, Device type, ODS-2 defines it as always 0 - public readonly ushort devtype; - /// 0x026, Relative volume number (number of the volume in a set) - public readonly ushort rvn; - /// 0x028, Total number of volumes in the set this volume is - public readonly ushort setcount; - /// 0x02A, Flags - public readonly ushort volchar; - /// 0x02C, User ID of the volume owner - public readonly uint volowner; - /// 0x030, Security mask (??) - public readonly uint sec_mask; - /// 0x034, Volume permissions (system, owner, group and other) - public readonly ushort protect; - /// 0x036, Default file protection, unsupported in ODS-2 - public readonly ushort fileprot; - /// 0x038, Default file record protection - public readonly ushort recprot; - /// 0x03A, Checksum of all preceding entries - public readonly ushort checksum1; - /// 0x03C, Creation date - public readonly ulong credate; - /// 0x044, Window size (pointers for the window) - public readonly byte window; - /// 0x045, Directories to be stored in cache - public readonly byte lru_lim; - /// 0x046, Default allocation size in blocks - public readonly ushort extend; - /// 0x048, Minimum file retention period - public readonly ulong retainmin; - /// 0x050, Maximum file retention period - public readonly ulong retainmax; - /// 0x058, Last modification date - public readonly ulong revdate; - /// 0x060, Minimum security class, 20 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly byte[] min_class; - /// 0x074, Maximum security class, 20 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] - public readonly byte[] max_class; - /// 0x088, File lookup table FID - public readonly ushort filetab_fid1; - /// 0x08A, File lookup table FID - public readonly ushort filetab_fid2; - /// 0x08C, File lookup table FID - public readonly ushort filetab_fid3; - /// 0x08E, Lowest structure level on the volume - public readonly ushort lowstruclev; - /// 0x090, Highest structure level on the volume - public readonly ushort highstruclev; - /// 0x092, Volume copy date (??) - public readonly ulong copydate; - /// 0x09A, 302 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 302)] - public readonly byte[] reserved1; - /// 0x1C8, Physical drive serial number - public readonly uint serialnum; - /// 0x1CC, Name of the volume set, 12 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] strucname; - /// 0x1D8, Volume label, 12 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] volname; - /// 0x1E4, Name of the volume owner, 12 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] ownername; - /// 0x1F0, ODS-2 defines it as "DECFILE11B", 12 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] format; - /// 0x1FC, Reserved - public readonly ushort reserved2; - /// 0x1FE, Checksum of preceding 255 words (16 bit units) - public readonly ushort checksum2; + Type = "FILES-11", + ClusterSize = (uint)(homeblock.cluster * 512), + Clusters = partition.Size / (ulong)(homeblock.cluster * 512), + VolumeName = StringHandlers.SpacePaddedToString(homeblock.volname, Encoding), + VolumeSerial = $"{homeblock.serialnum:X8}" + }; + + if(homeblock.credate > 0) + { + XmlFsType.CreationDate = DateHandlers.VmsToDateTime(homeblock.credate); + XmlFsType.CreationDateSpecified = true; } + + if(homeblock.revdate > 0) + { + XmlFsType.ModificationDate = DateHandlers.VmsToDateTime(homeblock.revdate); + XmlFsType.ModificationDateSpecified = true; + } + + information = sb.ToString(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HomeBlock + { + /// 0x000, LBN of THIS home block + public readonly uint homelbn; + /// 0x004, LBN of the secondary home block + public readonly uint alhomelbn; + /// 0x008, LBN of backup INDEXF.SYS;1 + public readonly uint altidxlbn; + /// 0x00C, High byte contains filesystem version (1, 2 or 5), low byte contains revision (1) + public readonly ushort struclev; + /// 0x00E, Number of blocks each bit of the volume bitmap represents + public readonly ushort cluster; + /// 0x010, VBN of THIS home block + public readonly ushort homevbn; + /// 0x012, VBN of the secondary home block + public readonly ushort alhomevbn; + /// 0x014, VBN of backup INDEXF.SYS;1 + public readonly ushort altidxvbn; + /// 0x016, VBN of the bitmap + public readonly ushort ibmapvbn; + /// 0x018, LBN of the bitmap + public readonly uint ibmaplbn; + /// 0x01C, Max files on volume + public readonly uint maxfiles; + /// 0x020, Bitmap size in sectors + public readonly ushort ibmapsize; + /// 0x022, Reserved files, 5 at minimum + public readonly ushort resfiles; + /// 0x024, Device type, ODS-2 defines it as always 0 + public readonly ushort devtype; + /// 0x026, Relative volume number (number of the volume in a set) + public readonly ushort rvn; + /// 0x028, Total number of volumes in the set this volume is + public readonly ushort setcount; + /// 0x02A, Flags + public readonly ushort volchar; + /// 0x02C, User ID of the volume owner + public readonly uint volowner; + /// 0x030, Security mask (??) + public readonly uint sec_mask; + /// 0x034, Volume permissions (system, owner, group and other) + public readonly ushort protect; + /// 0x036, Default file protection, unsupported in ODS-2 + public readonly ushort fileprot; + /// 0x038, Default file record protection + public readonly ushort recprot; + /// 0x03A, Checksum of all preceding entries + public readonly ushort checksum1; + /// 0x03C, Creation date + public readonly ulong credate; + /// 0x044, Window size (pointers for the window) + public readonly byte window; + /// 0x045, Directories to be stored in cache + public readonly byte lru_lim; + /// 0x046, Default allocation size in blocks + public readonly ushort extend; + /// 0x048, Minimum file retention period + public readonly ulong retainmin; + /// 0x050, Maximum file retention period + public readonly ulong retainmax; + /// 0x058, Last modification date + public readonly ulong revdate; + /// 0x060, Minimum security class, 20 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly byte[] min_class; + /// 0x074, Maximum security class, 20 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)] + public readonly byte[] max_class; + /// 0x088, File lookup table FID + public readonly ushort filetab_fid1; + /// 0x08A, File lookup table FID + public readonly ushort filetab_fid2; + /// 0x08C, File lookup table FID + public readonly ushort filetab_fid3; + /// 0x08E, Lowest structure level on the volume + public readonly ushort lowstruclev; + /// 0x090, Highest structure level on the volume + public readonly ushort highstruclev; + /// 0x092, Volume copy date (??) + public readonly ulong copydate; + /// 0x09A, 302 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 302)] + public readonly byte[] reserved1; + /// 0x1C8, Physical drive serial number + public readonly uint serialnum; + /// 0x1CC, Name of the volume set, 12 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] strucname; + /// 0x1D8, Volume label, 12 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] volname; + /// 0x1E4, Name of the volume owner, 12 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] ownername; + /// 0x1F0, ODS-2 defines it as "DECFILE11B", 12 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] format; + /// 0x1FC, Reserved + public readonly ushort reserved2; + /// 0x1FE, Checksum of preceding 255 words (16 bit units) + public readonly ushort checksum2; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Opera/Consts.cs b/Aaru.Filesystems/Opera/Consts.cs index 40d53fc44..5f56476cb 100644 --- a/Aaru.Filesystems/Opera/Consts.cs +++ b/Aaru.Filesystems/Opera/Consts.cs @@ -33,27 +33,26 @@ using System.Diagnostics.CodeAnalysis; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class OperaFS { - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class OperaFS + const string SYNC = "ZZZZZ"; + const uint FLAGS_MASK = 0xFF; + const int MAX_NAME = 32; + + /// Directory + const uint TYPE_DIR = 0x2A646972; + /// Disc label + const uint TYPE_LBL = 0x2A6C626C; + /// Catapult + const uint TYPE_ZAP = 0x2A7A6170; + static readonly int _directoryEntrySize = Marshal.SizeOf(); + + enum FileFlags : uint { - const string SYNC = "ZZZZZ"; - const uint FLAGS_MASK = 0xFF; - const int MAX_NAME = 32; - - /// Directory - const uint TYPE_DIR = 0x2A646972; - /// Disc label - const uint TYPE_LBL = 0x2A6C626C; - /// Catapult - const uint TYPE_ZAP = 0x2A7A6170; - static readonly int _directoryEntrySize = Marshal.SizeOf(); - - enum FileFlags : uint - { - File = 2, Special = 6, Directory = 7, - LastEntryInBlock = 0x40000000, LastEntry = 0x80000000 - } + File = 2, Special = 6, Directory = 7, + LastEntryInBlock = 0x40000000, LastEntry = 0x80000000 } } \ No newline at end of file diff --git a/Aaru.Filesystems/Opera/Dir.cs b/Aaru.Filesystems/Opera/Dir.cs index e998de780..779effb9a 100644 --- a/Aaru.Filesystems/Opera/Dir.cs +++ b/Aaru.Filesystems/Opera/Dir.cs @@ -37,45 +37,59 @@ using System.Linq; using Aaru.CommonTypes.Enums; using Aaru.Helpers; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class OperaFS { - public sealed partial class OperaFS + /// + public ErrorNumber ReadDir(string path, out List contents) { - /// - public ErrorNumber ReadDir(string path, out List contents) + contents = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(string.IsNullOrWhiteSpace(path) || + path == "/") { - contents = null; + contents = _rootDirectoryCache.Keys.ToList(); - if(!_mounted) - return ErrorNumber.AccessDenied; + return ErrorNumber.NoError; + } - if(string.IsNullOrWhiteSpace(path) || - path == "/") - { - contents = _rootDirectoryCache.Keys.ToList(); + string cutPath = path.StartsWith("/", StringComparison.Ordinal) + ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) + : path.ToLower(CultureInfo.CurrentUICulture); - return ErrorNumber.NoError; - } + if(_directoryCache.TryGetValue(cutPath, + out Dictionary currentDirectory)) + { + contents = currentDirectory.Keys.ToList(); - string cutPath = path.StartsWith("/", StringComparison.Ordinal) - ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) - : path.ToLower(CultureInfo.CurrentUICulture); + return ErrorNumber.NoError; + } - if(_directoryCache.TryGetValue(cutPath, - out Dictionary currentDirectory)) - { - contents = currentDirectory.Keys.ToList(); + string[] pieces = cutPath.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); - return ErrorNumber.NoError; - } + KeyValuePair entry = + _rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[0]); - string[] pieces = cutPath.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + if(string.IsNullOrEmpty(entry.Key)) + return ErrorNumber.NoSuchFile; - KeyValuePair entry = - _rootDirectoryCache.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[0]); + if((entry.Value.Entry.flags & FLAGS_MASK) != (int)FileFlags.Directory) + return ErrorNumber.NotDirectory; + + string currentPath = pieces[0]; + + currentDirectory = _rootDirectoryCache; + + for(int p = 0; p < pieces.Length; p++) + { + entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[p]); if(string.IsNullOrEmpty(entry.Key)) return ErrorNumber.NoSuchFile; @@ -83,90 +97,75 @@ namespace Aaru.Filesystems if((entry.Value.Entry.flags & FLAGS_MASK) != (int)FileFlags.Directory) return ErrorNumber.NotDirectory; - string currentPath = pieces[0]; + currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; - currentDirectory = _rootDirectoryCache; + if(_directoryCache.TryGetValue(currentPath, out currentDirectory)) + continue; - for(int p = 0; p < pieces.Length; p++) + if(entry.Value.Pointers.Length < 1) + return ErrorNumber.InvalidArgument; + + currentDirectory = DecodeDirectory((int)entry.Value.Pointers[0]); + + _directoryCache.Add(currentPath, currentDirectory); + } + + contents = currentDirectory?.Keys.ToList(); + + return ErrorNumber.NoError; + } + + Dictionary DecodeDirectory(int firstBlock) + { + Dictionary entries = new(); + + int nextBlock = firstBlock; + + DirectoryHeader header; + + do + { + ErrorNumber errno = _image.ReadSectors((ulong)(nextBlock * _volumeBlockSizeRatio), + _volumeBlockSizeRatio, out byte[] data); + + if(errno != ErrorNumber.NoError) + break; + + header = Marshal.ByteArrayToStructureBigEndian(data); + nextBlock = header.next_block + firstBlock; + + int off = (int)header.first_used; + + var entry = new DirectoryEntry(); + + while(off + _directoryEntrySize < data.Length) { - entry = currentDirectory.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[p]); + entry = Marshal.ByteArrayToStructureBigEndian(data, off, _directoryEntrySize); + string name = StringHandlers.CToString(entry.name, Encoding); - if(string.IsNullOrEmpty(entry.Key)) - return ErrorNumber.NoSuchFile; + var entryWithPointers = new DirectoryEntryWithPointers + { + Entry = entry, + Pointers = new uint[entry.last_copy + 1] + }; - if((entry.Value.Entry.flags & FLAGS_MASK) != (int)FileFlags.Directory) - return ErrorNumber.NotDirectory; + for(int i = 0; i <= entry.last_copy; i++) + entryWithPointers.Pointers[i] = + BigEndianBitConverter.ToUInt32(data, off + _directoryEntrySize + (i * 4)); - currentPath = p == 0 ? pieces[0] : $"{currentPath}/{pieces[p]}"; + entries.Add(name, entryWithPointers); - if(_directoryCache.TryGetValue(currentPath, out currentDirectory)) - continue; + if((entry.flags & (uint)FileFlags.LastEntry) != 0 || + (entry.flags & (uint)FileFlags.LastEntryInBlock) != 0) + break; - if(entry.Value.Pointers.Length < 1) - return ErrorNumber.InvalidArgument; - - currentDirectory = DecodeDirectory((int)entry.Value.Pointers[0]); - - _directoryCache.Add(currentPath, currentDirectory); + off += (int)(_directoryEntrySize + ((entry.last_copy + 1) * 4)); } - contents = currentDirectory?.Keys.ToList(); + if((entry.flags & (uint)FileFlags.LastEntry) != 0) + break; + } while(header.next_block != -1); - return ErrorNumber.NoError; - } - - Dictionary DecodeDirectory(int firstBlock) - { - Dictionary entries = new(); - - int nextBlock = firstBlock; - - DirectoryHeader header; - - do - { - ErrorNumber errno = _image.ReadSectors((ulong)(nextBlock * _volumeBlockSizeRatio), - _volumeBlockSizeRatio, out byte[] data); - - if(errno != ErrorNumber.NoError) - break; - - header = Marshal.ByteArrayToStructureBigEndian(data); - nextBlock = header.next_block + firstBlock; - - int off = (int)header.first_used; - - var entry = new DirectoryEntry(); - - while(off + _directoryEntrySize < data.Length) - { - entry = Marshal.ByteArrayToStructureBigEndian(data, off, _directoryEntrySize); - string name = StringHandlers.CToString(entry.name, Encoding); - - var entryWithPointers = new DirectoryEntryWithPointers - { - Entry = entry, - Pointers = new uint[entry.last_copy + 1] - }; - - for(int i = 0; i <= entry.last_copy; i++) - entryWithPointers.Pointers[i] = - BigEndianBitConverter.ToUInt32(data, off + _directoryEntrySize + (i * 4)); - - entries.Add(name, entryWithPointers); - - if((entry.flags & (uint)FileFlags.LastEntry) != 0 || - (entry.flags & (uint)FileFlags.LastEntryInBlock) != 0) - break; - - off += (int)(_directoryEntrySize + ((entry.last_copy + 1) * 4)); - } - - if((entry.flags & (uint)FileFlags.LastEntry) != 0) - break; - } while(header.next_block != -1); - - return entries; - } + return entries; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Opera/File.cs b/Aaru.Filesystems/Opera/File.cs index a5b1a4f99..2a5d25561 100644 --- a/Aaru.Filesystems/Opera/File.cs +++ b/Aaru.Filesystems/Opera/File.cs @@ -37,189 +37,188 @@ using System.Linq; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class OperaFS { - public sealed partial class OperaFS + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) { - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + deviceBlock = 0; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = GetFileEntry(path, out DirectoryEntryWithPointers entry); + + if(err != ErrorNumber.NoError) + return err; + + if((entry.Entry.flags & FLAGS_MASK) == (uint)FileFlags.Directory && + !_debug) + return ErrorNumber.IsDirectory; + + deviceBlock = entry.Pointers[0] + fileBlock; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + { + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = Stat(path, out FileEntryInfo stat); + + if(err != ErrorNumber.NoError) + return err; + + attributes = stat.Attributes; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + buf = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = GetFileEntry(path, out DirectoryEntryWithPointers entry); + + if(err != ErrorNumber.NoError) + return err; + + if((entry.Entry.flags & FLAGS_MASK) == (uint)FileFlags.Directory && + !_debug) + return ErrorNumber.IsDirectory; + + if(entry.Pointers.Length < 1) + return ErrorNumber.InvalidArgument; + + if(entry.Entry.byte_count == 0) { - deviceBlock = 0; + buf = Array.Empty(); - if(!_mounted) - return ErrorNumber.AccessDenied; + return ErrorNumber.NoError; + } - ErrorNumber err = GetFileEntry(path, out DirectoryEntryWithPointers entry); + if(offset >= entry.Entry.byte_count) + return ErrorNumber.InvalidArgument; + + if(size + offset >= entry.Entry.byte_count) + size = entry.Entry.byte_count - offset; + + long firstBlock = offset / entry.Entry.block_size; + long offsetInBlock = offset % entry.Entry.block_size; + long sizeInBlocks = (size + offsetInBlock) / entry.Entry.block_size; + + if((size + offsetInBlock) % entry.Entry.block_size > 0) + sizeInBlocks++; + + uint fileBlockSizeRatio; + + if(_image.Info.SectorSize == 2336 || + _image.Info.SectorSize == 2352 || + _image.Info.SectorSize == 2448) + fileBlockSizeRatio = entry.Entry.block_size / 2048; + else + fileBlockSizeRatio = entry.Entry.block_size / _image.Info.SectorSize; + + ErrorNumber errno = _image.ReadSectors((ulong)(entry.Pointers[0] + (firstBlock * fileBlockSizeRatio)), + (uint)(sizeInBlocks * fileBlockSizeRatio), out byte[] buffer); + + if(errno != ErrorNumber.NoError) + return errno; + + buf = new byte[size]; + Array.Copy(buffer, offsetInBlock, buf, 0, size); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + ErrorNumber err = GetFileEntry(path, out DirectoryEntryWithPointers entryWithPointers); + + if(err != ErrorNumber.NoError) + return err; + + DirectoryEntry entry = entryWithPointers.Entry; + + stat = new FileEntryInfo + { + Attributes = new FileAttributes(), + Blocks = entry.block_count, + BlockSize = entry.block_size, + Length = entry.byte_count, + Inode = entry.id, + Links = (ulong)entryWithPointers.Pointers.Length + }; + + var flags = (FileFlags)(entry.flags & FLAGS_MASK); + + if(flags == FileFlags.Directory) + stat.Attributes |= FileAttributes.Directory; + + if(flags == FileFlags.Special) + stat.Attributes |= FileAttributes.Device; + + return ErrorNumber.NoError; + } + + ErrorNumber GetFileEntry(string path, out DirectoryEntryWithPointers entry) + { + entry = null; + + string cutPath = path.StartsWith("/", StringComparison.Ordinal) + ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) + : path.ToLower(CultureInfo.CurrentUICulture); + + string[] pieces = cutPath.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + if(pieces.Length == 0) + return ErrorNumber.InvalidArgument; + + string parentPath = string.Join("/", pieces, 0, pieces.Length - 1); + + if(!_directoryCache.TryGetValue(parentPath, out _)) + { + ErrorNumber err = ReadDir(parentPath, out _); if(err != ErrorNumber.NoError) return err; - - if((entry.Entry.flags & FLAGS_MASK) == (uint)FileFlags.Directory && - !_debug) - return ErrorNumber.IsDirectory; - - deviceBlock = entry.Pointers[0] + fileBlock; - - return ErrorNumber.NoError; } - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) - { - attributes = new FileAttributes(); + Dictionary parent; - if(!_mounted) - return ErrorNumber.AccessDenied; + if(pieces.Length == 1) + parent = _rootDirectoryCache; + else if(!_directoryCache.TryGetValue(parentPath, out parent)) + return ErrorNumber.InvalidArgument; - ErrorNumber err = Stat(path, out FileEntryInfo stat); + KeyValuePair dirent = + parent.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[^1]); - if(err != ErrorNumber.NoError) - return err; + if(string.IsNullOrEmpty(dirent.Key)) + return ErrorNumber.NoSuchFile; - attributes = stat.Attributes; + entry = dirent.Value; - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) - { - buf = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = GetFileEntry(path, out DirectoryEntryWithPointers entry); - - if(err != ErrorNumber.NoError) - return err; - - if((entry.Entry.flags & FLAGS_MASK) == (uint)FileFlags.Directory && - !_debug) - return ErrorNumber.IsDirectory; - - if(entry.Pointers.Length < 1) - return ErrorNumber.InvalidArgument; - - if(entry.Entry.byte_count == 0) - { - buf = Array.Empty(); - - return ErrorNumber.NoError; - } - - if(offset >= entry.Entry.byte_count) - return ErrorNumber.InvalidArgument; - - if(size + offset >= entry.Entry.byte_count) - size = entry.Entry.byte_count - offset; - - long firstBlock = offset / entry.Entry.block_size; - long offsetInBlock = offset % entry.Entry.block_size; - long sizeInBlocks = (size + offsetInBlock) / entry.Entry.block_size; - - if((size + offsetInBlock) % entry.Entry.block_size > 0) - sizeInBlocks++; - - uint fileBlockSizeRatio; - - if(_image.Info.SectorSize == 2336 || - _image.Info.SectorSize == 2352 || - _image.Info.SectorSize == 2448) - fileBlockSizeRatio = entry.Entry.block_size / 2048; - else - fileBlockSizeRatio = entry.Entry.block_size / _image.Info.SectorSize; - - ErrorNumber errno = _image.ReadSectors((ulong)(entry.Pointers[0] + (firstBlock * fileBlockSizeRatio)), - (uint)(sizeInBlocks * fileBlockSizeRatio), out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return errno; - - buf = new byte[size]; - Array.Copy(buffer, offsetInBlock, buf, 0, size); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) - { - stat = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - ErrorNumber err = GetFileEntry(path, out DirectoryEntryWithPointers entryWithPointers); - - if(err != ErrorNumber.NoError) - return err; - - DirectoryEntry entry = entryWithPointers.Entry; - - stat = new FileEntryInfo - { - Attributes = new FileAttributes(), - Blocks = entry.block_count, - BlockSize = entry.block_size, - Length = entry.byte_count, - Inode = entry.id, - Links = (ulong)entryWithPointers.Pointers.Length - }; - - var flags = (FileFlags)(entry.flags & FLAGS_MASK); - - if(flags == FileFlags.Directory) - stat.Attributes |= FileAttributes.Directory; - - if(flags == FileFlags.Special) - stat.Attributes |= FileAttributes.Device; - - return ErrorNumber.NoError; - } - - ErrorNumber GetFileEntry(string path, out DirectoryEntryWithPointers entry) - { - entry = null; - - string cutPath = path.StartsWith("/", StringComparison.Ordinal) - ? path.Substring(1).ToLower(CultureInfo.CurrentUICulture) - : path.ToLower(CultureInfo.CurrentUICulture); - - string[] pieces = cutPath.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pieces.Length == 0) - return ErrorNumber.InvalidArgument; - - string parentPath = string.Join("/", pieces, 0, pieces.Length - 1); - - if(!_directoryCache.TryGetValue(parentPath, out _)) - { - ErrorNumber err = ReadDir(parentPath, out _); - - if(err != ErrorNumber.NoError) - return err; - } - - Dictionary parent; - - if(pieces.Length == 1) - parent = _rootDirectoryCache; - else if(!_directoryCache.TryGetValue(parentPath, out parent)) - return ErrorNumber.InvalidArgument; - - KeyValuePair dirent = - parent.FirstOrDefault(t => t.Key.ToLower(CultureInfo.CurrentUICulture) == pieces[^1]); - - if(string.IsNullOrEmpty(dirent.Key)) - return ErrorNumber.NoSuchFile; - - entry = dirent.Value; - - return ErrorNumber.NoError; - } + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Opera/Info.cs b/Aaru.Filesystems/Opera/Info.cs index b099e2c85..ac62715a0 100644 --- a/Aaru.Filesystems/Opera/Info.cs +++ b/Aaru.Filesystems/Opera/Info.cs @@ -38,111 +38,110 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class OperaFS { - public sealed partial class OperaFS + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(2 + partition.Start >= partition.End) - return false; + if(2 + partition.Start >= partition.End) + return false; - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); - if(errno != ErrorNumber.NoError) - return false; + if(errno != ErrorNumber.NoError) + return false; - byte[] syncBytes = new byte[5]; + byte[] syncBytes = new byte[5]; - byte recordType = sbSector[0x000]; - Array.Copy(sbSector, 0x001, syncBytes, 0, 5); - byte recordVersion = sbSector[0x006]; + byte recordType = sbSector[0x000]; + Array.Copy(sbSector, 0x001, syncBytes, 0, 5); + byte recordVersion = sbSector[0x006]; - if(recordType != 1 || - recordVersion != 1) - return false; + if(recordType != 1 || + recordVersion != 1) + return false; - return Encoding.ASCII.GetString(syncBytes) == SYNC; - } + return Encoding.ASCII.GetString(syncBytes) == SYNC; + } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - // TODO: Find correct default encoding - Encoding = Encoding.ASCII; - information = ""; - var superBlockMetadata = new StringBuilder(); + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + // TODO: Find correct default encoding + Encoding = Encoding.ASCII; + information = ""; + var superBlockMetadata = new StringBuilder(); - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); - if(errno != ErrorNumber.NoError) - return; + if(errno != ErrorNumber.NoError) + return; - SuperBlock sb = Marshal.ByteArrayToStructureBigEndian(sbSector); + SuperBlock sb = Marshal.ByteArrayToStructureBigEndian(sbSector); - if(sb.record_type != 1 || - sb.record_version != 1) - return; + if(sb.record_type != 1 || + sb.record_version != 1) + return; - if(Encoding.ASCII.GetString(sb.sync_bytes) != SYNC) - return; + if(Encoding.ASCII.GetString(sb.sync_bytes) != SYNC) + return; - superBlockMetadata.AppendFormat("Opera filesystem disc.").AppendLine(); - - if(!string.IsNullOrEmpty(StringHandlers.CToString(sb.volume_label, Encoding))) - superBlockMetadata. - AppendFormat("Volume label: {0}", StringHandlers.CToString(sb.volume_label, Encoding)).AppendLine(); - - if(!string.IsNullOrEmpty(StringHandlers.CToString(sb.volume_comment, Encoding))) - superBlockMetadata. - AppendFormat("Volume comment: {0}", StringHandlers.CToString(sb.volume_comment, Encoding)). - AppendLine(); - - superBlockMetadata.AppendFormat("Volume identifier: 0x{0:X8}", sb.volume_id).AppendLine(); - superBlockMetadata.AppendFormat("Block size: {0} bytes", sb.block_size).AppendLine(); - - if(imagePlugin.Info.SectorSize == 2336 || - imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448) - { - if(sb.block_size != 2048) - superBlockMetadata. - AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/block", - sb.block_size, 2048); - } - else if(imagePlugin.Info.SectorSize != sb.block_size) - superBlockMetadata. - AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/block", - sb.block_size, imagePlugin.Info.SectorSize); + superBlockMetadata.AppendFormat("Opera filesystem disc.").AppendLine(); + if(!string.IsNullOrEmpty(StringHandlers.CToString(sb.volume_label, Encoding))) superBlockMetadata. - AppendFormat("Volume size: {0} blocks, {1} bytes", sb.block_count, sb.block_size * sb.block_count). + AppendFormat("Volume label: {0}", StringHandlers.CToString(sb.volume_label, Encoding)).AppendLine(); + + if(!string.IsNullOrEmpty(StringHandlers.CToString(sb.volume_comment, Encoding))) + superBlockMetadata. + AppendFormat("Volume comment: {0}", StringHandlers.CToString(sb.volume_comment, Encoding)). AppendLine(); - if(sb.block_count > imagePlugin.Info.Sectors) + superBlockMetadata.AppendFormat("Volume identifier: 0x{0:X8}", sb.volume_id).AppendLine(); + superBlockMetadata.AppendFormat("Block size: {0} bytes", sb.block_size).AppendLine(); + + if(imagePlugin.Info.SectorSize == 2336 || + imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448) + { + if(sb.block_size != 2048) superBlockMetadata. - AppendFormat("WARNING: Filesystem indicates {0} blocks while device indicates {1} blocks", - sb.block_count, imagePlugin.Info.Sectors); - - superBlockMetadata.AppendFormat("Root directory identifier: 0x{0:X8}", sb.root_dirid).AppendLine(); - superBlockMetadata.AppendFormat("Root directory block size: {0} bytes", sb.rootdir_bsize).AppendLine(); - - superBlockMetadata.AppendFormat("Root directory size: {0} blocks, {1} bytes", sb.rootdir_blocks, - sb.rootdir_bsize * sb.rootdir_blocks).AppendLine(); - - superBlockMetadata.AppendFormat("Last root directory copy: {0}", sb.last_root_copy).AppendLine(); - - information = superBlockMetadata.ToString(); - - XmlFsType = new FileSystemType - { - Type = "Opera", - VolumeName = StringHandlers.CToString(sb.volume_label, Encoding), - ClusterSize = sb.block_size, - Clusters = sb.block_count - }; + AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/block", + sb.block_size, 2048); } + else if(imagePlugin.Info.SectorSize != sb.block_size) + superBlockMetadata. + AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/block", + sb.block_size, imagePlugin.Info.SectorSize); + + superBlockMetadata. + AppendFormat("Volume size: {0} blocks, {1} bytes", sb.block_count, sb.block_size * sb.block_count). + AppendLine(); + + if(sb.block_count > imagePlugin.Info.Sectors) + superBlockMetadata. + AppendFormat("WARNING: Filesystem indicates {0} blocks while device indicates {1} blocks", + sb.block_count, imagePlugin.Info.Sectors); + + superBlockMetadata.AppendFormat("Root directory identifier: 0x{0:X8}", sb.root_dirid).AppendLine(); + superBlockMetadata.AppendFormat("Root directory block size: {0} bytes", sb.rootdir_bsize).AppendLine(); + + superBlockMetadata.AppendFormat("Root directory size: {0} blocks, {1} bytes", sb.rootdir_blocks, + sb.rootdir_bsize * sb.rootdir_blocks).AppendLine(); + + superBlockMetadata.AppendFormat("Last root directory copy: {0}", sb.last_root_copy).AppendLine(); + + information = superBlockMetadata.ToString(); + + XmlFsType = new FileSystemType + { + Type = "Opera", + VolumeName = StringHandlers.CToString(sb.volume_label, Encoding), + ClusterSize = sb.block_size, + Clusters = sb.block_count + }; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Opera/Opera.cs b/Aaru.Filesystems/Opera/Opera.cs index 7cc8688c8..ae5b4b8af 100644 --- a/Aaru.Filesystems/Opera/Opera.cs +++ b/Aaru.Filesystems/Opera/Opera.cs @@ -38,63 +38,62 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements the 3DO Opera filesystem +public sealed partial class OperaFS : IReadOnlyFilesystem { + bool _debug; + Dictionary> _directoryCache; + IMediaImage _image; + bool _mounted; + Dictionary _rootDirectoryCache; + FileSystemInfo _statfs; + uint _volumeBlockSizeRatio; + /// - /// Implements the 3DO Opera filesystem - public sealed partial class OperaFS : IReadOnlyFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Opera Filesystem Plugin"; + /// + public Guid Id => new("0ec84ec7-eae6-4196-83fe-943b3fe46dbd"); + /// + public string Author => "Natalia Portillo"; + + /// + public ErrorNumber ListXAttr(string path, out List xattrs) { - bool _debug; - Dictionary> _directoryCache; - IMediaImage _image; - bool _mounted; - Dictionary _rootDirectoryCache; - FileSystemInfo _statfs; - uint _volumeBlockSizeRatio; + xattrs = null; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Opera Filesystem Plugin"; - /// - public Guid Id => new("0ec84ec7-eae6-4196-83fe-943b3fe46dbd"); - /// - public string Author => "Natalia Portillo"; - - /// - public ErrorNumber ListXAttr(string path, out List xattrs) - { - xattrs = null; - - return ErrorNumber.NotSupported; - } - - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) => ErrorNumber.NotSupported; - - /// - public ErrorNumber ReadLink(string path, out string dest) - { - dest = null; - - return ErrorNumber.NotSupported; - } - - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - {}; - - /// - public Dictionary Namespaces => null; - - static Dictionary GetDefaultOptions() => new() - { - { - "debug", false.ToString() - } - }; + return ErrorNumber.NotSupported; } + + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) => ErrorNumber.NotSupported; + + /// + public ErrorNumber ReadLink(string path, out string dest) + { + dest = null; + + return ErrorNumber.NotSupported; + } + + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + {}; + + /// + public Dictionary Namespaces => null; + + static Dictionary GetDefaultOptions() => new() + { + { + "debug", false.ToString() + } + }; } \ No newline at end of file diff --git a/Aaru.Filesystems/Opera/Structs.cs b/Aaru.Filesystems/Opera/Structs.cs index 3a04400da..122277aed 100644 --- a/Aaru.Filesystems/Opera/Structs.cs +++ b/Aaru.Filesystems/Opera/Structs.cs @@ -32,89 +32,88 @@ using System.Runtime.InteropServices; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class OperaFS { - public sealed partial class OperaFS + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock { - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - /// 0x000, Record type, must be 1 - public readonly byte record_type; - /// 0x001, 5 bytes, "ZZZZZ" - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] sync_bytes; - /// 0x006, Record version, must be 1 - public readonly byte record_version; - /// 0x007, Volume flags - public readonly byte volume_flags; - /// 0x008, 32 bytes, volume comment - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_NAME)] - public readonly byte[] volume_comment; - /// 0x028, 32 bytes, volume label - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_NAME)] - public readonly byte[] volume_label; - /// 0x048, Volume ID - public readonly uint volume_id; - /// 0x04C, Block size in bytes - public readonly uint block_size; - /// 0x050, Blocks in volume - public readonly uint block_count; - /// 0x054, Root directory ID - public readonly uint root_dirid; - /// 0x058, Root directory blocks - public readonly uint rootdir_blocks; - /// 0x05C, Root directory block size - public readonly uint rootdir_bsize; - /// 0x060, Last root directory copy - public readonly uint last_root_copy; - } + /// 0x000, Record type, must be 1 + public readonly byte record_type; + /// 0x001, 5 bytes, "ZZZZZ" + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] sync_bytes; + /// 0x006, Record version, must be 1 + public readonly byte record_version; + /// 0x007, Volume flags + public readonly byte volume_flags; + /// 0x008, 32 bytes, volume comment + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_NAME)] + public readonly byte[] volume_comment; + /// 0x028, 32 bytes, volume label + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_NAME)] + public readonly byte[] volume_label; + /// 0x048, Volume ID + public readonly uint volume_id; + /// 0x04C, Block size in bytes + public readonly uint block_size; + /// 0x050, Blocks in volume + public readonly uint block_count; + /// 0x054, Root directory ID + public readonly uint root_dirid; + /// 0x058, Root directory blocks + public readonly uint rootdir_blocks; + /// 0x05C, Root directory block size + public readonly uint rootdir_bsize; + /// 0x060, Last root directory copy + public readonly uint last_root_copy; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryHeader - { - /// Next block from this directory, -1 if last - public readonly int next_block; - /// Previous block from this directory, -1 if first - public readonly int prev_block; - /// Directory flags - public readonly uint flags; - /// Offset to first free unused byte in the directory - public readonly uint first_free; - /// Offset to first directory entry - public readonly uint first_used; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryHeader + { + /// Next block from this directory, -1 if last + public readonly int next_block; + /// Previous block from this directory, -1 if first + public readonly int prev_block; + /// Directory flags + public readonly uint flags; + /// Offset to first free unused byte in the directory + public readonly uint first_free; + /// Offset to first directory entry + public readonly uint first_used; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryEntry - { - /// File flags, see - public readonly uint flags; - /// Unique file identifier - public readonly uint id; - /// Entry type - public readonly uint type; - /// Block size - public readonly uint block_size; - /// Size in bytes - public readonly uint byte_count; - /// Block count - public readonly uint block_count; - /// Unknown - public readonly uint burst; - /// Unknown - public readonly uint gap; - /// Filename - [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_NAME)] - public readonly byte[] name; - /// Last copy - public readonly uint last_copy; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryEntry + { + /// File flags, see + public readonly uint flags; + /// Unique file identifier + public readonly uint id; + /// Entry type + public readonly uint type; + /// Block size + public readonly uint block_size; + /// Size in bytes + public readonly uint byte_count; + /// Block count + public readonly uint block_count; + /// Unknown + public readonly uint burst; + /// Unknown + public readonly uint gap; + /// Filename + [MarshalAs(UnmanagedType.ByValArray, SizeConst = MAX_NAME)] + public readonly byte[] name; + /// Last copy + public readonly uint last_copy; + } - class DirectoryEntryWithPointers - { - public DirectoryEntry Entry; - public uint[] Pointers; - } + class DirectoryEntryWithPointers + { + public DirectoryEntry Entry; + public uint[] Pointers; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Opera/Super.cs b/Aaru.Filesystems/Opera/Super.cs index dd6af6b50..76e60b125 100644 --- a/Aaru.Filesystems/Opera/Super.cs +++ b/Aaru.Filesystems/Opera/Super.cs @@ -39,98 +39,97 @@ using Aaru.CommonTypes.Structs; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +public sealed partial class OperaFS { - public sealed partial class OperaFS + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + // TODO: Find correct default encoding + Encoding = Encoding.ASCII; + + options ??= GetDefaultOptions(); + + if(options.TryGetValue("debug", out string debugString)) + bool.TryParse(debugString, out _debug); + + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); + + if(errno != ErrorNumber.NoError) + return errno; + + SuperBlock sb = Marshal.ByteArrayToStructureBigEndian(sbSector); + + if(sb.record_type != 1 || + sb.record_version != 1) + return ErrorNumber.InvalidArgument; + + if(Encoding.ASCII.GetString(sb.sync_bytes) != SYNC) + return ErrorNumber.InvalidArgument; + + if(imagePlugin.Info.SectorSize == 2336 || + imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448) + _volumeBlockSizeRatio = sb.block_size / 2048; + else + _volumeBlockSizeRatio = sb.block_size / imagePlugin.Info.SectorSize; + + XmlFsType = new FileSystemType { - // TODO: Find correct default encoding - Encoding = Encoding.ASCII; + Type = "Opera", + VolumeName = StringHandlers.CToString(sb.volume_label, Encoding), + ClusterSize = sb.block_size, + Clusters = sb.block_count, + Bootable = true, + VolumeSerial = $"{sb.volume_id:X8}" + }; - options ??= GetDefaultOptions(); - - if(options.TryGetValue("debug", out string debugString)) - bool.TryParse(debugString, out _debug); - - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] sbSector); - - if(errno != ErrorNumber.NoError) - return errno; - - SuperBlock sb = Marshal.ByteArrayToStructureBigEndian(sbSector); - - if(sb.record_type != 1 || - sb.record_version != 1) - return ErrorNumber.InvalidArgument; - - if(Encoding.ASCII.GetString(sb.sync_bytes) != SYNC) - return ErrorNumber.InvalidArgument; - - if(imagePlugin.Info.SectorSize == 2336 || - imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448) - _volumeBlockSizeRatio = sb.block_size / 2048; - else - _volumeBlockSizeRatio = sb.block_size / imagePlugin.Info.SectorSize; - - XmlFsType = new FileSystemType + _statfs = new FileSystemInfo + { + Blocks = sb.block_count, + FilenameLength = MAX_NAME, + FreeBlocks = 0, + Id = new FileSystemId { - Type = "Opera", - VolumeName = StringHandlers.CToString(sb.volume_label, Encoding), - ClusterSize = sb.block_size, - Clusters = sb.block_count, - Bootable = true, - VolumeSerial = $"{sb.volume_id:X8}" - }; + IsInt = true, + Serial32 = sb.volume_id + }, + PluginId = Id, + Type = "Opera" + }; - _statfs = new FileSystemInfo - { - Blocks = sb.block_count, - FilenameLength = MAX_NAME, - FreeBlocks = 0, - Id = new FileSystemId - { - IsInt = true, - Serial32 = sb.volume_id - }, - PluginId = Id, - Type = "Opera" - }; + _image = imagePlugin; + int firstRootBlock = BigEndianBitConverter.ToInt32(sbSector, Marshal.SizeOf()); + _rootDirectoryCache = DecodeDirectory(firstRootBlock); + _directoryCache = new Dictionary>(); + _mounted = true; - _image = imagePlugin; - int firstRootBlock = BigEndianBitConverter.ToInt32(sbSector, Marshal.SizeOf()); - _rootDirectoryCache = DecodeDirectory(firstRootBlock); - _directoryCache = new Dictionary>(); - _mounted = true; + return ErrorNumber.NoError; + } - return ErrorNumber.NoError; - } + /// + public ErrorNumber Unmount() + { + if(!_mounted) + return ErrorNumber.AccessDenied; - /// - public ErrorNumber Unmount() - { - if(!_mounted) - return ErrorNumber.AccessDenied; + _mounted = false; - _mounted = false; + return ErrorNumber.NoError; + } - return ErrorNumber.NoError; - } + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = null; - /// - public ErrorNumber StatFs(out FileSystemInfo stat) - { - stat = null; + if(!_mounted) + return ErrorNumber.AccessDenied; - if(!_mounted) - return ErrorNumber.AccessDenied; + stat = _statfs.ShallowCopy(); - stat = _statfs.ShallowCopy(); - - return ErrorNumber.NoError; - } + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/PCEngine.cs b/Aaru.Filesystems/PCEngine.cs index 261d9ca00..3c83f7c30 100644 --- a/Aaru.Filesystems/PCEngine.cs +++ b/Aaru.Filesystems/PCEngine.cs @@ -37,53 +37,52 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the PC-Engine CD file headers +public sealed class PCEnginePlugin : IFilesystem { /// - /// Implements detection of the PC-Engine CD file headers - public sealed class PCEnginePlugin : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "PC Engine CD Plugin"; + /// + public Guid Id => new("e5ee6d7c-90fa-49bd-ac89-14ef750b8af3"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "PC Engine CD Plugin"; - /// - public Guid Id => new("e5ee6d7c-90fa-49bd-ac89-14ef750b8af3"); - /// - public string Author => "Natalia Portillo"; + if(2 + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + byte[] systemDescriptor = new byte[23]; + ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + Array.Copy(sector, 0x20, systemDescriptor, 0, 23); + + return Encoding.ASCII.GetString(systemDescriptor) == "PC Engine CD-ROM SYSTEM"; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("shift_jis"); + information = ""; + + XmlFsType = new FileSystemType { - if(2 + partition.Start >= partition.End) - return false; - - byte[] systemDescriptor = new byte[23]; - ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - Array.Copy(sector, 0x20, systemDescriptor, 0, 23); - - return Encoding.ASCII.GetString(systemDescriptor) == "PC Engine CD-ROM SYSTEM"; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("shift_jis"); - information = ""; - - XmlFsType = new FileSystemType - { - Type = "PC Engine filesystem", - Clusters = (partition.End - partition.Start + 1) / imagePlugin.Info.SectorSize * 2048, - ClusterSize = 2048 - }; - } + Type = "PC Engine filesystem", + Clusters = (partition.End - partition.Start + 1) / imagePlugin.Info.SectorSize * 2048, + ClusterSize = 2048 + }; } } \ No newline at end of file diff --git a/Aaru.Filesystems/PCFX.cs b/Aaru.Filesystems/PCFX.cs index ee237671a..963cf8f38 100644 --- a/Aaru.Filesystems/PCFX.cs +++ b/Aaru.Filesystems/PCFX.cs @@ -40,133 +40,132 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Not a filesystem, more like an executable header +/// +/// Implements detection of NEC PC-FX headers +public sealed class PCFX : IFilesystem { - // Not a filesystem, more like an executable header + const string IDENTIFIER = "PC-FX:Hu_CD-ROM "; /// - /// Implements detection of NEC PC-FX headers - public sealed class PCFX : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "PC-FX Plugin"; + /// + public Guid Id => new("8BC27CCE-D9E9-48F8-BA93-C66A86EB565A"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const string IDENTIFIER = "PC-FX:Hu_CD-ROM "; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "PC-FX Plugin"; - /// - public Guid Id => new("8BC27CCE-D9E9-48F8-BA93-C66A86EB565A"); - /// - public string Author => "Natalia Portillo"; + if(2 + partition.Start >= partition.End || + imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + var encoding = Encoding.GetEncoding("shift_jis"); + + return encoding.GetString(sector, 0, 16) == IDENTIFIER; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + // Always Shift-JIS + Encoding = Encoding.GetEncoding("shift_jis"); + information = ""; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + Header header = Marshal.ByteArrayToStructureLittleEndian
(sector); + + string date; + DateTime dateTime = DateTime.MinValue; + + try { - if(2 + partition.Start >= partition.End || - imagePlugin.Info.XmlMediaType != XmlMediaType.OpticalDisc) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - var encoding = Encoding.GetEncoding("shift_jis"); - - return encoding.GetString(sector, 0, 16) == IDENTIFIER; + date = Encoding.GetString(header.date); + int year = int.Parse(date.Substring(0, 4)); + int month = int.Parse(date.Substring(4, 2)); + int day = int.Parse(date.Substring(6, 2)); + dateTime = new DateTime(year, month, day); + } + catch + { + date = null; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + var sb = new StringBuilder(); + sb.AppendLine("PC-FX executable:"); + sb.AppendFormat("Identifier: {0}", StringHandlers.CToString(header.signature, Encoding)).AppendLine(); + sb.AppendFormat("Copyright: {0}", StringHandlers.CToString(header.copyright, Encoding)).AppendLine(); + sb.AppendFormat("Title: {0}", StringHandlers.CToString(header.title, Encoding)).AppendLine(); + sb.AppendFormat("Maker ID: {0}", StringHandlers.CToString(header.makerId, Encoding)).AppendLine(); + sb.AppendFormat("Maker name: {0}", StringHandlers.CToString(header.makerName, Encoding)).AppendLine(); + sb.AppendFormat("Volume number: {0}", header.volumeNumber).AppendLine(); + sb.AppendFormat("Country code: {0}", header.country).AppendLine(); + sb.AppendFormat("Version: {0}.{1}", header.minorVersion, header.majorVersion).AppendLine(); + + if(date != null) + sb.AppendFormat("Dated {0}", dateTime).AppendLine(); + + sb.AppendFormat("Load {0} sectors from sector {1}", header.loadCount, header.loadOffset).AppendLine(); + + sb.AppendFormat("Load at 0x{0:X8} and jump to 0x{1:X8}", header.loadAddress, header.entryPoint). + AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - // Always Shift-JIS - Encoding = Encoding.GetEncoding("shift_jis"); - information = ""; + Type = "PC-FX", + Clusters = partition.Length, + ClusterSize = 2048, + Bootable = true, + CreationDate = dateTime, + CreationDateSpecified = date != null, + PublisherIdentifier = StringHandlers.CToString(header.makerName, Encoding), + VolumeName = StringHandlers.CToString(header.title, Encoding), + SystemIdentifier = "PC-FX" + }; + } - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - Header header = Marshal.ByteArrayToStructureLittleEndian
(sector); - - string date; - DateTime dateTime = DateTime.MinValue; - - try - { - date = Encoding.GetString(header.date); - int year = int.Parse(date.Substring(0, 4)); - int month = int.Parse(date.Substring(4, 2)); - int day = int.Parse(date.Substring(6, 2)); - dateTime = new DateTime(year, month, day); - } - catch - { - date = null; - } - - var sb = new StringBuilder(); - sb.AppendLine("PC-FX executable:"); - sb.AppendFormat("Identifier: {0}", StringHandlers.CToString(header.signature, Encoding)).AppendLine(); - sb.AppendFormat("Copyright: {0}", StringHandlers.CToString(header.copyright, Encoding)).AppendLine(); - sb.AppendFormat("Title: {0}", StringHandlers.CToString(header.title, Encoding)).AppendLine(); - sb.AppendFormat("Maker ID: {0}", StringHandlers.CToString(header.makerId, Encoding)).AppendLine(); - sb.AppendFormat("Maker name: {0}", StringHandlers.CToString(header.makerName, Encoding)).AppendLine(); - sb.AppendFormat("Volume number: {0}", header.volumeNumber).AppendLine(); - sb.AppendFormat("Country code: {0}", header.country).AppendLine(); - sb.AppendFormat("Version: {0}.{1}", header.minorVersion, header.majorVersion).AppendLine(); - - if(date != null) - sb.AppendFormat("Dated {0}", dateTime).AppendLine(); - - sb.AppendFormat("Load {0} sectors from sector {1}", header.loadCount, header.loadOffset).AppendLine(); - - sb.AppendFormat("Load at 0x{0:X8} and jump to 0x{1:X8}", header.loadAddress, header.entryPoint). - AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "PC-FX", - Clusters = partition.Length, - ClusterSize = 2048, - Bootable = true, - CreationDate = dateTime, - CreationDateSpecified = date != null, - PublisherIdentifier = StringHandlers.CToString(header.makerName, Encoding), - VolumeName = StringHandlers.CToString(header.title, Encoding), - SystemIdentifier = "PC-FX" - }; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Header - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] signature; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xE0)] - public readonly byte[] copyright; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x710)] - public readonly byte[] unknown; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] title; - public readonly uint loadOffset; - public readonly uint loadCount; - public readonly uint loadAddress; - public readonly uint entryPoint; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] makerId; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)] - public readonly byte[] makerName; - public readonly uint volumeNumber; - public readonly byte majorVersion; - public readonly byte minorVersion; - public readonly ushort country; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] date; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Header + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] signature; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0xE0)] + public readonly byte[] copyright; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x710)] + public readonly byte[] unknown; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] title; + public readonly uint loadOffset; + public readonly uint loadCount; + public readonly uint loadAddress; + public readonly uint entryPoint; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] makerId; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)] + public readonly byte[] makerName; + public readonly uint volumeNumber; + public readonly byte majorVersion; + public readonly byte minorVersion; + public readonly ushort country; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] date; } } \ No newline at end of file diff --git a/Aaru.Filesystems/PFS.cs b/Aaru.Filesystems/PFS.cs index e5da8b54d..b47b2ae43 100644 --- a/Aaru.Filesystems/PFS.cs +++ b/Aaru.Filesystems/PFS.cs @@ -42,175 +42,174 @@ using Marshal = Aaru.Helpers.Marshal; // ReSharper disable UnusedType.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Professional File System +public sealed class PFS : IFilesystem { + /// Identifier for AFS (PFS v1) + const uint AFS_DISK = 0x41465301; + /// Identifier for PFS v2 + const uint PFS2_DISK = 0x50465302; + /// Identifier for PFS v3 + const uint PFS_DISK = 0x50465301; + /// Identifier for multi-user AFS + const uint MUAF_DISK = 0x6D754146; + /// Identifier for multi-user PFS + const uint MUPFS_DISK = 0x6D755046; + /// - /// Implements detection of the Professional File System - public sealed class PFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Professional File System"; + /// + public Guid Id => new("68DE769E-D957-406A-8AE4-3781CA8CDA77"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Identifier for AFS (PFS v1) - const uint AFS_DISK = 0x41465301; - /// Identifier for PFS v2 - const uint PFS2_DISK = 0x50465302; - /// Identifier for PFS v3 - const uint PFS_DISK = 0x50465301; - /// Identifier for multi-user AFS - const uint MUAF_DISK = 0x6D754146; - /// Identifier for multi-user PFS - const uint MUPFS_DISK = 0x6D755046; + if(partition.Length < 3) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Professional File System"; - /// - public Guid Id => new("68DE769E-D957-406A-8AE4-3781CA8CDA77"); - /// - public string Author => "Natalia Portillo"; + ErrorNumber errno = imagePlugin.ReadSector(2 + partition.Start, out byte[] sector); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(errno != ErrorNumber.NoError) + return false; + + uint magic = BigEndianBitConverter.ToUInt32(sector, 0x00); + + return magic == AFS_DISK || magic == PFS2_DISK || magic == PFS_DISK || magic == MUAF_DISK || + magic == MUPFS_DISK; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + information = ""; + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); + ErrorNumber errno = imagePlugin.ReadSector(2 + partition.Start, out byte[] rootBlockSector); + + if(errno != ErrorNumber.NoError) + return; + + RootBlock rootBlock = Marshal.ByteArrayToStructureBigEndian(rootBlockSector); + + var sbInformation = new StringBuilder(); + XmlFsType = new FileSystemType(); + + switch(rootBlock.diskType) { - if(partition.Length < 3) - return false; + case AFS_DISK: + case MUAF_DISK: + sbInformation.Append("Professional File System v1"); + XmlFsType.Type = "PFS v1"; - ErrorNumber errno = imagePlugin.ReadSector(2 + partition.Start, out byte[] sector); + break; + case PFS2_DISK: + sbInformation.Append("Professional File System v2"); + XmlFsType.Type = "PFS v2"; - if(errno != ErrorNumber.NoError) - return false; + break; + case PFS_DISK: + case MUPFS_DISK: + sbInformation.Append("Professional File System v3"); + XmlFsType.Type = "PFS v3"; - uint magic = BigEndianBitConverter.ToUInt32(sector, 0x00); - - return magic == AFS_DISK || magic == PFS2_DISK || magic == PFS_DISK || magic == MUAF_DISK || - magic == MUPFS_DISK; + break; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - information = ""; - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); - ErrorNumber errno = imagePlugin.ReadSector(2 + partition.Start, out byte[] rootBlockSector); + if(rootBlock.diskType == MUAF_DISK || + rootBlock.diskType == MUPFS_DISK) + sbInformation.Append(", with multi-user support"); - if(errno != ErrorNumber.NoError) - return; + sbInformation.AppendLine(); - RootBlock rootBlock = Marshal.ByteArrayToStructureBigEndian(rootBlockSector); + sbInformation.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(rootBlock.diskname, Encoding)). + AppendLine(); - var sbInformation = new StringBuilder(); - XmlFsType = new FileSystemType(); + sbInformation.AppendFormat("Volume has {0} free sectors of {1}", rootBlock.blocksfree, rootBlock.diskSize). + AppendLine(); - switch(rootBlock.diskType) - { - case AFS_DISK: - case MUAF_DISK: - sbInformation.Append("Professional File System v1"); - XmlFsType.Type = "PFS v1"; + sbInformation.AppendFormat("Volume created on {0}", + DateHandlers.AmigaToDateTime(rootBlock.creationday, rootBlock.creationminute, + rootBlock.creationtick)).AppendLine(); - break; - case PFS2_DISK: - sbInformation.Append("Professional File System v2"); - XmlFsType.Type = "PFS v2"; - - break; - case PFS_DISK: - case MUPFS_DISK: - sbInformation.Append("Professional File System v3"); - XmlFsType.Type = "PFS v3"; - - break; - } - - if(rootBlock.diskType == MUAF_DISK || - rootBlock.diskType == MUPFS_DISK) - sbInformation.Append(", with multi-user support"); - - sbInformation.AppendLine(); - - sbInformation.AppendFormat("Volume name: {0}", StringHandlers.PascalToString(rootBlock.diskname, Encoding)). + if(rootBlock.extension > 0) + sbInformation.AppendFormat("Root block extension resides at block {0}", rootBlock.extension). AppendLine(); - sbInformation.AppendFormat("Volume has {0} free sectors of {1}", rootBlock.blocksfree, rootBlock.diskSize). - AppendLine(); + information = sbInformation.ToString(); - sbInformation.AppendFormat("Volume created on {0}", - DateHandlers.AmigaToDateTime(rootBlock.creationday, rootBlock.creationminute, - rootBlock.creationtick)).AppendLine(); + XmlFsType.CreationDate = + DateHandlers.AmigaToDateTime(rootBlock.creationday, rootBlock.creationminute, rootBlock.creationtick); - if(rootBlock.extension > 0) - sbInformation.AppendFormat("Root block extension resides at block {0}", rootBlock.extension). - AppendLine(); + XmlFsType.CreationDateSpecified = true; + XmlFsType.FreeClusters = rootBlock.blocksfree; + XmlFsType.FreeClustersSpecified = true; + XmlFsType.Clusters = rootBlock.diskSize; + XmlFsType.ClusterSize = imagePlugin.Info.SectorSize; + XmlFsType.VolumeName = StringHandlers.PascalToString(rootBlock.diskname, Encoding); + } - information = sbInformation.ToString(); + /// Boot block, first 2 sectors + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct BootBlock + { + /// "PFS\1" disk type + public readonly uint diskType; + /// Boot code, til completion + public readonly byte[] bootCode; + } - XmlFsType.CreationDate = - DateHandlers.AmigaToDateTime(rootBlock.creationday, rootBlock.creationminute, rootBlock.creationtick); - - XmlFsType.CreationDateSpecified = true; - XmlFsType.FreeClusters = rootBlock.blocksfree; - XmlFsType.FreeClustersSpecified = true; - XmlFsType.Clusters = rootBlock.diskSize; - XmlFsType.ClusterSize = imagePlugin.Info.SectorSize; - XmlFsType.VolumeName = StringHandlers.PascalToString(rootBlock.diskname, Encoding); - } - - /// Boot block, first 2 sectors - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct BootBlock - { - /// "PFS\1" disk type - public readonly uint diskType; - /// Boot code, til completion - public readonly byte[] bootCode; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct RootBlock - { - /// Disk type - public readonly uint diskType; - /// Options - public readonly uint options; - /// Current datestamp - public readonly uint datestamp; - /// Volume creation day - public readonly ushort creationday; - /// Volume creation minute - public readonly ushort creationminute; - /// Volume creation tick - public readonly ushort creationtick; - /// AmigaDOS protection bits - public readonly ushort protection; - /// Volume label (Pascal string) - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] diskname; - /// Last reserved block - public readonly uint lastreserved; - /// First reserved block - public readonly uint firstreserved; - /// Free reserved blocks - public readonly uint reservedfree; - /// Size of reserved blocks in bytes - public readonly ushort reservedblocksize; - /// Blocks in rootblock, including bitmap - public readonly ushort rootblockclusters; - /// Free blocks - public readonly uint blocksfree; - /// Blocks that must be always free - public readonly uint alwaysfree; - /// Current bitmapfield number for allocation - public readonly uint rovingPointer; - /// Pointer to deldir - public readonly uint delDirPtr; - /// Disk size in sectors - public readonly uint diskSize; - /// Rootblock extension - public readonly uint extension; - /// Unused - public readonly uint unused; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct RootBlock + { + /// Disk type + public readonly uint diskType; + /// Options + public readonly uint options; + /// Current datestamp + public readonly uint datestamp; + /// Volume creation day + public readonly ushort creationday; + /// Volume creation minute + public readonly ushort creationminute; + /// Volume creation tick + public readonly ushort creationtick; + /// AmigaDOS protection bits + public readonly ushort protection; + /// Volume label (Pascal string) + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] diskname; + /// Last reserved block + public readonly uint lastreserved; + /// First reserved block + public readonly uint firstreserved; + /// Free reserved blocks + public readonly uint reservedfree; + /// Size of reserved blocks in bytes + public readonly ushort reservedblocksize; + /// Blocks in rootblock, including bitmap + public readonly ushort rootblockclusters; + /// Free blocks + public readonly uint blocksfree; + /// Blocks that must be always free + public readonly uint alwaysfree; + /// Current bitmapfield number for allocation + public readonly uint rovingPointer; + /// Pointer to deldir + public readonly uint delDirPtr; + /// Disk size in sectors + public readonly uint diskSize; + /// Rootblock extension + public readonly uint extension; + /// Unused + public readonly uint unused; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ProDOS.cs b/Aaru.Filesystems/ProDOS.cs index be5afd2c7..3ab9d4e49 100644 --- a/Aaru.Filesystems/ProDOS.cs +++ b/Aaru.Filesystems/ProDOS.cs @@ -44,474 +44,473 @@ using Encoding = System.Text.Encoding; // ReSharper disable NotAccessedField.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from Apple ProDOS 8 Technical Reference +/// +/// Implements detection of Apple ProDOS filesystem +[SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed class ProDOSPlugin : IFilesystem { - // Information from Apple ProDOS 8 Technical Reference + const byte EMPTY_STORAGE_TYPE = 0x00; + /// A file that occupies one block or less + const byte SEEDLING_FILE_TYPE = 0x01; + /// A file that occupies between 2 and 256 blocks + const byte SAPLING_FILE_TYPE = 0x02; + /// A file that occupies between 257 and 32768 blocks + const byte TREE_FILE_TYPE = 0x03; + const byte PASCAL_AREA_TYPE = 0x04; + const byte SUBDIRECTORY_TYPE = 0x0D; + const byte SUBDIRECTORY_HEADER_TYPE = 0x0E; + const byte ROOT_DIRECTORY_TYPE = 0x0F; + + const byte VERSION1 = 0x00; + + const uint YEAR_MASK = 0xFE000000; + const uint MONTH_MASK = 0x1E00000; + const uint DAY_MASK = 0x1F0000; + const uint HOUR_MASK = 0x1F00; + const uint MINUTE_MASK = 0x3F; + + const byte DESTROY_ATTRIBUTE = 0x80; + const byte RENAME_ATTRIBUTE = 0x40; + const byte BACKUP_ATTRIBUTE = 0x20; + const byte WRITE_ATTRIBUTE = 0x02; + const byte READ_ATTRIBUTE = 0x01; + const byte RESERVED_ATTRIBUTE_MASK = 0x1C; + + const byte STORAGE_TYPE_MASK = 0xF0; + const byte NAME_LENGTH_MASK = 0x0F; + const byte ENTRY_LENGTH = 0x27; + const byte ENTRIES_PER_BLOCK = 0x0D; + /// - /// Implements detection of Apple ProDOS filesystem - [SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed class ProDOSPlugin : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Apple ProDOS filesystem"; + /// + public Guid Id => new("43874265-7B8A-4739-BCF7-07F80D5932BF"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const byte EMPTY_STORAGE_TYPE = 0x00; - /// A file that occupies one block or less - const byte SEEDLING_FILE_TYPE = 0x01; - /// A file that occupies between 2 and 256 blocks - const byte SAPLING_FILE_TYPE = 0x02; - /// A file that occupies between 257 and 32768 blocks - const byte TREE_FILE_TYPE = 0x03; - const byte PASCAL_AREA_TYPE = 0x04; - const byte SUBDIRECTORY_TYPE = 0x0D; - const byte SUBDIRECTORY_HEADER_TYPE = 0x0E; - const byte ROOT_DIRECTORY_TYPE = 0x0F; + if(partition.Length < 3) + return false; - const byte VERSION1 = 0x00; + uint multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); - const uint YEAR_MASK = 0xFE000000; - const uint MONTH_MASK = 0x1E00000; - const uint DAY_MASK = 0x1F0000; - const uint HOUR_MASK = 0x1F00; - const uint MINUTE_MASK = 0x3F; + // Blocks 0 and 1 are boot code + ErrorNumber errno = imagePlugin.ReadSectors((2 * multiplier) + partition.Start, multiplier, + out byte[] rootDirectoryKeyBlock); - const byte DESTROY_ATTRIBUTE = 0x80; - const byte RENAME_ATTRIBUTE = 0x40; - const byte BACKUP_ATTRIBUTE = 0x20; - const byte WRITE_ATTRIBUTE = 0x02; - const byte READ_ATTRIBUTE = 0x01; - const byte RESERVED_ATTRIBUTE_MASK = 0x1C; + bool apmFromHddOnCd = false; - const byte STORAGE_TYPE_MASK = 0xF0; - const byte NAME_LENGTH_MASK = 0x0F; - const byte ENTRY_LENGTH = 0x27; - const byte ENTRIES_PER_BLOCK = 0x0D; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Apple ProDOS filesystem"; - /// - public Guid Id => new("43874265-7B8A-4739-BCF7-07F80D5932BF"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448 || + imagePlugin.Info.SectorSize == 2048) { - if(partition.Length < 3) + errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] tmp); + + if(errno != ErrorNumber.NoError) return false; - uint multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); - - // Blocks 0 and 1 are boot code - ErrorNumber errno = imagePlugin.ReadSectors((2 * multiplier) + partition.Start, multiplier, - out byte[] rootDirectoryKeyBlock); - - bool apmFromHddOnCd = false; - - if(imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448 || - imagePlugin.Info.SectorSize == 2048) + foreach(int offset in new[] + { + 0, 0x200, 0x400, 0x600, 0x800, 0xA00 + }.Where(offset => tmp.Length > offset + 0x200 && BitConverter.ToUInt16(tmp, offset) == 0 && + (byte)((tmp[offset + 0x04] & STORAGE_TYPE_MASK) >> 4) == ROOT_DIRECTORY_TYPE && + tmp[offset + 0x23] == ENTRY_LENGTH && tmp[offset + 0x24] == ENTRIES_PER_BLOCK)) { - errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] tmp); + Array.Copy(tmp, offset, rootDirectoryKeyBlock, 0, 0x200); + apmFromHddOnCd = true; - if(errno != ErrorNumber.NoError) - return false; - - foreach(int offset in new[] - { - 0, 0x200, 0x400, 0x600, 0x800, 0xA00 - }.Where(offset => tmp.Length > offset + 0x200 && BitConverter.ToUInt16(tmp, offset) == 0 && - (byte)((tmp[offset + 0x04] & STORAGE_TYPE_MASK) >> 4) == ROOT_DIRECTORY_TYPE && - tmp[offset + 0x23] == ENTRY_LENGTH && tmp[offset + 0x24] == ENTRIES_PER_BLOCK)) - { - Array.Copy(tmp, offset, rootDirectoryKeyBlock, 0, 0x200); - apmFromHddOnCd = true; - - break; - } + break; } - - ushort prePointer = BitConverter.ToUInt16(rootDirectoryKeyBlock, 0); - AaruConsole.DebugWriteLine("ProDOS plugin", "prePointer = {0}", prePointer); - - if(prePointer != 0) - return false; - - byte storageType = (byte)((rootDirectoryKeyBlock[0x04] & STORAGE_TYPE_MASK) >> 4); - AaruConsole.DebugWriteLine("ProDOS plugin", "storage_type = {0}", storageType); - - if(storageType != ROOT_DIRECTORY_TYPE) - return false; - - byte entryLength = rootDirectoryKeyBlock[0x23]; - AaruConsole.DebugWriteLine("ProDOS plugin", "entry_length = {0}", entryLength); - - if(entryLength != ENTRY_LENGTH) - return false; - - byte entriesPerBlock = rootDirectoryKeyBlock[0x24]; - AaruConsole.DebugWriteLine("ProDOS plugin", "entries_per_block = {0}", entriesPerBlock); - - if(entriesPerBlock != ENTRIES_PER_BLOCK) - return false; - - ushort bitMapPointer = BitConverter.ToUInt16(rootDirectoryKeyBlock, 0x27); - AaruConsole.DebugWriteLine("ProDOS plugin", "bit_map_pointer = {0}", bitMapPointer); - - if(bitMapPointer > partition.End) - return false; - - ushort totalBlocks = BitConverter.ToUInt16(rootDirectoryKeyBlock, 0x29); - - if(apmFromHddOnCd) - totalBlocks /= 4; - - AaruConsole.DebugWriteLine("ProDOS plugin", "{0} <= ({1} - {2} + 1)? {3}", totalBlocks, partition.End, - partition.Start, totalBlocks <= partition.End - partition.Start + 1); - - return totalBlocks <= partition.End - partition.Start + 1; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? new Apple2c(); - information = ""; - var sbInformation = new StringBuilder(); - uint multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); + ushort prePointer = BitConverter.ToUInt16(rootDirectoryKeyBlock, 0); + AaruConsole.DebugWriteLine("ProDOS plugin", "prePointer = {0}", prePointer); - // Blocks 0 and 1 are boot code - ErrorNumber errno = imagePlugin.ReadSectors((2 * multiplier) + partition.Start, multiplier, - out byte[] rootDirectoryKeyBlockBytes); + if(prePointer != 0) + return false; + + byte storageType = (byte)((rootDirectoryKeyBlock[0x04] & STORAGE_TYPE_MASK) >> 4); + AaruConsole.DebugWriteLine("ProDOS plugin", "storage_type = {0}", storageType); + + if(storageType != ROOT_DIRECTORY_TYPE) + return false; + + byte entryLength = rootDirectoryKeyBlock[0x23]; + AaruConsole.DebugWriteLine("ProDOS plugin", "entry_length = {0}", entryLength); + + if(entryLength != ENTRY_LENGTH) + return false; + + byte entriesPerBlock = rootDirectoryKeyBlock[0x24]; + AaruConsole.DebugWriteLine("ProDOS plugin", "entries_per_block = {0}", entriesPerBlock); + + if(entriesPerBlock != ENTRIES_PER_BLOCK) + return false; + + ushort bitMapPointer = BitConverter.ToUInt16(rootDirectoryKeyBlock, 0x27); + AaruConsole.DebugWriteLine("ProDOS plugin", "bit_map_pointer = {0}", bitMapPointer); + + if(bitMapPointer > partition.End) + return false; + + ushort totalBlocks = BitConverter.ToUInt16(rootDirectoryKeyBlock, 0x29); + + if(apmFromHddOnCd) + totalBlocks /= 4; + + AaruConsole.DebugWriteLine("ProDOS plugin", "{0} <= ({1} - {2} + 1)? {3}", totalBlocks, partition.End, + partition.Start, totalBlocks <= partition.End - partition.Start + 1); + + return totalBlocks <= partition.End - partition.Start + 1; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? new Apple2c(); + information = ""; + var sbInformation = new StringBuilder(); + uint multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); + + // Blocks 0 and 1 are boot code + ErrorNumber errno = imagePlugin.ReadSectors((2 * multiplier) + partition.Start, multiplier, + out byte[] rootDirectoryKeyBlockBytes); + + if(errno != ErrorNumber.NoError) + return; + + bool apmFromHddOnCd = false; + + if(imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448 || + imagePlugin.Info.SectorSize == 2048) + { + errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] tmp); if(errno != ErrorNumber.NoError) return; - bool apmFromHddOnCd = false; - - if(imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448 || - imagePlugin.Info.SectorSize == 2048) + foreach(int offset in new[] + { + 0, 0x200, 0x400, 0x600, 0x800, 0xA00 + }.Where(offset => BitConverter.ToUInt16(tmp, offset) == 0 && + (byte)((tmp[offset + 0x04] & STORAGE_TYPE_MASK) >> 4) == ROOT_DIRECTORY_TYPE && + tmp[offset + 0x23] == ENTRY_LENGTH && tmp[offset + 0x24] == ENTRIES_PER_BLOCK)) { - errno = imagePlugin.ReadSectors(partition.Start, 2, out byte[] tmp); + Array.Copy(tmp, offset, rootDirectoryKeyBlockBytes, 0, 0x200); + apmFromHddOnCd = true; - if(errno != ErrorNumber.NoError) - return; - - foreach(int offset in new[] - { - 0, 0x200, 0x400, 0x600, 0x800, 0xA00 - }.Where(offset => BitConverter.ToUInt16(tmp, offset) == 0 && - (byte)((tmp[offset + 0x04] & STORAGE_TYPE_MASK) >> 4) == ROOT_DIRECTORY_TYPE && - tmp[offset + 0x23] == ENTRY_LENGTH && tmp[offset + 0x24] == ENTRIES_PER_BLOCK)) - { - Array.Copy(tmp, offset, rootDirectoryKeyBlockBytes, 0, 0x200); - apmFromHddOnCd = true; - - break; - } + break; } + } - var rootDirectoryKeyBlock = new RootDirectoryKeyBlock - { - header = new RootDirectoryHeader(), - zero = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x00), - next_pointer = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x02) - }; + var rootDirectoryKeyBlock = new RootDirectoryKeyBlock + { + header = new RootDirectoryHeader(), + zero = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x00), + next_pointer = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x02) + }; - rootDirectoryKeyBlock.header.storage_type = - (byte)((rootDirectoryKeyBlockBytes[0x04] & STORAGE_TYPE_MASK) >> 4); + rootDirectoryKeyBlock.header.storage_type = + (byte)((rootDirectoryKeyBlockBytes[0x04] & STORAGE_TYPE_MASK) >> 4); - rootDirectoryKeyBlock.header.name_length = (byte)(rootDirectoryKeyBlockBytes[0x04] & NAME_LENGTH_MASK); - byte[] temporal = new byte[rootDirectoryKeyBlock.header.name_length]; - Array.Copy(rootDirectoryKeyBlockBytes, 0x05, temporal, 0, rootDirectoryKeyBlock.header.name_length); - rootDirectoryKeyBlock.header.volume_name = Encoding.GetString(temporal); - rootDirectoryKeyBlock.header.reserved = BitConverter.ToUInt64(rootDirectoryKeyBlockBytes, 0x14); + rootDirectoryKeyBlock.header.name_length = (byte)(rootDirectoryKeyBlockBytes[0x04] & NAME_LENGTH_MASK); + byte[] temporal = new byte[rootDirectoryKeyBlock.header.name_length]; + Array.Copy(rootDirectoryKeyBlockBytes, 0x05, temporal, 0, rootDirectoryKeyBlock.header.name_length); + rootDirectoryKeyBlock.header.volume_name = Encoding.GetString(temporal); + rootDirectoryKeyBlock.header.reserved = BitConverter.ToUInt64(rootDirectoryKeyBlockBytes, 0x14); - ushort tempTimestampLeft = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x1C); - ushort tempTimestampRight = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x1E); + ushort tempTimestampLeft = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x1C); + ushort tempTimestampRight = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x1E); - bool dateCorrect; + bool dateCorrect; - try - { - uint tempTimestamp = (uint)((tempTimestampLeft << 16) + tempTimestampRight); - int year = (int)((tempTimestamp & YEAR_MASK) >> 25); - int month = (int)((tempTimestamp & MONTH_MASK) >> 21); - int day = (int)((tempTimestamp & DAY_MASK) >> 16); - int hour = (int)((tempTimestamp & HOUR_MASK) >> 8); - int minute = (int)(tempTimestamp & MINUTE_MASK); - year += 1900; + try + { + uint tempTimestamp = (uint)((tempTimestampLeft << 16) + tempTimestampRight); + int year = (int)((tempTimestamp & YEAR_MASK) >> 25); + int month = (int)((tempTimestamp & MONTH_MASK) >> 21); + int day = (int)((tempTimestamp & DAY_MASK) >> 16); + int hour = (int)((tempTimestamp & HOUR_MASK) >> 8); + int minute = (int)(tempTimestamp & MINUTE_MASK); + year += 1900; - if(year < 1940) - year += 100; + if(year < 1940) + year += 100; - AaruConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp_left = 0x{0:X4}", tempTimestampLeft); - AaruConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp_right = 0x{0:X4}", tempTimestampRight); - AaruConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp = 0x{0:X8}", tempTimestamp); + AaruConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp_left = 0x{0:X4}", tempTimestampLeft); + AaruConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp_right = 0x{0:X4}", tempTimestampRight); + AaruConsole.DebugWriteLine("ProDOS plugin", "temp_timestamp = 0x{0:X8}", tempTimestamp); - AaruConsole.DebugWriteLine("ProDOS plugin", - "Datetime field year {0}, month {1}, day {2}, hour {3}, minute {4}.", year, - month, day, hour, minute); + AaruConsole.DebugWriteLine("ProDOS plugin", + "Datetime field year {0}, month {1}, day {2}, hour {3}, minute {4}.", year, + month, day, hour, minute); - rootDirectoryKeyBlock.header.creation_time = new DateTime(year, month, day, hour, minute, 0); - dateCorrect = true; - } - catch(ArgumentOutOfRangeException) - { - dateCorrect = false; - } + rootDirectoryKeyBlock.header.creation_time = new DateTime(year, month, day, hour, minute, 0); + dateCorrect = true; + } + catch(ArgumentOutOfRangeException) + { + dateCorrect = false; + } - rootDirectoryKeyBlock.header.version = rootDirectoryKeyBlockBytes[0x20]; - rootDirectoryKeyBlock.header.min_version = rootDirectoryKeyBlockBytes[0x21]; - rootDirectoryKeyBlock.header.access = rootDirectoryKeyBlockBytes[0x22]; - rootDirectoryKeyBlock.header.entry_length = rootDirectoryKeyBlockBytes[0x23]; - rootDirectoryKeyBlock.header.entries_per_block = rootDirectoryKeyBlockBytes[0x24]; + rootDirectoryKeyBlock.header.version = rootDirectoryKeyBlockBytes[0x20]; + rootDirectoryKeyBlock.header.min_version = rootDirectoryKeyBlockBytes[0x21]; + rootDirectoryKeyBlock.header.access = rootDirectoryKeyBlockBytes[0x22]; + rootDirectoryKeyBlock.header.entry_length = rootDirectoryKeyBlockBytes[0x23]; + rootDirectoryKeyBlock.header.entries_per_block = rootDirectoryKeyBlockBytes[0x24]; - rootDirectoryKeyBlock.header.file_count = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x25); - rootDirectoryKeyBlock.header.bit_map_pointer = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x27); - rootDirectoryKeyBlock.header.total_blocks = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x29); + rootDirectoryKeyBlock.header.file_count = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x25); + rootDirectoryKeyBlock.header.bit_map_pointer = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x27); + rootDirectoryKeyBlock.header.total_blocks = BitConverter.ToUInt16(rootDirectoryKeyBlockBytes, 0x29); - if(apmFromHddOnCd) - sbInformation.AppendLine("ProDOS uses 512 bytes/sector while devices uses 2048 bytes/sector."). - AppendLine(); - - if(rootDirectoryKeyBlock.header.version != VERSION1 || - rootDirectoryKeyBlock.header.min_version != VERSION1) - { - sbInformation.AppendLine("Warning! Detected unknown ProDOS version ProDOS filesystem."); - sbInformation.AppendLine("All of the following information may be incorrect"); - } - - if(rootDirectoryKeyBlock.header.version == VERSION1) - sbInformation.AppendLine("ProDOS version 1 used to create this volume."); - else - sbInformation.AppendFormat("Unknown ProDOS version with field {0} used to create this volume.", - rootDirectoryKeyBlock.header.version).AppendLine(); - - if(rootDirectoryKeyBlock.header.min_version == VERSION1) - sbInformation.AppendLine("ProDOS version 1 at least required for reading this volume."); - else - sbInformation. - AppendFormat("Unknown ProDOS version with field {0} is at least required for reading this volume.", - rootDirectoryKeyBlock.header.min_version).AppendLine(); - - sbInformation.AppendFormat("Volume name is {0}", rootDirectoryKeyBlock.header.volume_name).AppendLine(); - - if(dateCorrect) - sbInformation.AppendFormat("Volume created on {0}", rootDirectoryKeyBlock.header.creation_time). - AppendLine(); - - sbInformation.AppendFormat("{0} bytes per directory entry", rootDirectoryKeyBlock.header.entry_length). + if(apmFromHddOnCd) + sbInformation.AppendLine("ProDOS uses 512 bytes/sector while devices uses 2048 bytes/sector."). AppendLine(); + if(rootDirectoryKeyBlock.header.version != VERSION1 || + rootDirectoryKeyBlock.header.min_version != VERSION1) + { + sbInformation.AppendLine("Warning! Detected unknown ProDOS version ProDOS filesystem."); + sbInformation.AppendLine("All of the following information may be incorrect"); + } + + if(rootDirectoryKeyBlock.header.version == VERSION1) + sbInformation.AppendLine("ProDOS version 1 used to create this volume."); + else + sbInformation.AppendFormat("Unknown ProDOS version with field {0} used to create this volume.", + rootDirectoryKeyBlock.header.version).AppendLine(); + + if(rootDirectoryKeyBlock.header.min_version == VERSION1) + sbInformation.AppendLine("ProDOS version 1 at least required for reading this volume."); + else sbInformation. - AppendFormat("{0} entries per directory block", rootDirectoryKeyBlock.header.entries_per_block). - AppendLine(); + AppendFormat("Unknown ProDOS version with field {0} is at least required for reading this volume.", + rootDirectoryKeyBlock.header.min_version).AppendLine(); - sbInformation.AppendFormat("{0} files in root directory", rootDirectoryKeyBlock.header.file_count). + sbInformation.AppendFormat("Volume name is {0}", rootDirectoryKeyBlock.header.volume_name).AppendLine(); + + if(dateCorrect) + sbInformation.AppendFormat("Volume created on {0}", rootDirectoryKeyBlock.header.creation_time). AppendLine(); - sbInformation.AppendFormat("{0} blocks in volume", rootDirectoryKeyBlock.header.total_blocks).AppendLine(); + sbInformation.AppendFormat("{0} bytes per directory entry", rootDirectoryKeyBlock.header.entry_length). + AppendLine(); - sbInformation.AppendFormat("Bitmap starts at block {0}", rootDirectoryKeyBlock.header.bit_map_pointer). - AppendLine(); + sbInformation. + AppendFormat("{0} entries per directory block", rootDirectoryKeyBlock.header.entries_per_block). + AppendLine(); - if((rootDirectoryKeyBlock.header.access & READ_ATTRIBUTE) == READ_ATTRIBUTE) - sbInformation.AppendLine("Volume can be read"); + sbInformation.AppendFormat("{0} files in root directory", rootDirectoryKeyBlock.header.file_count). + AppendLine(); - if((rootDirectoryKeyBlock.header.access & WRITE_ATTRIBUTE) == WRITE_ATTRIBUTE) - sbInformation.AppendLine("Volume can be written"); + sbInformation.AppendFormat("{0} blocks in volume", rootDirectoryKeyBlock.header.total_blocks).AppendLine(); - if((rootDirectoryKeyBlock.header.access & RENAME_ATTRIBUTE) == RENAME_ATTRIBUTE) - sbInformation.AppendLine("Volume can be renamed"); + sbInformation.AppendFormat("Bitmap starts at block {0}", rootDirectoryKeyBlock.header.bit_map_pointer). + AppendLine(); - if((rootDirectoryKeyBlock.header.access & DESTROY_ATTRIBUTE) == DESTROY_ATTRIBUTE) - sbInformation.AppendLine("Volume can be destroyed"); + if((rootDirectoryKeyBlock.header.access & READ_ATTRIBUTE) == READ_ATTRIBUTE) + sbInformation.AppendLine("Volume can be read"); - if((rootDirectoryKeyBlock.header.access & BACKUP_ATTRIBUTE) == BACKUP_ATTRIBUTE) - sbInformation.AppendLine("Volume must be backed up"); + if((rootDirectoryKeyBlock.header.access & WRITE_ATTRIBUTE) == WRITE_ATTRIBUTE) + sbInformation.AppendLine("Volume can be written"); - if((rootDirectoryKeyBlock.header.access & RESERVED_ATTRIBUTE_MASK) != 0) - AaruConsole.DebugWriteLine("ProDOS plugin", "Reserved attributes are set: {0:X2}", - rootDirectoryKeyBlock.header.access); + if((rootDirectoryKeyBlock.header.access & RENAME_ATTRIBUTE) == RENAME_ATTRIBUTE) + sbInformation.AppendLine("Volume can be renamed"); - information = sbInformation.ToString(); + if((rootDirectoryKeyBlock.header.access & DESTROY_ATTRIBUTE) == DESTROY_ATTRIBUTE) + sbInformation.AppendLine("Volume can be destroyed"); - XmlFsType = new FileSystemType - { - VolumeName = rootDirectoryKeyBlock.header.volume_name, - Files = rootDirectoryKeyBlock.header.file_count, - FilesSpecified = true, - Clusters = rootDirectoryKeyBlock.header.total_blocks, - Type = "ProDOS" - }; + if((rootDirectoryKeyBlock.header.access & BACKUP_ATTRIBUTE) == BACKUP_ATTRIBUTE) + sbInformation.AppendLine("Volume must be backed up"); - XmlFsType.ClusterSize = (uint)((partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / - XmlFsType.Clusters); + if((rootDirectoryKeyBlock.header.access & RESERVED_ATTRIBUTE_MASK) != 0) + AaruConsole.DebugWriteLine("ProDOS plugin", "Reserved attributes are set: {0:X2}", + rootDirectoryKeyBlock.header.access); - if(!dateCorrect) - return; + information = sbInformation.ToString(); - XmlFsType.CreationDate = rootDirectoryKeyBlock.header.creation_time; - XmlFsType.CreationDateSpecified = true; - } - - /// ProDOS directory entry, decoded structure - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct Entry + XmlFsType = new FileSystemType { - /// Type of file pointed by this entry Offset 0x00, mask 0xF0 - public byte storage_type; - /// Length of name_length pascal string Offset 0x00, mask 0x0F - public byte name_length; - /// Pascal string of file name Offset 0x01, 15 bytes - public string file_name; - /// Descriptor of internal structure of the file Offset 0x10, 1 byte - public byte file_type; - /// - /// Block address of master index block for tree files. Block address of index block for sapling files. Block - /// address of block for seedling files. Offset 0x11, 2 bytes - /// - public ushort key_pointer; - /// Blocks used by file or directory, including index blocks. Offset 0x13, 2 bytes - public ushort blocks_used; - /// Size of file in bytes Offset 0x15, 3 bytes - public uint EOF; - /// File creation datetime Offset 0x18, 4 bytes - public DateTime creation_time; - /// Version of ProDOS that created this file Offset 0x1C, 1 byte - public byte version; - /// Minimum version of ProDOS needed to access this file Offset 0x1D, 1 byte - public byte min_version; - /// File permissions Offset 0x1E, 1 byte - public byte access; - /// General purpose field to store additional information about file format Offset 0x1F, 2 bytes - public ushort aux_type; - /// File last modification date time Offset 0x21, 4 bytes - public DateTime last_mod; - /// Block address pointer to key block of the directory containing this entry Offset 0x25, 2 bytes - public ushort header_pointer; - } + VolumeName = rootDirectoryKeyBlock.header.volume_name, + Files = rootDirectoryKeyBlock.header.file_count, + FilesSpecified = true, + Clusters = rootDirectoryKeyBlock.header.total_blocks, + Type = "ProDOS" + }; - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct RootDirectoryHeader - { - /// Constant 0x0F Offset 0x04, mask 0xF0 - public byte storage_type; - /// Length of volume_name pascal string Offset 0x04, mask 0x0F - public byte name_length; - /// The name of the volume. Offset 0x05, 15 bytes - public string volume_name; - /// Reserved for future expansion Offset 0x14, 8 bytes - public ulong reserved; - /// Creation time of the volume Offset 0x1C, 4 bytes - public DateTime creation_time; - /// Version number of the volume format Offset 0x20, 1 byte - public byte version; - /// Reserved for future use Offset 0x21, 1 byte - public byte min_version; - /// Permissions for the volume Offset 0x22, 1 byte - public byte access; - /// Length of an entry in this directory Const 0x27 Offset 0x23, 1 byte - public byte entry_length; - /// Number of entries per block Const 0x0D Offset 0x24, 1 byte - public byte entries_per_block; - /// Number of active files in this directory Offset 0x25, 2 bytes - public ushort file_count; - /// - /// Block address of the first block of the volume's bitmap, one for every 4096 blocks or fraction Offset 0x27, 2 - /// bytes - /// - public ushort bit_map_pointer; - /// Total number of blocks in the volume Offset 0x29, 2 bytes - public ushort total_blocks; - } + XmlFsType.ClusterSize = (uint)((partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / + XmlFsType.Clusters); - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct DirectoryHeader - { - /// Constant 0x0E Offset 0x04, mask 0xF0 - public byte storage_type; - /// Length of volume_name pascal string Offset 0x04, mask 0x0F - public byte name_length; - /// The name of the directory. Offset 0x05, 15 bytes - public string directory_name; - /// Reserved for future expansion Offset 0x14, 8 bytes - public ulong reserved; - /// Creation time of the volume Offset 0x1C, 4 bytes - public DateTime creation_time; - /// Version number of the volume format Offset 0x20, 1 byte - public byte version; - /// Reserved for future use Offset 0x21, 1 byte - public byte min_version; - /// Permissions for the volume Offset 0x22, 1 byte - public byte access; - /// Length of an entry in this directory Const 0x27 Offset 0x23, 1 byte - public byte entry_length; - /// Number of entries per block Const 0x0D Offset 0x24, 1 byte - public byte entries_per_block; - /// Number of active files in this directory Offset 0x25, 2 bytes - public ushort file_count; - /// Block address of parent directory block that contains this entry Offset 0x27, 2 bytes - public ushort parent_pointer; - /// Entry number within the block indicated in parent_pointer Offset 0x29, 1 byte - public byte parent_entry_number; - /// Length of the entry that holds this directory, in the parent entry Const 0x27 Offset 0x2A, 1 byte - public byte parent_entry_length; - } + if(!dateCorrect) + return; - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct DirectoryKeyBlock - { - /// Always 0 Offset 0x00, 2 bytes - public ushort zero; - /// Pointer to next directory block, 0 if last Offset 0x02, 2 bytes - public ushort next_pointer; - /// Directory header Offset 0x04, 39 bytes - public DirectoryHeader header; - /// Directory entries Offset 0x2F, 39 bytes each, 12 entries - public Entry[] entries; - } + XmlFsType.CreationDate = rootDirectoryKeyBlock.header.creation_time; + XmlFsType.CreationDateSpecified = true; + } - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct RootDirectoryKeyBlock - { - /// Always 0 Offset 0x00, 2 bytes - public ushort zero; - /// Pointer to next directory block, 0 if last Offset 0x02, 2 bytes - public ushort next_pointer; - /// Directory header Offset 0x04, 39 bytes - public RootDirectoryHeader header; - /// Directory entries Offset 0x2F, 39 bytes each, 12 entries - public Entry[] entries; - } + /// ProDOS directory entry, decoded structure + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct Entry + { + /// Type of file pointed by this entry Offset 0x00, mask 0xF0 + public byte storage_type; + /// Length of name_length pascal string Offset 0x00, mask 0x0F + public byte name_length; + /// Pascal string of file name Offset 0x01, 15 bytes + public string file_name; + /// Descriptor of internal structure of the file Offset 0x10, 1 byte + public byte file_type; + /// + /// Block address of master index block for tree files. Block address of index block for sapling files. Block + /// address of block for seedling files. Offset 0x11, 2 bytes + /// + public ushort key_pointer; + /// Blocks used by file or directory, including index blocks. Offset 0x13, 2 bytes + public ushort blocks_used; + /// Size of file in bytes Offset 0x15, 3 bytes + public uint EOF; + /// File creation datetime Offset 0x18, 4 bytes + public DateTime creation_time; + /// Version of ProDOS that created this file Offset 0x1C, 1 byte + public byte version; + /// Minimum version of ProDOS needed to access this file Offset 0x1D, 1 byte + public byte min_version; + /// File permissions Offset 0x1E, 1 byte + public byte access; + /// General purpose field to store additional information about file format Offset 0x1F, 2 bytes + public ushort aux_type; + /// File last modification date time Offset 0x21, 4 bytes + public DateTime last_mod; + /// Block address pointer to key block of the directory containing this entry Offset 0x25, 2 bytes + public ushort header_pointer; + } - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct DirectoryBlock - { - /// Pointer to previous directory block Offset 0x00, 2 bytes - public ushort zero; - /// Pointer to next directory block, 0 if last Offset 0x02, 2 bytes - public ushort next_pointer; - /// Directory entries Offset 0x2F, 39 bytes each, 13 entries - public Entry[] entries; - } + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct RootDirectoryHeader + { + /// Constant 0x0F Offset 0x04, mask 0xF0 + public byte storage_type; + /// Length of volume_name pascal string Offset 0x04, mask 0x0F + public byte name_length; + /// The name of the volume. Offset 0x05, 15 bytes + public string volume_name; + /// Reserved for future expansion Offset 0x14, 8 bytes + public ulong reserved; + /// Creation time of the volume Offset 0x1C, 4 bytes + public DateTime creation_time; + /// Version number of the volume format Offset 0x20, 1 byte + public byte version; + /// Reserved for future use Offset 0x21, 1 byte + public byte min_version; + /// Permissions for the volume Offset 0x22, 1 byte + public byte access; + /// Length of an entry in this directory Const 0x27 Offset 0x23, 1 byte + public byte entry_length; + /// Number of entries per block Const 0x0D Offset 0x24, 1 byte + public byte entries_per_block; + /// Number of active files in this directory Offset 0x25, 2 bytes + public ushort file_count; + /// + /// Block address of the first block of the volume's bitmap, one for every 4096 blocks or fraction Offset 0x27, 2 + /// bytes + /// + public ushort bit_map_pointer; + /// Total number of blocks in the volume Offset 0x29, 2 bytes + public ushort total_blocks; + } - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct IndexBlock - { - /// Up to 256 pointers to blocks, 0 to indicate the block is sparsed (non-allocated) - public ushort[] block_pointer; - } + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct DirectoryHeader + { + /// Constant 0x0E Offset 0x04, mask 0xF0 + public byte storage_type; + /// Length of volume_name pascal string Offset 0x04, mask 0x0F + public byte name_length; + /// The name of the directory. Offset 0x05, 15 bytes + public string directory_name; + /// Reserved for future expansion Offset 0x14, 8 bytes + public ulong reserved; + /// Creation time of the volume Offset 0x1C, 4 bytes + public DateTime creation_time; + /// Version number of the volume format Offset 0x20, 1 byte + public byte version; + /// Reserved for future use Offset 0x21, 1 byte + public byte min_version; + /// Permissions for the volume Offset 0x22, 1 byte + public byte access; + /// Length of an entry in this directory Const 0x27 Offset 0x23, 1 byte + public byte entry_length; + /// Number of entries per block Const 0x0D Offset 0x24, 1 byte + public byte entries_per_block; + /// Number of active files in this directory Offset 0x25, 2 bytes + public ushort file_count; + /// Block address of parent directory block that contains this entry Offset 0x27, 2 bytes + public ushort parent_pointer; + /// Entry number within the block indicated in parent_pointer Offset 0x29, 1 byte + public byte parent_entry_number; + /// Length of the entry that holds this directory, in the parent entry Const 0x27 Offset 0x2A, 1 byte + public byte parent_entry_length; + } - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct MasterIndexBlock - { - /// Up to 128 pointers to index blocks - public ushort[] index_block_pointer; - } + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct DirectoryKeyBlock + { + /// Always 0 Offset 0x00, 2 bytes + public ushort zero; + /// Pointer to next directory block, 0 if last Offset 0x02, 2 bytes + public ushort next_pointer; + /// Directory header Offset 0x04, 39 bytes + public DirectoryHeader header; + /// Directory entries Offset 0x2F, 39 bytes each, 12 entries + public Entry[] entries; + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct RootDirectoryKeyBlock + { + /// Always 0 Offset 0x00, 2 bytes + public ushort zero; + /// Pointer to next directory block, 0 if last Offset 0x02, 2 bytes + public ushort next_pointer; + /// Directory header Offset 0x04, 39 bytes + public RootDirectoryHeader header; + /// Directory entries Offset 0x2F, 39 bytes each, 12 entries + public Entry[] entries; + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct DirectoryBlock + { + /// Pointer to previous directory block Offset 0x00, 2 bytes + public ushort zero; + /// Pointer to next directory block, 0 if last Offset 0x02, 2 bytes + public ushort next_pointer; + /// Directory entries Offset 0x2F, 39 bytes each, 13 entries + public Entry[] entries; + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct IndexBlock + { + /// Up to 256 pointers to blocks, 0 to indicate the block is sparsed (non-allocated) + public ushort[] block_pointer; + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct MasterIndexBlock + { + /// Up to 128 pointers to index blocks + public ushort[] index_block_pointer; } } \ No newline at end of file diff --git a/Aaru.Filesystems/QNX4.cs b/Aaru.Filesystems/QNX4.cs index aa93bb37f..1d66dbfcc 100644 --- a/Aaru.Filesystems/QNX4.cs +++ b/Aaru.Filesystems/QNX4.cs @@ -42,244 +42,243 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of QNX 4 filesystem +[SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed class QNX4 : IFilesystem { - /// - /// Implements detection of QNX 4 filesystem - [SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed class QNX4 : IFilesystem + readonly byte[] _rootDirFname = { - readonly byte[] _rootDirFname = + 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /// + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "QNX4 Plugin"; + /// + public Guid Id => new("E73A63FA-B5B0-48BF-BF82-DA5F0A8170D2"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + if(partition.Start + 1 >= imagePlugin.Info.Sectors) + return false; + + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + 1, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < 512) + return false; + + Superblock qnxSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + // Check root directory name + if(!_rootDirFname.SequenceEqual(qnxSb.rootDir.di_fname)) + return false; + + // Check sizes are multiple of blocks + if(qnxSb.rootDir.di_size % 512 != 0 || + qnxSb.inode.di_size % 512 != 0 || + qnxSb.boot.di_size % 512 != 0 || + qnxSb.altBoot.di_size % 512 != 0) + return false; + + // Check extents are not past device + if(qnxSb.rootDir.di_first_xtnt.Block + partition.Start >= partition.End || + qnxSb.inode.di_first_xtnt.Block + partition.Start >= partition.End || + qnxSb.boot.di_first_xtnt.Block + partition.Start >= partition.End || + qnxSb.altBoot.di_first_xtnt.Block + partition.Start >= partition.End) + return false; + + // Check inodes are in use + if((qnxSb.rootDir.di_status & 0x01) != 0x01 || + (qnxSb.inode.di_status & 0x01) != 0x01 || + (qnxSb.boot.di_status & 0x01) != 0x01) + return false; + + // All hail filesystems without identification marks + return true; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + 1, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < 512) + return; + + Superblock qnxSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + // Too much useless information + /* + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_fname = {0}", CurrentEncoding.GetString(qnxSb.rootDir.di_fname)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_size = {0}", qnxSb.rootDir.di_size); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_first_xtnt.block = {0}", qnxSb.rootDir.di_first_xtnt.block); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_first_xtnt.length = {0}", qnxSb.rootDir.di_first_xtnt.length); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_xblk = {0}", qnxSb.rootDir.di_xblk); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_ftime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_mtime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_atime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_ctime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_num_xtnts = {0}", qnxSb.rootDir.di_num_xtnts); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_mode = {0}", Convert.ToString(qnxSb.rootDir.di_mode, 8)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_uid = {0}", qnxSb.rootDir.di_uid); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_gid = {0}", qnxSb.rootDir.di_gid); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_nlink = {0}", qnxSb.rootDir.di_nlink); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_zero = {0}", qnxSb.rootDir.di_zero); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_type = {0}", qnxSb.rootDir.di_type); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_status = {0}", qnxSb.rootDir.di_status); + + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_fname = {0}", CurrentEncoding.GetString(qnxSb.inode.di_fname)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_size = {0}", qnxSb.inode.di_size); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_first_xtnt.block = {0}", qnxSb.inode.di_first_xtnt.block); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_first_xtnt.length = {0}", qnxSb.inode.di_first_xtnt.length); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_xblk = {0}", qnxSb.inode.di_xblk); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_ftime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_mtime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_atime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_ctime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_num_xtnts = {0}", qnxSb.inode.di_num_xtnts); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_mode = {0}", Convert.ToString(qnxSb.inode.di_mode, 8)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_uid = {0}", qnxSb.inode.di_uid); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_gid = {0}", qnxSb.inode.di_gid); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_nlink = {0}", qnxSb.inode.di_nlink); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_zero = {0}", qnxSb.inode.di_zero); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_type = {0}", qnxSb.inode.di_type); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_status = {0}", qnxSb.inode.di_status); + + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_fname = {0}", CurrentEncoding.GetString(qnxSb.boot.di_fname)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_size = {0}", qnxSb.boot.di_size); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_first_xtnt.block = {0}", qnxSb.boot.di_first_xtnt.block); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_first_xtnt.length = {0}", qnxSb.boot.di_first_xtnt.length); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_xblk = {0}", qnxSb.boot.di_xblk); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_ftime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_mtime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_atime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_ctime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_num_xtnts = {0}", qnxSb.boot.di_num_xtnts); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_mode = {0}", Convert.ToString(qnxSb.boot.di_mode, 8)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_uid = {0}", qnxSb.boot.di_uid); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_gid = {0}", qnxSb.boot.di_gid); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_nlink = {0}", qnxSb.boot.di_nlink); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_zero = {0}", qnxSb.boot.di_zero); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_type = {0}", qnxSb.boot.di_type); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_status = {0}", qnxSb.boot.di_status); + + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_fname = {0}", CurrentEncoding.GetString(qnxSb.altBoot.di_fname)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_size = {0}", qnxSb.altBoot.di_size); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_first_xtnt.block = {0}", qnxSb.altBoot.di_first_xtnt.block); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_first_xtnt.length = {0}", qnxSb.altBoot.di_first_xtnt.length); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_xblk = {0}", qnxSb.altBoot.di_xblk); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_ftime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_mtime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_atime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_ctime)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_num_xtnts = {0}", qnxSb.altBoot.di_num_xtnts); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_mode = {0}", Convert.ToString(qnxSb.altBoot.di_mode, 8)); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_uid = {0}", qnxSb.altBoot.di_uid); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_gid = {0}", qnxSb.altBoot.di_gid); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_nlink = {0}", qnxSb.altBoot.di_nlink); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_zero = {0}", qnxSb.altBoot.di_zero); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_type = {0}", qnxSb.altBoot.di_type); + AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_status = {0}", qnxSb.altBoot.di_status); + */ + + information = + $"QNX4 filesystem\nCreated on {DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_ftime)}\n"; + + XmlFsType = new FileSystemType { - 0x2F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + Type = "QNX4 filesystem", + Clusters = partition.Length, + ClusterSize = 512, + CreationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_ftime), + CreationDateSpecified = true, + ModificationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_mtime), + ModificationDateSpecified = true }; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "QNX4 Plugin"; - /// - public Guid Id => new("E73A63FA-B5B0-48BF-BF82-DA5F0A8170D2"); - /// - public string Author => "Natalia Portillo"; + XmlFsType.Bootable |= qnxSb.boot.di_size != 0 || qnxSb.altBoot.di_size != 0; + } - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(partition.Start + 1 >= imagePlugin.Info.Sectors) - return false; + struct Extent + { + public uint Block; + public uint Length; + } - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + 1, out byte[] sector); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Inode + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] di_fname; + public readonly uint di_size; + public readonly Extent di_first_xtnt; + public readonly uint di_xblk; + public readonly uint di_ftime; + public readonly uint di_mtime; + public readonly uint di_atime; + public readonly uint di_ctime; + public readonly ushort di_num_xtnts; + public readonly ushort di_mode; + public readonly ushort di_uid; + public readonly ushort di_gid; + public readonly ushort di_nlink; + public readonly uint di_zero; + public readonly byte di_type; + public readonly byte di_status; + } - if(errno != ErrorNumber.NoError) - return false; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct LinkInfo + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)] + public readonly byte[] dl_fname; + public readonly uint dl_inode_blk; + public readonly byte dl_inode_ndx; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly byte[] dl_spare; + public readonly byte dl_status; + } - if(sector.Length < 512) - return false; + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ExtentBlock + { + public readonly uint next_xblk; + public readonly uint prev_xblk; + public readonly byte num_xtnts; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] spare; + public readonly uint num_blocks; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)] + public readonly Extent[] xtnts; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] signature; + public readonly Extent first_xtnt; + } - Superblock qnxSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - // Check root directory name - if(!_rootDirFname.SequenceEqual(qnxSb.rootDir.di_fname)) - return false; - - // Check sizes are multiple of blocks - if(qnxSb.rootDir.di_size % 512 != 0 || - qnxSb.inode.di_size % 512 != 0 || - qnxSb.boot.di_size % 512 != 0 || - qnxSb.altBoot.di_size % 512 != 0) - return false; - - // Check extents are not past device - if(qnxSb.rootDir.di_first_xtnt.Block + partition.Start >= partition.End || - qnxSb.inode.di_first_xtnt.Block + partition.Start >= partition.End || - qnxSb.boot.di_first_xtnt.Block + partition.Start >= partition.End || - qnxSb.altBoot.di_first_xtnt.Block + partition.Start >= partition.End) - return false; - - // Check inodes are in use - if((qnxSb.rootDir.di_status & 0x01) != 0x01 || - (qnxSb.inode.di_status & 0x01) != 0x01 || - (qnxSb.boot.di_status & 0x01) != 0x01) - return false; - - // All hail filesystems without identification marks - return true; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + 1, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < 512) - return; - - Superblock qnxSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - // Too much useless information - /* - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_fname = {0}", CurrentEncoding.GetString(qnxSb.rootDir.di_fname)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_size = {0}", qnxSb.rootDir.di_size); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_first_xtnt.block = {0}", qnxSb.rootDir.di_first_xtnt.block); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_first_xtnt.length = {0}", qnxSb.rootDir.di_first_xtnt.length); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_xblk = {0}", qnxSb.rootDir.di_xblk); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_ftime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_mtime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_atime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.rootDir.di_ctime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_num_xtnts = {0}", qnxSb.rootDir.di_num_xtnts); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_mode = {0}", Convert.ToString(qnxSb.rootDir.di_mode, 8)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_uid = {0}", qnxSb.rootDir.di_uid); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_gid = {0}", qnxSb.rootDir.di_gid); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_nlink = {0}", qnxSb.rootDir.di_nlink); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_zero = {0}", qnxSb.rootDir.di_zero); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_type = {0}", qnxSb.rootDir.di_type); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.rootDir.di_status = {0}", qnxSb.rootDir.di_status); - - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_fname = {0}", CurrentEncoding.GetString(qnxSb.inode.di_fname)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_size = {0}", qnxSb.inode.di_size); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_first_xtnt.block = {0}", qnxSb.inode.di_first_xtnt.block); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_first_xtnt.length = {0}", qnxSb.inode.di_first_xtnt.length); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_xblk = {0}", qnxSb.inode.di_xblk); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_ftime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_mtime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_atime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.inode.di_ctime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_num_xtnts = {0}", qnxSb.inode.di_num_xtnts); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_mode = {0}", Convert.ToString(qnxSb.inode.di_mode, 8)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_uid = {0}", qnxSb.inode.di_uid); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_gid = {0}", qnxSb.inode.di_gid); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_nlink = {0}", qnxSb.inode.di_nlink); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_zero = {0}", qnxSb.inode.di_zero); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_type = {0}", qnxSb.inode.di_type); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.inode.di_status = {0}", qnxSb.inode.di_status); - - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_fname = {0}", CurrentEncoding.GetString(qnxSb.boot.di_fname)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_size = {0}", qnxSb.boot.di_size); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_first_xtnt.block = {0}", qnxSb.boot.di_first_xtnt.block); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_first_xtnt.length = {0}", qnxSb.boot.di_first_xtnt.length); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_xblk = {0}", qnxSb.boot.di_xblk); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_ftime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_mtime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_atime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.boot.di_ctime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_num_xtnts = {0}", qnxSb.boot.di_num_xtnts); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_mode = {0}", Convert.ToString(qnxSb.boot.di_mode, 8)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_uid = {0}", qnxSb.boot.di_uid); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_gid = {0}", qnxSb.boot.di_gid); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_nlink = {0}", qnxSb.boot.di_nlink); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_zero = {0}", qnxSb.boot.di_zero); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_type = {0}", qnxSb.boot.di_type); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.boot.di_status = {0}", qnxSb.boot.di_status); - - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_fname = {0}", CurrentEncoding.GetString(qnxSb.altBoot.di_fname)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_size = {0}", qnxSb.altBoot.di_size); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_first_xtnt.block = {0}", qnxSb.altBoot.di_first_xtnt.block); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_first_xtnt.length = {0}", qnxSb.altBoot.di_first_xtnt.length); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_xblk = {0}", qnxSb.altBoot.di_xblk); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_ftime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_ftime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_mtime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_mtime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_atime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_atime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_ctime = {0}", DateHandlers.UNIXUnsignedToDateTime(qnxSb.altBoot.di_ctime)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_num_xtnts = {0}", qnxSb.altBoot.di_num_xtnts); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_mode = {0}", Convert.ToString(qnxSb.altBoot.di_mode, 8)); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_uid = {0}", qnxSb.altBoot.di_uid); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_gid = {0}", qnxSb.altBoot.di_gid); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_nlink = {0}", qnxSb.altBoot.di_nlink); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_zero = {0}", qnxSb.altBoot.di_zero); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_type = {0}", qnxSb.altBoot.di_type); - AaruConsole.DebugWriteLine("QNX4 plugin", "qnxSb.altBoot.di_status = {0}", qnxSb.altBoot.di_status); - */ - - information = - $"QNX4 filesystem\nCreated on {DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_ftime)}\n"; - - XmlFsType = new FileSystemType - { - Type = "QNX4 filesystem", - Clusters = partition.Length, - ClusterSize = 512, - CreationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_ftime), - CreationDateSpecified = true, - ModificationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.rootDir.di_mtime), - ModificationDateSpecified = true - }; - - XmlFsType.Bootable |= qnxSb.boot.di_size != 0 || qnxSb.altBoot.di_size != 0; - } - - struct Extent - { - public uint Block; - public uint Length; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Inode - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] di_fname; - public readonly uint di_size; - public readonly Extent di_first_xtnt; - public readonly uint di_xblk; - public readonly uint di_ftime; - public readonly uint di_mtime; - public readonly uint di_atime; - public readonly uint di_ctime; - public readonly ushort di_num_xtnts; - public readonly ushort di_mode; - public readonly ushort di_uid; - public readonly ushort di_gid; - public readonly ushort di_nlink; - public readonly uint di_zero; - public readonly byte di_type; - public readonly byte di_status; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct LinkInfo - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 48)] - public readonly byte[] dl_fname; - public readonly uint dl_inode_blk; - public readonly byte dl_inode_ndx; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly byte[] dl_spare; - public readonly byte dl_status; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ExtentBlock - { - public readonly uint next_xblk; - public readonly uint prev_xblk; - public readonly byte num_xtnts; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] spare; - public readonly uint num_blocks; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 60)] - public readonly Extent[] xtnts; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] signature; - public readonly Extent first_xtnt; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Superblock - { - public readonly Inode rootDir; - public readonly Inode inode; - public readonly Inode boot; - public readonly Inode altBoot; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Superblock + { + public readonly Inode rootDir; + public readonly Inode inode; + public readonly Inode boot; + public readonly Inode altBoot; } } \ No newline at end of file diff --git a/Aaru.Filesystems/QNX6.cs b/Aaru.Filesystems/QNX6.cs index 621958a49..f3bd6ea6b 100644 --- a/Aaru.Filesystems/QNX6.cs +++ b/Aaru.Filesystems/QNX6.cs @@ -40,213 +40,212 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of QNX 6 filesystem +public sealed class QNX6 : IFilesystem { + const uint QNX6_SUPER_BLOCK_SIZE = 0x1000; + const uint QNX6_BOOT_BLOCKS_SIZE = 0x2000; + const uint QNX6_MAGIC = 0x68191122; + /// - /// Implements detection of QNX 6 filesystem - public sealed class QNX6 : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "QNX6 Plugin"; + /// + public Guid Id => new("3E610EA2-4D08-4D70-8947-830CD4C74FC0"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint QNX6_SUPER_BLOCK_SIZE = 0x1000; - const uint QNX6_BOOT_BLOCKS_SIZE = 0x2000; - const uint QNX6_MAGIC = 0x68191122; + uint sectors = QNX6_SUPER_BLOCK_SIZE / imagePlugin.Info.SectorSize; + uint bootSectors = QNX6_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "QNX6 Plugin"; - /// - public Guid Id => new("3E610EA2-4D08-4D70-8947-830CD4C74FC0"); - /// - public string Author => "Natalia Portillo"; + if(partition.Start + bootSectors + sectors >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sectors, out byte[] audiSector); + + if(errno != ErrorNumber.NoError) + return false; + + errno = imagePlugin.ReadSectors(partition.Start + bootSectors, sectors, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < QNX6_SUPER_BLOCK_SIZE) + return false; + + AudiSuperBlock audiSb = Marshal.ByteArrayToStructureLittleEndian(audiSector); + + SuperBlock qnxSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + return qnxSb.magic == QNX6_MAGIC || audiSb.magic == QNX6_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + var sb = new StringBuilder(); + uint sectors = QNX6_SUPER_BLOCK_SIZE / imagePlugin.Info.SectorSize; + uint bootSectors = QNX6_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sectors, out byte[] audiSector); + + if(errno != ErrorNumber.NoError) + return; + + errno = imagePlugin.ReadSectors(partition.Start + bootSectors, sectors, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < QNX6_SUPER_BLOCK_SIZE) + return; + + AudiSuperBlock audiSb = Marshal.ByteArrayToStructureLittleEndian(audiSector); + + SuperBlock qnxSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + bool audi = audiSb.magic == QNX6_MAGIC; + + if(audi) { - uint sectors = QNX6_SUPER_BLOCK_SIZE / imagePlugin.Info.SectorSize; - uint bootSectors = QNX6_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; + sb.AppendLine("QNX6 (Audi) filesystem"); + sb.AppendFormat("Checksum: 0x{0:X8}", audiSb.checksum).AppendLine(); + sb.AppendFormat("Serial: 0x{0:X16}", audiSb.checksum).AppendLine(); + sb.AppendFormat("{0} bytes per block", audiSb.blockSize).AppendLine(); + sb.AppendFormat("{0} inodes free of {1}", audiSb.freeInodes, audiSb.numInodes).AppendLine(); - if(partition.Start + bootSectors + sectors >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sectors, out byte[] audiSector); - - if(errno != ErrorNumber.NoError) - return false; - - errno = imagePlugin.ReadSectors(partition.Start + bootSectors, sectors, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < QNX6_SUPER_BLOCK_SIZE) - return false; - - AudiSuperBlock audiSb = Marshal.ByteArrayToStructureLittleEndian(audiSector); - - SuperBlock qnxSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - return qnxSb.magic == QNX6_MAGIC || audiSb.magic == QNX6_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - var sb = new StringBuilder(); - uint sectors = QNX6_SUPER_BLOCK_SIZE / imagePlugin.Info.SectorSize; - uint bootSectors = QNX6_BOOT_BLOCKS_SIZE / imagePlugin.Info.SectorSize; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sectors, out byte[] audiSector); - - if(errno != ErrorNumber.NoError) - return; - - errno = imagePlugin.ReadSectors(partition.Start + bootSectors, sectors, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < QNX6_SUPER_BLOCK_SIZE) - return; - - AudiSuperBlock audiSb = Marshal.ByteArrayToStructureLittleEndian(audiSector); - - SuperBlock qnxSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - bool audi = audiSb.magic == QNX6_MAGIC; - - if(audi) - { - sb.AppendLine("QNX6 (Audi) filesystem"); - sb.AppendFormat("Checksum: 0x{0:X8}", audiSb.checksum).AppendLine(); - sb.AppendFormat("Serial: 0x{0:X16}", audiSb.checksum).AppendLine(); - sb.AppendFormat("{0} bytes per block", audiSb.blockSize).AppendLine(); - sb.AppendFormat("{0} inodes free of {1}", audiSb.freeInodes, audiSb.numInodes).AppendLine(); - - sb.AppendFormat("{0} blocks ({1} bytes) free of {2} ({3} bytes)", audiSb.freeBlocks, - audiSb.freeBlocks * audiSb.blockSize, audiSb.numBlocks, - audiSb.numBlocks * audiSb.blockSize).AppendLine(); - - XmlFsType = new FileSystemType - { - Type = "QNX6 (Audi) filesystem", - Clusters = audiSb.numBlocks, - ClusterSize = audiSb.blockSize, - Bootable = true, - Files = audiSb.numInodes - audiSb.freeInodes, - FilesSpecified = true, - FreeClusters = audiSb.freeBlocks, - FreeClustersSpecified = true, - VolumeSerial = $"{audiSb.serial:X16}" - }; - - //xmlFSType.VolumeName = CurrentEncoding.GetString(audiSb.id); - - information = sb.ToString(); - - return; - } - - sb.AppendLine("QNX6 filesystem"); - sb.AppendFormat("Checksum: 0x{0:X8}", qnxSb.checksum).AppendLine(); - sb.AppendFormat("Serial: 0x{0:X16}", qnxSb.checksum).AppendLine(); - sb.AppendFormat("Created on {0}", DateHandlers.UnixUnsignedToDateTime(qnxSb.ctime)).AppendLine(); - sb.AppendFormat("Last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(qnxSb.atime)).AppendLine(); - sb.AppendFormat("Flags: 0x{0:X8}", qnxSb.flags).AppendLine(); - sb.AppendFormat("Version1: 0x{0:X4}", qnxSb.version1).AppendLine(); - sb.AppendFormat("Version2: 0x{0:X4}", qnxSb.version2).AppendLine(); - - //sb.AppendFormat("Volume ID: \"{0}\"", CurrentEncoding.GetString(qnxSb.volumeid)).AppendLine(); - sb.AppendFormat("{0} bytes per block", qnxSb.blockSize).AppendLine(); - sb.AppendFormat("{0} inodes free of {1}", qnxSb.freeInodes, qnxSb.numInodes).AppendLine(); - - sb.AppendFormat("{0} blocks ({1} bytes) free of {2} ({3} bytes)", qnxSb.freeBlocks, - qnxSb.freeBlocks * qnxSb.blockSize, qnxSb.numBlocks, qnxSb.numBlocks * qnxSb.blockSize). - AppendLine(); + sb.AppendFormat("{0} blocks ({1} bytes) free of {2} ({3} bytes)", audiSb.freeBlocks, + audiSb.freeBlocks * audiSb.blockSize, audiSb.numBlocks, + audiSb.numBlocks * audiSb.blockSize).AppendLine(); XmlFsType = new FileSystemType { - Type = "QNX6 filesystem", - Clusters = qnxSb.numBlocks, - ClusterSize = qnxSb.blockSize, - Bootable = true, - Files = qnxSb.numInodes - qnxSb.freeInodes, - FilesSpecified = true, - FreeClusters = qnxSb.freeBlocks, - FreeClustersSpecified = true, - VolumeSerial = $"{qnxSb.serial:X16}", - CreationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.ctime), - CreationDateSpecified = true, - ModificationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.atime), - ModificationDateSpecified = true + Type = "QNX6 (Audi) filesystem", + Clusters = audiSb.numBlocks, + ClusterSize = audiSb.blockSize, + Bootable = true, + Files = audiSb.numInodes - audiSb.freeInodes, + FilesSpecified = true, + FreeClusters = audiSb.freeBlocks, + FreeClustersSpecified = true, + VolumeSerial = $"{audiSb.serial:X16}" }; - //xmlFSType.VolumeName = CurrentEncoding.GetString(qnxSb.volumeid); + //xmlFSType.VolumeName = CurrentEncoding.GetString(audiSb.id); information = sb.ToString(); + + return; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct RootNode - { - public readonly ulong size; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly uint[] pointers; - public readonly byte levels; - public readonly byte mode; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] spare; - } + sb.AppendLine("QNX6 filesystem"); + sb.AppendFormat("Checksum: 0x{0:X8}", qnxSb.checksum).AppendLine(); + sb.AppendFormat("Serial: 0x{0:X16}", qnxSb.checksum).AppendLine(); + sb.AppendFormat("Created on {0}", DateHandlers.UnixUnsignedToDateTime(qnxSb.ctime)).AppendLine(); + sb.AppendFormat("Last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(qnxSb.atime)).AppendLine(); + sb.AppendFormat("Flags: 0x{0:X8}", qnxSb.flags).AppendLine(); + sb.AppendFormat("Version1: 0x{0:X4}", qnxSb.version1).AppendLine(); + sb.AppendFormat("Version2: 0x{0:X4}", qnxSb.version2).AppendLine(); - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - public readonly uint magic; - public readonly uint checksum; - public readonly ulong serial; - public readonly uint ctime; - public readonly uint atime; - public readonly uint flags; - public readonly ushort version1; - public readonly ushort version2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] volumeid; - public readonly uint blockSize; - public readonly uint numInodes; - public readonly uint freeInodes; - public readonly uint numBlocks; - public readonly uint freeBlocks; - public readonly uint allocationGroup; - public readonly RootNode inode; - public readonly RootNode bitmap; - public readonly RootNode longfile; - public readonly RootNode unknown; - } + //sb.AppendFormat("Volume ID: \"{0}\"", CurrentEncoding.GetString(qnxSb.volumeid)).AppendLine(); + sb.AppendFormat("{0} bytes per block", qnxSb.blockSize).AppendLine(); + sb.AppendFormat("{0} inodes free of {1}", qnxSb.freeInodes, qnxSb.numInodes).AppendLine(); - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AudiSuperBlock + sb.AppendFormat("{0} blocks ({1} bytes) free of {2} ({3} bytes)", qnxSb.freeBlocks, + qnxSb.freeBlocks * qnxSb.blockSize, qnxSb.numBlocks, qnxSb.numBlocks * qnxSb.blockSize). + AppendLine(); + + XmlFsType = new FileSystemType { - public readonly uint magic; - public readonly uint checksum; - public readonly ulong serial; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] spare1; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] id; - public readonly uint blockSize; - public readonly uint numInodes; - public readonly uint freeInodes; - public readonly uint numBlocks; - public readonly uint freeBlocks; - public readonly uint spare2; - public readonly RootNode inode; - public readonly RootNode bitmap; - public readonly RootNode longfile; - public readonly RootNode unknown; - } + Type = "QNX6 filesystem", + Clusters = qnxSb.numBlocks, + ClusterSize = qnxSb.blockSize, + Bootable = true, + Files = qnxSb.numInodes - qnxSb.freeInodes, + FilesSpecified = true, + FreeClusters = qnxSb.freeBlocks, + FreeClustersSpecified = true, + VolumeSerial = $"{qnxSb.serial:X16}", + CreationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.ctime), + CreationDateSpecified = true, + ModificationDate = DateHandlers.UnixUnsignedToDateTime(qnxSb.atime), + ModificationDateSpecified = true + }; + + //xmlFSType.VolumeName = CurrentEncoding.GetString(qnxSb.volumeid); + + information = sb.ToString(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct RootNode + { + public readonly ulong size; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly uint[] pointers; + public readonly byte levels; + public readonly byte mode; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] spare; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + public readonly uint magic; + public readonly uint checksum; + public readonly ulong serial; + public readonly uint ctime; + public readonly uint atime; + public readonly uint flags; + public readonly ushort version1; + public readonly ushort version2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] volumeid; + public readonly uint blockSize; + public readonly uint numInodes; + public readonly uint freeInodes; + public readonly uint numBlocks; + public readonly uint freeBlocks; + public readonly uint allocationGroup; + public readonly RootNode inode; + public readonly RootNode bitmap; + public readonly RootNode longfile; + public readonly RootNode unknown; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AudiSuperBlock + { + public readonly uint magic; + public readonly uint checksum; + public readonly ulong serial; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] spare1; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] id; + public readonly uint blockSize; + public readonly uint numInodes; + public readonly uint freeInodes; + public readonly uint numBlocks; + public readonly uint freeBlocks; + public readonly uint spare2; + public readonly RootNode inode; + public readonly RootNode bitmap; + public readonly RootNode longfile; + public readonly RootNode unknown; } } \ No newline at end of file diff --git a/Aaru.Filesystems/RBF.cs b/Aaru.Filesystems/RBF.cs index 5fe61d9d5..3c5415bde 100644 --- a/Aaru.Filesystems/RBF.cs +++ b/Aaru.Filesystems/RBF.cs @@ -41,373 +41,372 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Locus filesystem +public sealed class RBF : IFilesystem { + /// Magic number for OS-9. Same for OS-9000? + const uint RBF_SYNC = 0x4372757A; + const uint RBF_CNYS = 0x7A757243; + /// - /// Implements detection of the Locus filesystem - public sealed class RBF : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "OS-9 Random Block File Plugin"; + /// + public Guid Id => new("E864E45B-0B52-4D29-A858-7BDFA9199FB2"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Magic number for OS-9. Same for OS-9000? - const uint RBF_SYNC = 0x4372757A; - const uint RBF_CNYS = 0x7A757243; + if(imagePlugin.Info.SectorSize < 256) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "OS-9 Random Block File Plugin"; - /// - public Guid Id => new("E864E45B-0B52-4D29-A858-7BDFA9199FB2"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + // Documentation says ID should be sector 0 + // I've found that OS-9/X68000 has it on sector 4 + // I've read OS-9/Apple2 has it on sector 15 + foreach(int i in new[] + { + 0, 4, 15 + }) { - if(imagePlugin.Info.SectorSize < 256) + ulong location = (ulong)i; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + if(partition.Start + location + sbSize >= imagePlugin.Info.Sectors) + break; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) return false; - // Documentation says ID should be sector 0 - // I've found that OS-9/X68000 has it on sector 4 - // I've read OS-9/Apple2 has it on sector 15 - foreach(int i in new[] - { - 0, 4, 15 - }) - { - ulong location = (ulong)i; + if(sector.Length < Marshal.SizeOf()) + return false; - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + IdSector rbfSb = Marshal.ByteArrayToStructureBigEndian(sector); + NewIdSector rbf9000Sb = Marshal.ByteArrayToStructureBigEndian(sector); - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; + AaruConsole.DebugWriteLine("RBF plugin", + "magic at {0} = 0x{1:X8} or 0x{2:X8} (expected 0x{3:X8} or 0x{4:X8})", + location, rbfSb.dd_sync, rbf9000Sb.rid_sync, RBF_SYNC, RBF_CNYS); - if(partition.Start + location + sbSize >= imagePlugin.Info.Sectors) - break; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - IdSector rbfSb = Marshal.ByteArrayToStructureBigEndian(sector); - NewIdSector rbf9000Sb = Marshal.ByteArrayToStructureBigEndian(sector); - - AaruConsole.DebugWriteLine("RBF plugin", - "magic at {0} = 0x{1:X8} or 0x{2:X8} (expected 0x{3:X8} or 0x{4:X8})", - location, rbfSb.dd_sync, rbf9000Sb.rid_sync, RBF_SYNC, RBF_CNYS); - - if(rbfSb.dd_sync == RBF_SYNC || - rbf9000Sb.rid_sync == RBF_SYNC || - rbf9000Sb.rid_sync == RBF_CNYS) - return true; - } - - return false; + if(rbfSb.dd_sync == RBF_SYNC || + rbf9000Sb.rid_sync == RBF_SYNC || + rbf9000Sb.rid_sync == RBF_CNYS) + return true; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + return false; + } - if(imagePlugin.Info.SectorSize < 256) - return; + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; - var rbfSb = new IdSector(); - var rbf9000Sb = new NewIdSector(); + if(imagePlugin.Info.SectorSize < 256) + return; - foreach(int i in new[] - { - 0, 4, 15 - }) - { - ulong location = (ulong)i; - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + var rbfSb = new IdSector(); + var rbf9000Sb = new NewIdSector(); - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < Marshal.SizeOf()) - return; - - rbfSb = Marshal.ByteArrayToStructureBigEndian(sector); - rbf9000Sb = Marshal.ByteArrayToStructureBigEndian(sector); - - AaruConsole.DebugWriteLine("RBF plugin", - "magic at {0} = 0x{1:X8} or 0x{2:X8} (expected 0x{3:X8} or 0x{4:X8})", - location, rbfSb.dd_sync, rbf9000Sb.rid_sync, RBF_SYNC, RBF_CNYS); - - if(rbfSb.dd_sync == RBF_SYNC || - rbf9000Sb.rid_sync == RBF_SYNC || - rbf9000Sb.rid_sync == RBF_CNYS) - break; - } - - if(rbfSb.dd_sync != RBF_SYNC && - rbf9000Sb.rid_sync != RBF_SYNC && - rbf9000Sb.rid_sync != RBF_CNYS) - return; - - if(rbf9000Sb.rid_sync == RBF_CNYS) - rbf9000Sb = (NewIdSector)Marshal.SwapStructureMembersEndian(rbf9000Sb); - - var sb = new StringBuilder(); - - sb.AppendLine("OS-9 Random Block File"); - - if(rbf9000Sb.rid_sync == RBF_SYNC) - { - sb.AppendFormat("Volume ID: {0:X8}", rbf9000Sb.rid_diskid).AppendLine(); - sb.AppendFormat("{0} blocks in volume", rbf9000Sb.rid_totblocks).AppendLine(); - sb.AppendFormat("{0} cylinders", rbf9000Sb.rid_cylinders).AppendLine(); - sb.AppendFormat("{0} blocks in cylinder 0", rbf9000Sb.rid_cyl0size).AppendLine(); - sb.AppendFormat("{0} blocks per cylinder", rbf9000Sb.rid_cylsize).AppendLine(); - sb.AppendFormat("{0} heads", rbf9000Sb.rid_heads).AppendLine(); - sb.AppendFormat("{0} bytes per block", rbf9000Sb.rid_blocksize).AppendLine(); - - // TODO: Convert to flags? - sb.AppendLine((rbf9000Sb.rid_format & 0x01) == 0x01 ? "Disk is double sided" : "Disk is single sided"); - - sb.AppendLine((rbf9000Sb.rid_format & 0x02) == 0x02 ? "Disk is double density" - : "Disk is single density"); - - if((rbf9000Sb.rid_format & 0x10) == 0x10) - sb.AppendLine("Disk is 384 TPI"); - else if((rbf9000Sb.rid_format & 0x08) == 0x08) - sb.AppendLine("Disk is 192 TPI"); - else if((rbf9000Sb.rid_format & 0x04) == 0x04) - sb.AppendLine("Disk is 96 TPI or 135 TPI"); - else - sb.AppendLine("Disk is 48 TPI"); - - sb.AppendFormat("Allocation bitmap descriptor starts at block {0}", - rbf9000Sb.rid_bitmap == 0 ? 1 : rbf9000Sb.rid_bitmap).AppendLine(); - - if(rbf9000Sb.rid_firstboot > 0) - sb.AppendFormat("Debugger descriptor starts at block {0}", rbf9000Sb.rid_firstboot).AppendLine(); - - if(rbf9000Sb.rid_bootfile > 0) - sb.AppendFormat("Boot file descriptor starts at block {0}", rbf9000Sb.rid_bootfile).AppendLine(); - - sb.AppendFormat("Root directory descriptor starts at block {0}", rbf9000Sb.rid_rootdir).AppendLine(); - - sb.AppendFormat("Disk is owned by group {0} user {1}", rbf9000Sb.rid_group, rbf9000Sb.rid_owner). - AppendLine(); - - sb.AppendFormat("Volume was created on {0}", DateHandlers.UnixToDateTime(rbf9000Sb.rid_ctime)). - AppendLine(); - - sb.AppendFormat("Volume's identification block was last written on {0}", - DateHandlers.UnixToDateTime(rbf9000Sb.rid_mtime)).AppendLine(); - - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(rbf9000Sb.rid_name, Encoding)). - AppendLine(); - - XmlFsType = new FileSystemType + foreach(int i in new[] { - Type = "OS-9 Random Block File", - Bootable = rbf9000Sb.rid_bootfile > 0, - ClusterSize = rbf9000Sb.rid_blocksize, - Clusters = rbf9000Sb.rid_totblocks, - CreationDate = DateHandlers.UnixToDateTime(rbf9000Sb.rid_ctime), - CreationDateSpecified = true, - ModificationDate = DateHandlers.UnixToDateTime(rbf9000Sb.rid_mtime), - ModificationDateSpecified = true, - VolumeName = StringHandlers.CToString(rbf9000Sb.rid_name, Encoding), - VolumeSerial = $"{rbf9000Sb.rid_diskid:X8}" - }; - } + 0, 4, 15 + }) + { + ulong location = (ulong)i; + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < Marshal.SizeOf()) + return; + + rbfSb = Marshal.ByteArrayToStructureBigEndian(sector); + rbf9000Sb = Marshal.ByteArrayToStructureBigEndian(sector); + + AaruConsole.DebugWriteLine("RBF plugin", + "magic at {0} = 0x{1:X8} or 0x{2:X8} (expected 0x{3:X8} or 0x{4:X8})", + location, rbfSb.dd_sync, rbf9000Sb.rid_sync, RBF_SYNC, RBF_CNYS); + + if(rbfSb.dd_sync == RBF_SYNC || + rbf9000Sb.rid_sync == RBF_SYNC || + rbf9000Sb.rid_sync == RBF_CNYS) + break; + } + + if(rbfSb.dd_sync != RBF_SYNC && + rbf9000Sb.rid_sync != RBF_SYNC && + rbf9000Sb.rid_sync != RBF_CNYS) + return; + + if(rbf9000Sb.rid_sync == RBF_CNYS) + rbf9000Sb = (NewIdSector)Marshal.SwapStructureMembersEndian(rbf9000Sb); + + var sb = new StringBuilder(); + + sb.AppendLine("OS-9 Random Block File"); + + if(rbf9000Sb.rid_sync == RBF_SYNC) + { + sb.AppendFormat("Volume ID: {0:X8}", rbf9000Sb.rid_diskid).AppendLine(); + sb.AppendFormat("{0} blocks in volume", rbf9000Sb.rid_totblocks).AppendLine(); + sb.AppendFormat("{0} cylinders", rbf9000Sb.rid_cylinders).AppendLine(); + sb.AppendFormat("{0} blocks in cylinder 0", rbf9000Sb.rid_cyl0size).AppendLine(); + sb.AppendFormat("{0} blocks per cylinder", rbf9000Sb.rid_cylsize).AppendLine(); + sb.AppendFormat("{0} heads", rbf9000Sb.rid_heads).AppendLine(); + sb.AppendFormat("{0} bytes per block", rbf9000Sb.rid_blocksize).AppendLine(); + + // TODO: Convert to flags? + sb.AppendLine((rbf9000Sb.rid_format & 0x01) == 0x01 ? "Disk is double sided" : "Disk is single sided"); + + sb.AppendLine((rbf9000Sb.rid_format & 0x02) == 0x02 ? "Disk is double density" + : "Disk is single density"); + + if((rbf9000Sb.rid_format & 0x10) == 0x10) + sb.AppendLine("Disk is 384 TPI"); + else if((rbf9000Sb.rid_format & 0x08) == 0x08) + sb.AppendLine("Disk is 192 TPI"); + else if((rbf9000Sb.rid_format & 0x04) == 0x04) + sb.AppendLine("Disk is 96 TPI or 135 TPI"); else + sb.AppendLine("Disk is 48 TPI"); + + sb.AppendFormat("Allocation bitmap descriptor starts at block {0}", + rbf9000Sb.rid_bitmap == 0 ? 1 : rbf9000Sb.rid_bitmap).AppendLine(); + + if(rbf9000Sb.rid_firstboot > 0) + sb.AppendFormat("Debugger descriptor starts at block {0}", rbf9000Sb.rid_firstboot).AppendLine(); + + if(rbf9000Sb.rid_bootfile > 0) + sb.AppendFormat("Boot file descriptor starts at block {0}", rbf9000Sb.rid_bootfile).AppendLine(); + + sb.AppendFormat("Root directory descriptor starts at block {0}", rbf9000Sb.rid_rootdir).AppendLine(); + + sb.AppendFormat("Disk is owned by group {0} user {1}", rbf9000Sb.rid_group, rbf9000Sb.rid_owner). + AppendLine(); + + sb.AppendFormat("Volume was created on {0}", DateHandlers.UnixToDateTime(rbf9000Sb.rid_ctime)). + AppendLine(); + + sb.AppendFormat("Volume's identification block was last written on {0}", + DateHandlers.UnixToDateTime(rbf9000Sb.rid_mtime)).AppendLine(); + + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(rbf9000Sb.rid_name, Encoding)). + AppendLine(); + + XmlFsType = new FileSystemType { - sb.AppendFormat("Volume ID: {0:X4}", rbfSb.dd_dsk).AppendLine(); - sb.AppendFormat("{0} blocks in volume", LSNToUInt32(rbfSb.dd_tot)).AppendLine(); - sb.AppendFormat("{0} tracks", rbfSb.dd_tks).AppendLine(); - sb.AppendFormat("{0} sectors per track", rbfSb.dd_spt).AppendLine(); - sb.AppendFormat("{0} bytes per sector", 256 << rbfSb.dd_lsnsize).AppendLine(); - - sb.AppendFormat("{0} sectors per cluster ({1} bytes)", rbfSb.dd_bit, - rbfSb.dd_bit * (256 << rbfSb.dd_lsnsize)).AppendLine(); - - // TODO: Convert to flags? - sb.AppendLine((rbfSb.dd_fmt & 0x01) == 0x01 ? "Disk is double sided" : "Disk is single sided"); - sb.AppendLine((rbfSb.dd_fmt & 0x02) == 0x02 ? "Disk is double density" : "Disk is single density"); - - if((rbfSb.dd_fmt & 0x10) == 0x10) - sb.AppendLine("Disk is 384 TPI"); - else if((rbfSb.dd_fmt & 0x08) == 0x08) - sb.AppendLine("Disk is 192 TPI"); - else if((rbfSb.dd_fmt & 0x04) == 0x04) - sb.AppendLine("Disk is 96 TPI or 135 TPI"); - else - sb.AppendLine("Disk is 48 TPI"); - - sb.AppendFormat("Allocation bitmap descriptor starts at block {0}", - rbfSb.dd_maplsn == 0 ? 1 : rbfSb.dd_maplsn).AppendLine(); - - sb.AppendFormat("{0} bytes in allocation bitmap", rbfSb.dd_map).AppendLine(); - - if(LSNToUInt32(rbfSb.dd_bt) > 0 && - rbfSb.dd_bsz > 0) - sb.AppendFormat("Boot file starts at block {0} and has {1} bytes", LSNToUInt32(rbfSb.dd_bt), - rbfSb.dd_bsz).AppendLine(); - - sb.AppendFormat("Root directory descriptor starts at block {0}", LSNToUInt32(rbfSb.dd_dir)). - AppendLine(); - - sb.AppendFormat("Disk is owned by user {0}", rbfSb.dd_own).AppendLine(); - sb.AppendFormat("Volume was created on {0}", DateHandlers.Os9ToDateTime(rbfSb.dd_dat)).AppendLine(); - sb.AppendFormat("Volume attributes: {0:X2}", rbfSb.dd_att).AppendLine(); - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(rbfSb.dd_nam, Encoding)).AppendLine(); - - sb.AppendFormat("Path descriptor options: {0}", StringHandlers.CToString(rbfSb.dd_opt, Encoding)). - AppendLine(); - - XmlFsType = new FileSystemType - { - Type = "OS-9 Random Block File", - Bootable = LSNToUInt32(rbfSb.dd_bt) > 0 && rbfSb.dd_bsz > 0, - ClusterSize = (uint)(rbfSb.dd_bit * (256 << rbfSb.dd_lsnsize)), - Clusters = LSNToUInt32(rbfSb.dd_tot), - CreationDate = DateHandlers.Os9ToDateTime(rbfSb.dd_dat), - CreationDateSpecified = true, - VolumeName = StringHandlers.CToString(rbfSb.dd_nam, Encoding), - VolumeSerial = $"{rbfSb.dd_dsk:X4}" - }; - } - - information = sb.ToString(); + Type = "OS-9 Random Block File", + Bootable = rbf9000Sb.rid_bootfile > 0, + ClusterSize = rbf9000Sb.rid_blocksize, + Clusters = rbf9000Sb.rid_totblocks, + CreationDate = DateHandlers.UnixToDateTime(rbf9000Sb.rid_ctime), + CreationDateSpecified = true, + ModificationDate = DateHandlers.UnixToDateTime(rbf9000Sb.rid_mtime), + ModificationDateSpecified = true, + VolumeName = StringHandlers.CToString(rbf9000Sb.rid_name, Encoding), + VolumeSerial = $"{rbf9000Sb.rid_diskid:X8}" + }; } - - static uint LSNToUInt32(byte[] lsn) + else { - if(lsn == null || - lsn.Length != 3) - return 0; + sb.AppendFormat("Volume ID: {0:X4}", rbfSb.dd_dsk).AppendLine(); + sb.AppendFormat("{0} blocks in volume", LSNToUInt32(rbfSb.dd_tot)).AppendLine(); + sb.AppendFormat("{0} tracks", rbfSb.dd_tks).AppendLine(); + sb.AppendFormat("{0} sectors per track", rbfSb.dd_spt).AppendLine(); + sb.AppendFormat("{0} bytes per sector", 256 << rbfSb.dd_lsnsize).AppendLine(); - return (uint)((lsn[0] << 16) + (lsn[1] << 8) + lsn[2]); + sb.AppendFormat("{0} sectors per cluster ({1} bytes)", rbfSb.dd_bit, + rbfSb.dd_bit * (256 << rbfSb.dd_lsnsize)).AppendLine(); + + // TODO: Convert to flags? + sb.AppendLine((rbfSb.dd_fmt & 0x01) == 0x01 ? "Disk is double sided" : "Disk is single sided"); + sb.AppendLine((rbfSb.dd_fmt & 0x02) == 0x02 ? "Disk is double density" : "Disk is single density"); + + if((rbfSb.dd_fmt & 0x10) == 0x10) + sb.AppendLine("Disk is 384 TPI"); + else if((rbfSb.dd_fmt & 0x08) == 0x08) + sb.AppendLine("Disk is 192 TPI"); + else if((rbfSb.dd_fmt & 0x04) == 0x04) + sb.AppendLine("Disk is 96 TPI or 135 TPI"); + else + sb.AppendLine("Disk is 48 TPI"); + + sb.AppendFormat("Allocation bitmap descriptor starts at block {0}", + rbfSb.dd_maplsn == 0 ? 1 : rbfSb.dd_maplsn).AppendLine(); + + sb.AppendFormat("{0} bytes in allocation bitmap", rbfSb.dd_map).AppendLine(); + + if(LSNToUInt32(rbfSb.dd_bt) > 0 && + rbfSb.dd_bsz > 0) + sb.AppendFormat("Boot file starts at block {0} and has {1} bytes", LSNToUInt32(rbfSb.dd_bt), + rbfSb.dd_bsz).AppendLine(); + + sb.AppendFormat("Root directory descriptor starts at block {0}", LSNToUInt32(rbfSb.dd_dir)). + AppendLine(); + + sb.AppendFormat("Disk is owned by user {0}", rbfSb.dd_own).AppendLine(); + sb.AppendFormat("Volume was created on {0}", DateHandlers.Os9ToDateTime(rbfSb.dd_dat)).AppendLine(); + sb.AppendFormat("Volume attributes: {0:X2}", rbfSb.dd_att).AppendLine(); + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(rbfSb.dd_nam, Encoding)).AppendLine(); + + sb.AppendFormat("Path descriptor options: {0}", StringHandlers.CToString(rbfSb.dd_opt, Encoding)). + AppendLine(); + + XmlFsType = new FileSystemType + { + Type = "OS-9 Random Block File", + Bootable = LSNToUInt32(rbfSb.dd_bt) > 0 && rbfSb.dd_bsz > 0, + ClusterSize = (uint)(rbfSb.dd_bit * (256 << rbfSb.dd_lsnsize)), + Clusters = LSNToUInt32(rbfSb.dd_tot), + CreationDate = DateHandlers.Os9ToDateTime(rbfSb.dd_dat), + CreationDateSpecified = true, + VolumeName = StringHandlers.CToString(rbfSb.dd_nam, Encoding), + VolumeSerial = $"{rbfSb.dd_dsk:X4}" + }; } - /// Identification sector. Wherever the sector this resides on, becomes LSN 0. - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct IdSector - { - /// Sectors on disk - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] dd_tot; - /// Tracks - public readonly byte dd_tks; - /// Bytes in allocation map - public readonly ushort dd_map; - /// Sectors per cluster - public readonly ushort dd_bit; - /// LSN of root directory - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] dd_dir; - /// Owner ID - public readonly ushort dd_own; - /// Attributes - public readonly byte dd_att; - /// Disk ID - public readonly ushort dd_dsk; - /// Format byte - public readonly byte dd_fmt; - /// Sectors per track - public readonly ushort dd_spt; - /// Reserved - public readonly ushort dd_res; - /// LSN of boot file - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] dd_bt; - /// Size of boot file - public readonly ushort dd_bsz; - /// Creation date - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] dd_dat; - /// Volume name - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] dd_nam; - /// Path options - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] dd_opt; - /// Reserved - public readonly byte reserved; - /// Magic number - public readonly uint dd_sync; - /// LSN of allocation map - public readonly uint dd_maplsn; - /// Size of an LSN - public readonly ushort dd_lsnsize; - /// Version ID - public readonly ushort dd_versid; - } + information = sb.ToString(); + } - /// - /// Identification sector. Wherever the sector this resides on, becomes LSN 0. Introduced on OS-9000, this can be - /// big or little endian. - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct NewIdSector - { - /// Magic number - public readonly uint rid_sync; - /// Disk ID - public readonly uint rid_diskid; - /// Sectors on disk - public readonly uint rid_totblocks; - /// Cylinders - public readonly ushort rid_cylinders; - /// Sectors in cylinder 0 - public readonly ushort rid_cyl0size; - /// Sectors per cylinder - public readonly ushort rid_cylsize; - /// Heads - public readonly ushort rid_heads; - /// Bytes per sector - public readonly ushort rid_blocksize; - /// Disk format - public readonly ushort rid_format; - /// Flags - public readonly ushort rid_flags; - /// Padding - public readonly ushort rid_unused1; - /// Sector of allocation bitmap - public readonly uint rid_bitmap; - /// Sector of debugger FD - public readonly uint rid_firstboot; - /// Sector of bootfile FD - public readonly uint rid_bootfile; - /// Sector of root directory FD - public readonly uint rid_rootdir; - /// Group owner of media - public readonly ushort rid_group; - /// Owner of media - public readonly ushort rid_owner; - /// Creation time - public readonly uint rid_ctime; - /// Last write time for this structure - public readonly uint rid_mtime; - /// Volume name - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] rid_name; - /// Endian flag - public readonly byte rid_endflag; - /// Padding - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] rid_unused2; - /// Parity - public readonly uint rid_parity; - } + static uint LSNToUInt32(byte[] lsn) + { + if(lsn == null || + lsn.Length != 3) + return 0; + + return (uint)((lsn[0] << 16) + (lsn[1] << 8) + lsn[2]); + } + + /// Identification sector. Wherever the sector this resides on, becomes LSN 0. + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct IdSector + { + /// Sectors on disk + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] dd_tot; + /// Tracks + public readonly byte dd_tks; + /// Bytes in allocation map + public readonly ushort dd_map; + /// Sectors per cluster + public readonly ushort dd_bit; + /// LSN of root directory + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] dd_dir; + /// Owner ID + public readonly ushort dd_own; + /// Attributes + public readonly byte dd_att; + /// Disk ID + public readonly ushort dd_dsk; + /// Format byte + public readonly byte dd_fmt; + /// Sectors per track + public readonly ushort dd_spt; + /// Reserved + public readonly ushort dd_res; + /// LSN of boot file + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] dd_bt; + /// Size of boot file + public readonly ushort dd_bsz; + /// Creation date + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] dd_dat; + /// Volume name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] dd_nam; + /// Path options + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] dd_opt; + /// Reserved + public readonly byte reserved; + /// Magic number + public readonly uint dd_sync; + /// LSN of allocation map + public readonly uint dd_maplsn; + /// Size of an LSN + public readonly ushort dd_lsnsize; + /// Version ID + public readonly ushort dd_versid; + } + + /// + /// Identification sector. Wherever the sector this resides on, becomes LSN 0. Introduced on OS-9000, this can be + /// big or little endian. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct NewIdSector + { + /// Magic number + public readonly uint rid_sync; + /// Disk ID + public readonly uint rid_diskid; + /// Sectors on disk + public readonly uint rid_totblocks; + /// Cylinders + public readonly ushort rid_cylinders; + /// Sectors in cylinder 0 + public readonly ushort rid_cyl0size; + /// Sectors per cylinder + public readonly ushort rid_cylsize; + /// Heads + public readonly ushort rid_heads; + /// Bytes per sector + public readonly ushort rid_blocksize; + /// Disk format + public readonly ushort rid_format; + /// Flags + public readonly ushort rid_flags; + /// Padding + public readonly ushort rid_unused1; + /// Sector of allocation bitmap + public readonly uint rid_bitmap; + /// Sector of debugger FD + public readonly uint rid_firstboot; + /// Sector of bootfile FD + public readonly uint rid_bootfile; + /// Sector of root directory FD + public readonly uint rid_rootdir; + /// Group owner of media + public readonly ushort rid_group; + /// Owner of media + public readonly ushort rid_owner; + /// Creation time + public readonly uint rid_ctime; + /// Last write time for this structure + public readonly uint rid_mtime; + /// Volume name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] rid_name; + /// Endian flag + public readonly byte rid_endflag; + /// Padding + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] rid_unused2; + /// Parity + public readonly uint rid_parity; } } \ No newline at end of file diff --git a/Aaru.Filesystems/RT11.cs b/Aaru.Filesystems/RT11.cs index 4bacc5475..f559c0811 100644 --- a/Aaru.Filesystems/RT11.cs +++ b/Aaru.Filesystems/RT11.cs @@ -42,146 +42,145 @@ using Schemas; using Encoding = System.Text.Encoding; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from http://www.trailing-edge.com/~shoppa/rt11fs/ +/// +/// Implements detection of the DEC RT-11 filesystem +public sealed class RT11 : IFilesystem { - // Information from http://www.trailing-edge.com/~shoppa/rt11fs/ /// - /// Implements detection of the DEC RT-11 filesystem - public sealed class RT11 : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "RT-11 file system"; + /// + public Guid Id => new("DB3E2F98-8F98-463C-8126-E937843DA024"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "RT-11 file system"; - /// - public Guid Id => new("DB3E2F98-8F98-463C-8126-E937843DA024"); - /// - public string Author => "Natalia Portillo"; + if(1 + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + byte[] magicB = new byte[12]; + ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] hbSector); + + if(errno != ErrorNumber.NoError) + return false; + + if(hbSector.Length < 512) + return false; + + Array.Copy(hbSector, 0x1F0, magicB, 0, 12); + string magic = Encoding.ASCII.GetString(magicB); + + return magic == "DECRT11A "; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = new Radix50(); + information = ""; + + var sb = new StringBuilder(); + + ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] hbSector); + + if(errno != ErrorNumber.NoError) + return; + + HomeBlock homeblock = Marshal.ByteArrayToStructureLittleEndian(hbSector); + + /* TODO: Is this correct? + * Assembler: + * MOV address, R0 + * CLR R1 + * MOV #255., R2 + * 10$: ADD (R0)+, R1 + * SOB R2, 10$ + * MOV 1,@R0 + */ + ushort check = 0; + + for(int i = 0; i < 512; i += 2) + check += BitConverter.ToUInt16(hbSector, i); + + sb.AppendFormat("Volume format is {0}", + StringHandlers.SpacePaddedToString(homeblock.format, Encoding.ASCII)).AppendLine(); + + sb.AppendFormat("{0} sectors per cluster ({1} bytes)", homeblock.cluster, homeblock.cluster * 512). + AppendLine(); + + sb.AppendFormat("First directory segment starts at block {0}", homeblock.rootBlock).AppendLine(); + sb.AppendFormat("Volume owner is \"{0}\"", Encoding.GetString(homeblock.ownername).TrimEnd()).AppendLine(); + sb.AppendFormat("Volume label: \"{0}\"", Encoding.GetString(homeblock.volname).TrimEnd()).AppendLine(); + sb.AppendFormat("Checksum: 0x{0:X4} (calculated 0x{1:X4})", homeblock.checksum, check).AppendLine(); + + imagePlugin.ReadSector(0, out byte[] bootBlock); + + XmlFsType = new FileSystemType { - if(1 + partition.Start >= partition.End) - return false; + Type = "RT-11", + ClusterSize = (uint)(homeblock.cluster * 512), + Clusters = homeblock.cluster, + VolumeName = StringHandlers.SpacePaddedToString(homeblock.volname, Encoding), + Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlock) + }; - byte[] magicB = new byte[12]; - ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] hbSector); + information = sb.ToString(); + } - if(errno != ErrorNumber.NoError) - return false; - - if(hbSector.Length < 512) - return false; - - Array.Copy(hbSector, 0x1F0, magicB, 0, 12); - string magic = Encoding.ASCII.GetString(magicB); - - return magic == "DECRT11A "; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = new Radix50(); - information = ""; - - var sb = new StringBuilder(); - - ErrorNumber errno = imagePlugin.ReadSector(1 + partition.Start, out byte[] hbSector); - - if(errno != ErrorNumber.NoError) - return; - - HomeBlock homeblock = Marshal.ByteArrayToStructureLittleEndian(hbSector); - - /* TODO: Is this correct? - * Assembler: - * MOV address, R0 - * CLR R1 - * MOV #255., R2 - * 10$: ADD (R0)+, R1 - * SOB R2, 10$ - * MOV 1,@R0 - */ - ushort check = 0; - - for(int i = 0; i < 512; i += 2) - check += BitConverter.ToUInt16(hbSector, i); - - sb.AppendFormat("Volume format is {0}", - StringHandlers.SpacePaddedToString(homeblock.format, Encoding.ASCII)).AppendLine(); - - sb.AppendFormat("{0} sectors per cluster ({1} bytes)", homeblock.cluster, homeblock.cluster * 512). - AppendLine(); - - sb.AppendFormat("First directory segment starts at block {0}", homeblock.rootBlock).AppendLine(); - sb.AppendFormat("Volume owner is \"{0}\"", Encoding.GetString(homeblock.ownername).TrimEnd()).AppendLine(); - sb.AppendFormat("Volume label: \"{0}\"", Encoding.GetString(homeblock.volname).TrimEnd()).AppendLine(); - sb.AppendFormat("Checksum: 0x{0:X4} (calculated 0x{1:X4})", homeblock.checksum, check).AppendLine(); - - imagePlugin.ReadSector(0, out byte[] bootBlock); - - XmlFsType = new FileSystemType - { - Type = "RT-11", - ClusterSize = (uint)(homeblock.cluster * 512), - Clusters = homeblock.cluster, - VolumeName = StringHandlers.SpacePaddedToString(homeblock.volname, Encoding), - Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(bootBlock) - }; - - information = sb.ToString(); - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct HomeBlock - { - /// Bad block replacement table - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 130)] - public readonly byte[] badBlockTable; - /// Unused - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] unused; - /// INITIALIZE/RESTORE data area - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 38)] - public readonly byte[] initArea; - /// BUP information area - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)] - public readonly byte[] bupInformation; - /// Empty - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] - public readonly byte[] empty; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] reserved1; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] reserved2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] - public readonly byte[] empty2; - /// Cluster size - public readonly ushort cluster; - /// Block of the first directory segment - public readonly ushort rootBlock; - /// "V3A" in Radix-50 - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly byte[] systemVersion; - /// Name of the volume, 12 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] volname; - /// Name of the volume owner, 12 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] ownername; - /// RT11 defines it as "DECRT11A ", 12 bytes - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] format; - /// Unused - public readonly ushort unused2; - /// Checksum of preceding 255 words (16 bit units) - public readonly ushort checksum; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct HomeBlock + { + /// Bad block replacement table + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 130)] + public readonly byte[] badBlockTable; + /// Unused + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] unused; + /// INITIALIZE/RESTORE data area + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 38)] + public readonly byte[] initArea; + /// BUP information area + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)] + public readonly byte[] bupInformation; + /// Empty + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 260)] + public readonly byte[] empty; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] reserved1; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] reserved2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 14)] + public readonly byte[] empty2; + /// Cluster size + public readonly ushort cluster; + /// Block of the first directory segment + public readonly ushort rootBlock; + /// "V3A" in Radix-50 + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly byte[] systemVersion; + /// Name of the volume, 12 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] volname; + /// Name of the volume owner, 12 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] ownername; + /// RT11 defines it as "DECRT11A ", 12 bytes + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] format; + /// Unused + public readonly ushort unused2; + /// Checksum of preceding 255 words (16 bit units) + public readonly ushort checksum; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ReFS.cs b/Aaru.Filesystems/ReFS.cs index 30063e6ab..db86be220 100644 --- a/Aaru.Filesystems/ReFS.cs +++ b/Aaru.Filesystems/ReFS.cs @@ -42,152 +42,151 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of Microsoft's Resilient filesystem (ReFS) +public sealed class ReFS : IFilesystem { - /// - /// Implements detection of Microsoft's Resilient filesystem (ReFS) - public sealed class ReFS : IFilesystem + const uint FSRS = 0x53525346; + readonly byte[] _signature = { - const uint FSRS = 0x53525346; - readonly byte[] _signature = + 0x52, 0x65, 0x46, 0x53, 0x00, 0x00, 0x00, 0x00 + }; + /// + public string Name => "Resilient File System plugin"; + /// + public Guid Id => new("37766C4E-EBF5-4113-A712-B758B756ABD6"); + /// + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + if(partition.Start + sbSize >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < Marshal.SizeOf()) + return false; + + VolumeHeader vhdr = Marshal.ByteArrayToStructureLittleEndian(sector); + + return vhdr.identifier == FSRS && ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero) && + vhdr.signature.SequenceEqual(_signature); + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = Encoding.UTF8; + information = ""; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + if(partition.Start + sbSize >= partition.End) + return; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < Marshal.SizeOf()) + return; + + VolumeHeader vhdr = Marshal.ByteArrayToStructureLittleEndian(sector); + + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.jump empty? = {0}", + ArrayHelpers.ArrayIsNullOrEmpty(vhdr.jump)); + + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.signature = {0}", + StringHandlers.CToString(vhdr.signature)); + + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.mustBeZero empty? = {0}", + ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero)); + + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.identifier = {0}", + StringHandlers.CToString(BitConverter.GetBytes(vhdr.identifier))); + + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.length = {0}", vhdr.length); + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.checksum = 0x{0:X4}", vhdr.checksum); + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectors = {0}", vhdr.sectors); + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.bytesPerSector = {0}", vhdr.bytesPerSector); + + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectorsPerCluster = {0}", vhdr.sectorsPerCluster); + + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown1 zero? = {0}", vhdr.unknown1 == 0); + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown2 zero? = {0}", vhdr.unknown2 == 0); + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown3 zero? = {0}", vhdr.unknown3 == 0); + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown4 zero? = {0}", vhdr.unknown4 == 0); + + AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown5 empty? = {0}", + ArrayHelpers.ArrayIsNullOrEmpty(vhdr.unknown5)); + + if(vhdr.identifier != FSRS || + !ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero) || + !vhdr.signature.SequenceEqual(_signature)) + return; + + var sb = new StringBuilder(); + + sb.AppendLine("Microsoft Resilient File System"); + sb.AppendFormat("Volume uses {0} bytes per sector", vhdr.bytesPerSector).AppendLine(); + + sb.AppendFormat("Volume uses {0} sectors per cluster ({1} bytes)", vhdr.sectorsPerCluster, + vhdr.sectorsPerCluster * vhdr.bytesPerSector).AppendLine(); + + sb.AppendFormat("Volume has {0} sectors ({1} bytes)", vhdr.sectors, vhdr.sectors * vhdr.bytesPerSector). + AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - 0x52, 0x65, 0x46, 0x53, 0x00, 0x00, 0x00, 0x00 + Type = "Resilient File System", + ClusterSize = vhdr.bytesPerSector * vhdr.sectorsPerCluster, + Clusters = vhdr.sectors / vhdr.sectorsPerCluster }; - /// - public string Name => "Resilient File System plugin"; - /// - public Guid Id => new("37766C4E-EBF5-4113-A712-B758B756ABD6"); - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Author => "Natalia Portillo"; + } - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - if(partition.Start + sbSize >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - VolumeHeader vhdr = Marshal.ByteArrayToStructureLittleEndian(sector); - - return vhdr.identifier == FSRS && ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero) && - vhdr.signature.SequenceEqual(_signature); - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = Encoding.UTF8; - information = ""; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - if(partition.Start + sbSize >= partition.End) - return; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < Marshal.SizeOf()) - return; - - VolumeHeader vhdr = Marshal.ByteArrayToStructureLittleEndian(sector); - - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.jump empty? = {0}", - ArrayHelpers.ArrayIsNullOrEmpty(vhdr.jump)); - - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.signature = {0}", - StringHandlers.CToString(vhdr.signature)); - - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.mustBeZero empty? = {0}", - ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero)); - - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.identifier = {0}", - StringHandlers.CToString(BitConverter.GetBytes(vhdr.identifier))); - - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.length = {0}", vhdr.length); - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.checksum = 0x{0:X4}", vhdr.checksum); - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectors = {0}", vhdr.sectors); - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.bytesPerSector = {0}", vhdr.bytesPerSector); - - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.sectorsPerCluster = {0}", vhdr.sectorsPerCluster); - - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown1 zero? = {0}", vhdr.unknown1 == 0); - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown2 zero? = {0}", vhdr.unknown2 == 0); - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown3 zero? = {0}", vhdr.unknown3 == 0); - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown4 zero? = {0}", vhdr.unknown4 == 0); - - AaruConsole.DebugWriteLine("ReFS plugin", "VolumeHeader.unknown5 empty? = {0}", - ArrayHelpers.ArrayIsNullOrEmpty(vhdr.unknown5)); - - if(vhdr.identifier != FSRS || - !ArrayHelpers.ArrayIsNullOrEmpty(vhdr.mustBeZero) || - !vhdr.signature.SequenceEqual(_signature)) - return; - - var sb = new StringBuilder(); - - sb.AppendLine("Microsoft Resilient File System"); - sb.AppendFormat("Volume uses {0} bytes per sector", vhdr.bytesPerSector).AppendLine(); - - sb.AppendFormat("Volume uses {0} sectors per cluster ({1} bytes)", vhdr.sectorsPerCluster, - vhdr.sectorsPerCluster * vhdr.bytesPerSector).AppendLine(); - - sb.AppendFormat("Volume has {0} sectors ({1} bytes)", vhdr.sectors, vhdr.sectors * vhdr.bytesPerSector). - AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "Resilient File System", - ClusterSize = vhdr.bytesPerSector * vhdr.sectorsPerCluster, - Clusters = vhdr.sectors / vhdr.sectorsPerCluster - }; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct VolumeHeader - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] signature; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] - public readonly byte[] mustBeZero; - public readonly uint identifier; - public readonly ushort length; - public readonly ushort checksum; - public readonly ulong sectors; - public readonly uint bytesPerSector; - public readonly uint sectorsPerCluster; - public readonly uint unknown1; - public readonly uint unknown2; - public readonly ulong unknown3; - public readonly ulong unknown4; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15872)] - public readonly byte[] unknown5; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct VolumeHeader + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] signature; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)] + public readonly byte[] mustBeZero; + public readonly uint identifier; + public readonly ushort length; + public readonly ushort checksum; + public readonly ulong sectors; + public readonly uint bytesPerSector; + public readonly uint sectorsPerCluster; + public readonly uint unknown1; + public readonly uint unknown2; + public readonly ulong unknown3; + public readonly ulong unknown4; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 15872)] + public readonly byte[] unknown5; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Reiser.cs b/Aaru.Filesystems/Reiser.cs index 72c0bcd35..18919fcc2 100644 --- a/Aaru.Filesystems/Reiser.cs +++ b/Aaru.Filesystems/Reiser.cs @@ -41,200 +41,199 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Reiser v3 filesystem +public sealed class Reiser : IFilesystem { - /// - /// Implements detection of the Reiser v3 filesystem - public sealed class Reiser : IFilesystem + const uint REISER_SUPER_OFFSET = 0x10000; + + readonly byte[] _magic35 = { - const uint REISER_SUPER_OFFSET = 0x10000; + 0x52, 0x65, 0x49, 0x73, 0x45, 0x72, 0x46, 0x73, 0x00, 0x00 + }; + readonly byte[] _magic36 = + { + 0x52, 0x65, 0x49, 0x73, 0x45, 0x72, 0x32, 0x46, 0x73, 0x00 + }; + readonly byte[] _magicJr = + { + 0x52, 0x65, 0x49, 0x73, 0x45, 0x72, 0x33, 0x46, 0x73, 0x00 + }; - readonly byte[] _magic35 = + /// + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Reiser Filesystem Plugin"; + /// + public Guid Id => new("1D8CD8B8-27E6-410F-9973-D16409225FBA"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + if(imagePlugin.Info.SectorSize < 512) + return false; + + uint sbAddr = REISER_SUPER_OFFSET / imagePlugin.Info.SectorSize; + + if(sbAddr == 0) + sbAddr = 1; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + if(partition.Start + sbAddr + sbSize >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < Marshal.SizeOf()) + return false; + + Superblock reiserSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + return _magic35.SequenceEqual(reiserSb.magic) || _magic36.SequenceEqual(reiserSb.magic) || + _magicJr.SequenceEqual(reiserSb.magic); + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + uint sbAddr = REISER_SUPER_OFFSET / imagePlugin.Info.SectorSize; + + if(sbAddr == 0) + sbAddr = 1; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < Marshal.SizeOf()) + return; + + Superblock reiserSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(!_magic35.SequenceEqual(reiserSb.magic) && + !_magic36.SequenceEqual(reiserSb.magic) && + !_magicJr.SequenceEqual(reiserSb.magic)) + return; + + var sb = new StringBuilder(); + + if(_magic35.SequenceEqual(reiserSb.magic)) + sb.AppendLine("Reiser 3.5 filesystem"); + else if(_magic36.SequenceEqual(reiserSb.magic)) + sb.AppendLine("Reiser 3.6 filesystem"); + else if(_magicJr.SequenceEqual(reiserSb.magic)) + sb.AppendLine("Reiser Jr. filesystem"); + + sb.AppendFormat("Volume has {0} blocks with {1} blocks free", reiserSb.block_count, reiserSb.free_blocks). + AppendLine(); + + sb.AppendFormat("{0} bytes per block", reiserSb.blocksize).AppendLine(); + sb.AppendFormat("Root directory resides on block {0}", reiserSb.root_block).AppendLine(); + + if(reiserSb.umount_state == 2) + sb.AppendLine("Volume has not been cleanly umounted"); + + sb.AppendFormat("Volume last checked on {0}", DateHandlers.UnixUnsignedToDateTime(reiserSb.last_check)). + AppendLine(); + + if(reiserSb.version >= 2) { - 0x52, 0x65, 0x49, 0x73, 0x45, 0x72, 0x46, 0x73, 0x00, 0x00 - }; - readonly byte[] _magic36 = - { - 0x52, 0x65, 0x49, 0x73, 0x45, 0x72, 0x32, 0x46, 0x73, 0x00 - }; - readonly byte[] _magicJr = - { - 0x52, 0x65, 0x49, 0x73, 0x45, 0x72, 0x33, 0x46, 0x73, 0x00 - }; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Reiser Filesystem Plugin"; - /// - public Guid Id => new("1D8CD8B8-27E6-410F-9973-D16409225FBA"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(imagePlugin.Info.SectorSize < 512) - return false; - - uint sbAddr = REISER_SUPER_OFFSET / imagePlugin.Info.SectorSize; - - if(sbAddr == 0) - sbAddr = 1; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - if(partition.Start + sbAddr + sbSize >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - Superblock reiserSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - return _magic35.SequenceEqual(reiserSb.magic) || _magic36.SequenceEqual(reiserSb.magic) || - _magicJr.SequenceEqual(reiserSb.magic); + sb.AppendFormat("Volume UUID: {0}", reiserSb.uuid).AppendLine(); + sb.AppendFormat("Volume name: {0}", Encoding.GetString(reiserSb.label)).AppendLine(); } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + information = sb.ToString(); - if(imagePlugin.Info.SectorSize < 512) - return; + XmlFsType = new FileSystemType(); - uint sbAddr = REISER_SUPER_OFFSET / imagePlugin.Info.SectorSize; + if(_magic35.SequenceEqual(reiserSb.magic)) + XmlFsType.Type = "Reiser 3.5 filesystem"; + else if(_magic36.SequenceEqual(reiserSb.magic)) + XmlFsType.Type = "Reiser 3.6 filesystem"; + else if(_magicJr.SequenceEqual(reiserSb.magic)) + XmlFsType.Type = "Reiser Jr. filesystem"; - if(sbAddr == 0) - sbAddr = 1; + XmlFsType.ClusterSize = reiserSb.blocksize; + XmlFsType.Clusters = reiserSb.block_count; + XmlFsType.FreeClusters = reiserSb.free_blocks; + XmlFsType.FreeClustersSpecified = true; + XmlFsType.Dirty = reiserSb.umount_state == 2; - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + if(reiserSb.version < 2) + return; - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; + XmlFsType.VolumeName = StringHandlers.CToString(reiserSb.label, Encoding); + XmlFsType.VolumeSerial = reiserSb.uuid.ToString(); + } - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct JournalParameters + { + public readonly uint journal_1stblock; + public readonly uint journal_dev; + public readonly uint journal_size; + public readonly uint journal_trans_max; + public readonly uint journal_magic; + public readonly uint journal_max_batch; + public readonly uint journal_max_commit_age; + public readonly uint journal_max_trans_age; + } - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < Marshal.SizeOf()) - return; - - Superblock reiserSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - if(!_magic35.SequenceEqual(reiserSb.magic) && - !_magic36.SequenceEqual(reiserSb.magic) && - !_magicJr.SequenceEqual(reiserSb.magic)) - return; - - var sb = new StringBuilder(); - - if(_magic35.SequenceEqual(reiserSb.magic)) - sb.AppendLine("Reiser 3.5 filesystem"); - else if(_magic36.SequenceEqual(reiserSb.magic)) - sb.AppendLine("Reiser 3.6 filesystem"); - else if(_magicJr.SequenceEqual(reiserSb.magic)) - sb.AppendLine("Reiser Jr. filesystem"); - - sb.AppendFormat("Volume has {0} blocks with {1} blocks free", reiserSb.block_count, reiserSb.free_blocks). - AppendLine(); - - sb.AppendFormat("{0} bytes per block", reiserSb.blocksize).AppendLine(); - sb.AppendFormat("Root directory resides on block {0}", reiserSb.root_block).AppendLine(); - - if(reiserSb.umount_state == 2) - sb.AppendLine("Volume has not been cleanly umounted"); - - sb.AppendFormat("Volume last checked on {0}", DateHandlers.UnixUnsignedToDateTime(reiserSb.last_check)). - AppendLine(); - - if(reiserSb.version >= 2) - { - sb.AppendFormat("Volume UUID: {0}", reiserSb.uuid).AppendLine(); - sb.AppendFormat("Volume name: {0}", Encoding.GetString(reiserSb.label)).AppendLine(); - } - - information = sb.ToString(); - - XmlFsType = new FileSystemType(); - - if(_magic35.SequenceEqual(reiserSb.magic)) - XmlFsType.Type = "Reiser 3.5 filesystem"; - else if(_magic36.SequenceEqual(reiserSb.magic)) - XmlFsType.Type = "Reiser 3.6 filesystem"; - else if(_magicJr.SequenceEqual(reiserSb.magic)) - XmlFsType.Type = "Reiser Jr. filesystem"; - - XmlFsType.ClusterSize = reiserSb.blocksize; - XmlFsType.Clusters = reiserSb.block_count; - XmlFsType.FreeClusters = reiserSb.free_blocks; - XmlFsType.FreeClustersSpecified = true; - XmlFsType.Dirty = reiserSb.umount_state == 2; - - if(reiserSb.version < 2) - return; - - XmlFsType.VolumeName = StringHandlers.CToString(reiserSb.label, Encoding); - XmlFsType.VolumeSerial = reiserSb.uuid.ToString(); - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct JournalParameters - { - public readonly uint journal_1stblock; - public readonly uint journal_dev; - public readonly uint journal_size; - public readonly uint journal_trans_max; - public readonly uint journal_magic; - public readonly uint journal_max_batch; - public readonly uint journal_max_commit_age; - public readonly uint journal_max_trans_age; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct Superblock - { - public readonly uint block_count; - public readonly uint free_blocks; - public readonly uint root_block; - public readonly JournalParameters journal; - public readonly ushort blocksize; - public readonly ushort oid_maxsize; - public readonly ushort oid_cursize; - public readonly ushort umount_state; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly byte[] magic; - public readonly ushort fs_state; - public readonly uint hash_function_code; - public readonly ushort tree_height; - public readonly ushort bmap_nr; - public readonly ushort version; - public readonly ushort reserved_for_journal; - public readonly uint inode_generation; - public readonly uint flags; - public readonly Guid uuid; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] label; - public readonly ushort mnt_count; - public readonly ushort max_mnt_count; - public readonly uint last_check; - public readonly uint check_interval; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 76)] - public readonly byte[] unused; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct Superblock + { + public readonly uint block_count; + public readonly uint free_blocks; + public readonly uint root_block; + public readonly JournalParameters journal; + public readonly ushort blocksize; + public readonly ushort oid_maxsize; + public readonly ushort oid_cursize; + public readonly ushort umount_state; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly byte[] magic; + public readonly ushort fs_state; + public readonly uint hash_function_code; + public readonly ushort tree_height; + public readonly ushort bmap_nr; + public readonly ushort version; + public readonly ushort reserved_for_journal; + public readonly uint inode_generation; + public readonly uint flags; + public readonly Guid uuid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] label; + public readonly ushort mnt_count; + public readonly ushort max_mnt_count; + public readonly uint last_check; + public readonly uint check_interval; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 76)] + public readonly byte[] unused; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Reiser4.cs b/Aaru.Filesystems/Reiser4.cs index 116a5af58..66d3e50de 100644 --- a/Aaru.Filesystems/Reiser4.cs +++ b/Aaru.Filesystems/Reiser4.cs @@ -41,125 +41,124 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Reiser v4 filesystem +public sealed class Reiser4 : IFilesystem { - /// - /// Implements detection of the Reiser v4 filesystem - public sealed class Reiser4 : IFilesystem + const uint REISER4_SUPER_OFFSET = 0x10000; + + readonly byte[] _magic = { - const uint REISER4_SUPER_OFFSET = 0x10000; + 0x52, 0x65, 0x49, 0x73, 0x45, 0x72, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; - readonly byte[] _magic = + /// + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Reiser4 Filesystem Plugin"; + /// + public Guid Id => new("301F2D00-E8D5-4F04-934E-81DFB21D15BA"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + if(imagePlugin.Info.SectorSize < 512) + return false; + + uint sbAddr = REISER4_SUPER_OFFSET / imagePlugin.Info.SectorSize; + + if(sbAddr == 0) + sbAddr = 1; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + if(partition.Start + sbAddr + sbSize >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < Marshal.SizeOf()) + return false; + + Superblock reiserSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + return _magic.SequenceEqual(reiserSb.magic); + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + uint sbAddr = REISER4_SUPER_OFFSET / imagePlugin.Info.SectorSize; + + if(sbAddr == 0) + sbAddr = 1; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < Marshal.SizeOf()) + return; + + Superblock reiserSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(!_magic.SequenceEqual(reiserSb.magic)) + return; + + var sb = new StringBuilder(); + + sb.AppendLine("Reiser 4 filesystem"); + sb.AppendFormat("{0} bytes per block", reiserSb.blocksize).AppendLine(); + sb.AppendFormat("Volume disk format: {0}", reiserSb.diskformat).AppendLine(); + sb.AppendFormat("Volume UUID: {0}", reiserSb.uuid).AppendLine(); + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(reiserSb.label, Encoding)).AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - 0x52, 0x65, 0x49, 0x73, 0x45, 0x72, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + Type = "Reiser 4 filesystem", + ClusterSize = reiserSb.blocksize, + Clusters = (partition.End - partition.Start) * imagePlugin.Info.SectorSize / reiserSb.blocksize, + VolumeName = StringHandlers.CToString(reiserSb.label, Encoding), + VolumeSerial = reiserSb.uuid.ToString() }; + } - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Reiser4 Filesystem Plugin"; - /// - public Guid Id => new("301F2D00-E8D5-4F04-934E-81DFB21D15BA"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(imagePlugin.Info.SectorSize < 512) - return false; - - uint sbAddr = REISER4_SUPER_OFFSET / imagePlugin.Info.SectorSize; - - if(sbAddr == 0) - sbAddr = 1; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - if(partition.Start + sbAddr + sbSize >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - Superblock reiserSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - return _magic.SequenceEqual(reiserSb.magic); - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - if(imagePlugin.Info.SectorSize < 512) - return; - - uint sbAddr = REISER4_SUPER_OFFSET / imagePlugin.Info.SectorSize; - - if(sbAddr == 0) - sbAddr = 1; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + sbAddr, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < Marshal.SizeOf()) - return; - - Superblock reiserSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - if(!_magic.SequenceEqual(reiserSb.magic)) - return; - - var sb = new StringBuilder(); - - sb.AppendLine("Reiser 4 filesystem"); - sb.AppendFormat("{0} bytes per block", reiserSb.blocksize).AppendLine(); - sb.AppendFormat("Volume disk format: {0}", reiserSb.diskformat).AppendLine(); - sb.AppendFormat("Volume UUID: {0}", reiserSb.uuid).AppendLine(); - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(reiserSb.label, Encoding)).AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "Reiser 4 filesystem", - ClusterSize = reiserSb.blocksize, - Clusters = (partition.End - partition.Start) * imagePlugin.Info.SectorSize / reiserSb.blocksize, - VolumeName = StringHandlers.CToString(reiserSb.label, Encoding), - VolumeSerial = reiserSb.uuid.ToString() - }; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Superblock - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] magic; - public readonly ushort diskformat; - public readonly ushort blocksize; - public readonly Guid uuid; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] label; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Superblock + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] magic; + public readonly ushort diskformat; + public readonly ushort blocksize; + public readonly Guid uuid; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] label; } } \ No newline at end of file diff --git a/Aaru.Filesystems/SFS.cs b/Aaru.Filesystems/SFS.cs index 214880f07..f7fc19923 100644 --- a/Aaru.Filesystems/SFS.cs +++ b/Aaru.Filesystems/SFS.cs @@ -40,142 +40,141 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Smart File System +public sealed class SFS : IFilesystem { + /// Identifier for SFS v1 + const uint SFS_MAGIC = 0x53465300; + /// Identifier for SFS v2 + const uint SFS2_MAGIC = 0x53465302; + /// - /// Implements detection of the Smart File System - public sealed class SFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "SmartFileSystem"; + /// + public Guid Id => new("26550C19-3671-4A2D-BC2F-F20CEB7F48DC"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Identifier for SFS v1 - const uint SFS_MAGIC = 0x53465300; - /// Identifier for SFS v2 - const uint SFS2_MAGIC = 0x53465302; + if(partition.Start >= partition.End) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "SmartFileSystem"; - /// - public Guid Id => new("26550C19-3671-4A2D-BC2F-F20CEB7F48DC"); - /// - public string Author => "Natalia Portillo"; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(errno != ErrorNumber.NoError) + return false; + + uint magic = BigEndianBitConverter.ToUInt32(sector, 0x00); + + return magic == SFS_MAGIC || magic == SFS2_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + information = ""; + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] rootBlockSector); + + if(errno != ErrorNumber.NoError) + return; + + RootBlock rootBlock = Marshal.ByteArrayToStructureBigEndian(rootBlockSector); + + var sbInformation = new StringBuilder(); + + sbInformation.AppendLine("SmartFileSystem"); + + sbInformation.AppendFormat("Volume version {0}", rootBlock.version).AppendLine(); + + sbInformation.AppendFormat("Volume starts on device byte {0} and ends on byte {1}", rootBlock.firstbyte, + rootBlock.lastbyte).AppendLine(); + + sbInformation. + AppendFormat("Volume has {0} blocks of {1} bytes each", rootBlock.totalblocks, rootBlock.blocksize). + AppendLine(); + + sbInformation.AppendFormat("Volume created on {0}", + DateHandlers.UnixUnsignedToDateTime(rootBlock.datecreated).AddYears(8)). + AppendLine(); + + sbInformation.AppendFormat("Bitmap starts in block {0}", rootBlock.bitmapbase).AppendLine(); + + sbInformation.AppendFormat("Admin space container starts in block {0}", rootBlock.adminspacecontainer). + AppendLine(); + + sbInformation.AppendFormat("Root object container starts in block {0}", rootBlock.rootobjectcontainer). + AppendLine(); + + sbInformation. + AppendFormat("Root node of the extent B-tree resides in block {0}", rootBlock.extentbnoderoot). + AppendLine(); + + sbInformation.AppendFormat("Root node of the object B-tree resides in block {0}", rootBlock.objectnoderoot). + AppendLine(); + + if(rootBlock.bits.HasFlag(Flags.CaseSensitive)) + sbInformation.AppendLine("Volume is case sensitive"); + + if(rootBlock.bits.HasFlag(Flags.RecycledFolder)) + sbInformation.AppendLine("Volume moves deleted files to a recycled folder"); + + information = sbInformation.ToString(); + + XmlFsType = new FileSystemType { - if(partition.Start >= partition.End) - return false; + CreationDate = DateHandlers.UnixUnsignedToDateTime(rootBlock.datecreated).AddYears(8), + CreationDateSpecified = true, + Clusters = rootBlock.totalblocks, + ClusterSize = rootBlock.blocksize, + Type = "SmartFileSystem" + }; + } - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + [Flags] + enum Flags : byte + { + RecycledFolder = 64, CaseSensitive = 128 + } - if(errno != ErrorNumber.NoError) - return false; - - uint magic = BigEndianBitConverter.ToUInt32(sector, 0x00); - - return magic == SFS_MAGIC || magic == SFS2_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - information = ""; - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-1"); - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] rootBlockSector); - - if(errno != ErrorNumber.NoError) - return; - - RootBlock rootBlock = Marshal.ByteArrayToStructureBigEndian(rootBlockSector); - - var sbInformation = new StringBuilder(); - - sbInformation.AppendLine("SmartFileSystem"); - - sbInformation.AppendFormat("Volume version {0}", rootBlock.version).AppendLine(); - - sbInformation.AppendFormat("Volume starts on device byte {0} and ends on byte {1}", rootBlock.firstbyte, - rootBlock.lastbyte).AppendLine(); - - sbInformation. - AppendFormat("Volume has {0} blocks of {1} bytes each", rootBlock.totalblocks, rootBlock.blocksize). - AppendLine(); - - sbInformation.AppendFormat("Volume created on {0}", - DateHandlers.UnixUnsignedToDateTime(rootBlock.datecreated).AddYears(8)). - AppendLine(); - - sbInformation.AppendFormat("Bitmap starts in block {0}", rootBlock.bitmapbase).AppendLine(); - - sbInformation.AppendFormat("Admin space container starts in block {0}", rootBlock.adminspacecontainer). - AppendLine(); - - sbInformation.AppendFormat("Root object container starts in block {0}", rootBlock.rootobjectcontainer). - AppendLine(); - - sbInformation. - AppendFormat("Root node of the extent B-tree resides in block {0}", rootBlock.extentbnoderoot). - AppendLine(); - - sbInformation.AppendFormat("Root node of the object B-tree resides in block {0}", rootBlock.objectnoderoot). - AppendLine(); - - if(rootBlock.bits.HasFlag(Flags.CaseSensitive)) - sbInformation.AppendLine("Volume is case sensitive"); - - if(rootBlock.bits.HasFlag(Flags.RecycledFolder)) - sbInformation.AppendLine("Volume moves deleted files to a recycled folder"); - - information = sbInformation.ToString(); - - XmlFsType = new FileSystemType - { - CreationDate = DateHandlers.UnixUnsignedToDateTime(rootBlock.datecreated).AddYears(8), - CreationDateSpecified = true, - Clusters = rootBlock.totalblocks, - ClusterSize = rootBlock.blocksize, - Type = "SmartFileSystem" - }; - } - - [Flags] - enum Flags : byte - { - RecycledFolder = 64, CaseSensitive = 128 - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct RootBlock - { - public readonly uint blockId; - public readonly uint blockChecksum; - public readonly uint blockSelfPointer; - public readonly ushort version; - public readonly ushort sequence; - public readonly uint datecreated; - public readonly Flags bits; - public readonly byte padding1; - public readonly ushort padding2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly uint[] reserved1; - public readonly ulong firstbyte; - public readonly ulong lastbyte; - public readonly uint totalblocks; - public readonly uint blocksize; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly uint[] reserved2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly uint[] reserved3; - public readonly uint bitmapbase; - public readonly uint adminspacecontainer; - public readonly uint rootobjectcontainer; - public readonly uint extentbnoderoot; - public readonly uint objectnoderoot; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly uint[] reserved4; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct RootBlock + { + public readonly uint blockId; + public readonly uint blockChecksum; + public readonly uint blockSelfPointer; + public readonly ushort version; + public readonly ushort sequence; + public readonly uint datecreated; + public readonly Flags bits; + public readonly byte padding1; + public readonly ushort padding2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly uint[] reserved1; + public readonly ulong firstbyte; + public readonly ulong lastbyte; + public readonly uint totalblocks; + public readonly uint blocksize; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly uint[] reserved2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly uint[] reserved3; + public readonly uint bitmapbase; + public readonly uint adminspacecontainer; + public readonly uint rootobjectcontainer; + public readonly uint extentbnoderoot; + public readonly uint objectnoderoot; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly uint[] reserved4; } } \ No newline at end of file diff --git a/Aaru.Filesystems/SolarFS.cs b/Aaru.Filesystems/SolarFS.cs index ad408abc6..3f218f4ee 100644 --- a/Aaru.Filesystems/SolarFS.cs +++ b/Aaru.Filesystems/SolarFS.cs @@ -40,185 +40,184 @@ using Aaru.Console; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Based on FAT's BPB, cannot find a FAT or directory +/// +/// Implements detection of the Solar OS filesystem +public sealed class SolarFS : IFilesystem { - // Based on FAT's BPB, cannot find a FAT or directory /// - /// Implements detection of the Solar OS filesystem - public sealed class SolarFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Solar_OS filesystem"; + /// + public Guid Id => new("EA3101C1-E777-4B4F-B5A3-8C57F50F6E65"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Solar_OS filesystem"; - /// - public Guid Id => new("EA3101C1-E777-4B4F-B5A3-8C57F50F6E65"); - /// - public string Author => "Natalia Portillo"; + if(2 + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bpb); + + if(errno != ErrorNumber.NoError) + return false; + + byte[] fsTypeB = new byte[8]; + + byte signature = bpb[0x25]; + Array.Copy(bpb, 0x35, fsTypeB, 0, 8); + string fsType = StringHandlers.CToString(fsTypeB); + + return signature == 0x29 && fsType == "SOL_FS "; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bpbSector); + + if(errno != ErrorNumber.NoError) + return; + + var bpb = new BiosParameterBlock { - if(2 + partition.Start >= partition.End) - return false; + bps = BitConverter.ToUInt16(bpbSector, 0x0B), + root_ent = BitConverter.ToUInt16(bpbSector, 0x10), + sectors = BitConverter.ToUInt16(bpbSector, 0x12), + media = bpbSector[0x14], + spfat = BitConverter.ToUInt16(bpbSector, 0x15), + sptrk = BitConverter.ToUInt16(bpbSector, 0x17), + heads = BitConverter.ToUInt16(bpbSector, 0x19), + signature = bpbSector[0x25] + }; - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bpb); + byte[] bpbStrings = new byte[8]; + Array.Copy(bpbSector, 0x03, bpbStrings, 0, 8); + bpb.OEMName = StringHandlers.CToString(bpbStrings); + bpbStrings = new byte[8]; + Array.Copy(bpbSector, 0x2A, bpbStrings, 0, 11); + bpb.vol_name = StringHandlers.CToString(bpbStrings, Encoding); + bpbStrings = new byte[8]; + Array.Copy(bpbSector, 0x35, bpbStrings, 0, 8); + bpb.fs_type = StringHandlers.CToString(bpbStrings, Encoding); - if(errno != ErrorNumber.NoError) - return false; + bpb.x86_jump = new byte[3]; + Array.Copy(bpbSector, 0x00, bpb.x86_jump, 0, 3); + bpb.unk1 = bpbSector[0x0D]; + bpb.unk2 = BitConverter.ToUInt16(bpbSector, 0x0E); + bpb.unk3 = new byte[10]; + Array.Copy(bpbSector, 0x1B, bpb.unk3, 0, 10); + bpb.unk4 = BitConverter.ToUInt32(bpbSector, 0x26); - byte[] fsTypeB = new byte[8]; + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.x86_jump: 0x{0:X2}{1:X2}{2:X2}", bpb.x86_jump[0], + bpb.x86_jump[1], bpb.x86_jump[2]); - byte signature = bpb[0x25]; - Array.Copy(bpb, 0x35, fsTypeB, 0, 8); - string fsType = StringHandlers.CToString(fsTypeB); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.OEMName: \"{0}\"", bpb.OEMName); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.bps: {0}", bpb.bps); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.unk1: 0x{0:X2}", bpb.unk1); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.unk2: 0x{0:X4}", bpb.unk2); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.root_ent: {0}", bpb.root_ent); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.sectors: {0}", bpb.sectors); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.media: 0x{0:X2}", bpb.media); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.spfat: {0}", bpb.spfat); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.sptrk: {0}", bpb.sptrk); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.heads: {0}", bpb.heads); - return signature == 0x29 && fsType == "SOL_FS "; - } + AaruConsole.DebugWriteLine("SolarFS plugin", + "BPB.unk3: 0x{0:X2}{1:X2}{2:X2}{3:X2}{4:X2}{5:X2}{6:X2}{7:X2}{8:X2}{9:X2}", + bpb.unk3[0], bpb.unk3[1], bpb.unk3[2], bpb.unk3[3], bpb.unk3[4], bpb.unk3[5], + bpb.unk3[6], bpb.unk3[7], bpb.unk3[8], bpb.unk3[9]); - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.signature: 0x{0:X2}", bpb.signature); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.unk4: 0x{0:X8}", bpb.unk4); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.vol_name: \"{0}\"", bpb.vol_name); + AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.fs_type: \"{0}\"", bpb.fs_type); + + sb.AppendLine("Solar_OS filesystem"); + sb.AppendFormat("Media descriptor: 0x{0:X2}", bpb.media).AppendLine(); + sb.AppendFormat("{0} bytes per sector", bpb.bps).AppendLine(); + + if(imagePlugin.Info.SectorSize == 2336 || + imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448) { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - var sb = new StringBuilder(); - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bpbSector); - - if(errno != ErrorNumber.NoError) - return; - - var bpb = new BiosParameterBlock - { - bps = BitConverter.ToUInt16(bpbSector, 0x0B), - root_ent = BitConverter.ToUInt16(bpbSector, 0x10), - sectors = BitConverter.ToUInt16(bpbSector, 0x12), - media = bpbSector[0x14], - spfat = BitConverter.ToUInt16(bpbSector, 0x15), - sptrk = BitConverter.ToUInt16(bpbSector, 0x17), - heads = BitConverter.ToUInt16(bpbSector, 0x19), - signature = bpbSector[0x25] - }; - - byte[] bpbStrings = new byte[8]; - Array.Copy(bpbSector, 0x03, bpbStrings, 0, 8); - bpb.OEMName = StringHandlers.CToString(bpbStrings); - bpbStrings = new byte[8]; - Array.Copy(bpbSector, 0x2A, bpbStrings, 0, 11); - bpb.vol_name = StringHandlers.CToString(bpbStrings, Encoding); - bpbStrings = new byte[8]; - Array.Copy(bpbSector, 0x35, bpbStrings, 0, 8); - bpb.fs_type = StringHandlers.CToString(bpbStrings, Encoding); - - bpb.x86_jump = new byte[3]; - Array.Copy(bpbSector, 0x00, bpb.x86_jump, 0, 3); - bpb.unk1 = bpbSector[0x0D]; - bpb.unk2 = BitConverter.ToUInt16(bpbSector, 0x0E); - bpb.unk3 = new byte[10]; - Array.Copy(bpbSector, 0x1B, bpb.unk3, 0, 10); - bpb.unk4 = BitConverter.ToUInt32(bpbSector, 0x26); - - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.x86_jump: 0x{0:X2}{1:X2}{2:X2}", bpb.x86_jump[0], - bpb.x86_jump[1], bpb.x86_jump[2]); - - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.OEMName: \"{0}\"", bpb.OEMName); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.bps: {0}", bpb.bps); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.unk1: 0x{0:X2}", bpb.unk1); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.unk2: 0x{0:X4}", bpb.unk2); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.root_ent: {0}", bpb.root_ent); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.sectors: {0}", bpb.sectors); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.media: 0x{0:X2}", bpb.media); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.spfat: {0}", bpb.spfat); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.sptrk: {0}", bpb.sptrk); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.heads: {0}", bpb.heads); - - AaruConsole.DebugWriteLine("SolarFS plugin", - "BPB.unk3: 0x{0:X2}{1:X2}{2:X2}{3:X2}{4:X2}{5:X2}{6:X2}{7:X2}{8:X2}{9:X2}", - bpb.unk3[0], bpb.unk3[1], bpb.unk3[2], bpb.unk3[3], bpb.unk3[4], bpb.unk3[5], - bpb.unk3[6], bpb.unk3[7], bpb.unk3[8], bpb.unk3[9]); - - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.signature: 0x{0:X2}", bpb.signature); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.unk4: 0x{0:X8}", bpb.unk4); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.vol_name: \"{0}\"", bpb.vol_name); - AaruConsole.DebugWriteLine("SolarFS plugin", "BPB.fs_type: \"{0}\"", bpb.fs_type); - - sb.AppendLine("Solar_OS filesystem"); - sb.AppendFormat("Media descriptor: 0x{0:X2}", bpb.media).AppendLine(); - sb.AppendFormat("{0} bytes per sector", bpb.bps).AppendLine(); - - if(imagePlugin.Info.SectorSize == 2336 || - imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448) - { - if(bpb.bps != imagePlugin.Info.SectorSize) - sb. - AppendFormat("WARNING: Filesystem describes a {0} bytes/sector, while device describes a {1} bytes/sector", - bpb.bps, 2048).AppendLine(); - } - else if(bpb.bps != imagePlugin.Info.SectorSize) + if(bpb.bps != imagePlugin.Info.SectorSize) sb. AppendFormat("WARNING: Filesystem describes a {0} bytes/sector, while device describes a {1} bytes/sector", - bpb.bps, imagePlugin.Info.SectorSize).AppendLine(); - - sb.AppendFormat("{0} sectors on volume ({1} bytes)", bpb.sectors, bpb.sectors * bpb.bps).AppendLine(); - - if(bpb.sectors > imagePlugin.Info.Sectors) - sb.AppendFormat("WARNING: Filesystem describes a {0} sectors volume, bigger than device ({1} sectors)", - bpb.sectors, imagePlugin.Info.Sectors); - - sb.AppendFormat("{0} heads", bpb.heads).AppendLine(); - sb.AppendFormat("{0} sectors per track", bpb.sptrk).AppendLine(); - sb.AppendFormat("Volume name: {0}", bpb.vol_name).AppendLine(); - - XmlFsType = new FileSystemType - { - Type = "SolarFS", - Clusters = bpb.sectors, - ClusterSize = bpb.bps, - VolumeName = bpb.vol_name - }; - - information = sb.ToString(); + bpb.bps, 2048).AppendLine(); } + else if(bpb.bps != imagePlugin.Info.SectorSize) + sb. + AppendFormat("WARNING: Filesystem describes a {0} bytes/sector, while device describes a {1} bytes/sector", + bpb.bps, imagePlugin.Info.SectorSize).AppendLine(); - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct BiosParameterBlock + sb.AppendFormat("{0} sectors on volume ({1} bytes)", bpb.sectors, bpb.sectors * bpb.bps).AppendLine(); + + if(bpb.sectors > imagePlugin.Info.Sectors) + sb.AppendFormat("WARNING: Filesystem describes a {0} sectors volume, bigger than device ({1} sectors)", + bpb.sectors, imagePlugin.Info.Sectors); + + sb.AppendFormat("{0} heads", bpb.heads).AppendLine(); + sb.AppendFormat("{0} sectors per track", bpb.sptrk).AppendLine(); + sb.AppendFormat("Volume name: {0}", bpb.vol_name).AppendLine(); + + XmlFsType = new FileSystemType { - /// 0x00, x86 jump (3 bytes), jumps to 0x60 - public byte[] x86_jump; - /// 0x03, 8 bytes, "SOLAR_OS" - public string OEMName; - /// 0x0B, Bytes per sector - public ushort bps; - /// 0x0D, unknown, 0x01 - public byte unk1; - /// 0x0E, unknown, 0x0201 - public ushort unk2; - /// 0x10, Number of entries on root directory ? (no root directory found) - public ushort root_ent; - /// 0x12, Sectors in volume - public ushort sectors; - /// 0x14, Media descriptor - public byte media; - /// 0x15, Sectors per FAT ? (no FAT found) - public ushort spfat; - /// 0x17, Sectors per track - public ushort sptrk; - /// 0x19, Heads - public ushort heads; - /// 0x1B, unknown, 10 bytes, zero-filled - public byte[] unk3; - /// 0x25, 0x29 - public byte signature; - /// 0x26, unknown, zero-filled - public uint unk4; - /// 0x2A, 11 bytes, volume name, space-padded - public string vol_name; - /// 0x35, 8 bytes, "SOL_FS " - public string fs_type; - } + Type = "SolarFS", + Clusters = bpb.sectors, + ClusterSize = bpb.bps, + VolumeName = bpb.vol_name + }; + + information = sb.ToString(); + } + + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct BiosParameterBlock + { + /// 0x00, x86 jump (3 bytes), jumps to 0x60 + public byte[] x86_jump; + /// 0x03, 8 bytes, "SOLAR_OS" + public string OEMName; + /// 0x0B, Bytes per sector + public ushort bps; + /// 0x0D, unknown, 0x01 + public byte unk1; + /// 0x0E, unknown, 0x0201 + public ushort unk2; + /// 0x10, Number of entries on root directory ? (no root directory found) + public ushort root_ent; + /// 0x12, Sectors in volume + public ushort sectors; + /// 0x14, Media descriptor + public byte media; + /// 0x15, Sectors per FAT ? (no FAT found) + public ushort spfat; + /// 0x17, Sectors per track + public ushort sptrk; + /// 0x19, Heads + public ushort heads; + /// 0x1B, unknown, 10 bytes, zero-filled + public byte[] unk3; + /// 0x25, 0x29 + public byte signature; + /// 0x26, unknown, zero-filled + public uint unk4; + /// 0x2A, 11 bytes, volume name, space-padded + public string vol_name; + /// 0x35, 8 bytes, "SOL_FS " + public string fs_type; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Squash.cs b/Aaru.Filesystems/Squash.cs index 8703215b5..500819a10 100644 --- a/Aaru.Filesystems/Squash.cs +++ b/Aaru.Filesystems/Squash.cs @@ -40,162 +40,161 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the squash filesystem +public sealed class Squash : IFilesystem { + /// Identifier for Squash + const uint SQUASH_MAGIC = 0x73717368; + const uint SQUASH_CIGAM = 0x68737173; + /// - /// Implements detection of the squash filesystem - public sealed class Squash : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Squash filesystem"; + /// + public Guid Id => new("F8F6E46F-7A2A-48E3-9C0A-46AF4DC29E09"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Identifier for Squash - const uint SQUASH_MAGIC = 0x73717368; - const uint SQUASH_CIGAM = 0x68737173; + if(partition.Start >= partition.End) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Squash filesystem"; - /// - public Guid Id => new("F8F6E46F-7A2A-48E3-9C0A-46AF4DC29E09"); - /// - public string Author => "Natalia Portillo"; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(errno != ErrorNumber.NoError) + return false; + + uint magic = BitConverter.ToUInt32(sector, 0x00); + + return magic == SQUASH_MAGIC || magic == SQUASH_CIGAM; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.UTF8; + information = ""; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + uint magic = BitConverter.ToUInt32(sector, 0x00); + + var sqSb = new SuperBlock(); + bool littleEndian = true; + + switch(magic) { - if(partition.Start >= partition.End) - return false; + case SQUASH_MAGIC: + sqSb = Marshal.ByteArrayToStructureLittleEndian(sector); - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + break; + case SQUASH_CIGAM: + sqSb = Marshal.ByteArrayToStructureBigEndian(sector); + littleEndian = false; - if(errno != ErrorNumber.NoError) - return false; - - uint magic = BitConverter.ToUInt32(sector, 0x00); - - return magic == SQUASH_MAGIC || magic == SQUASH_CIGAM; + break; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + var sbInformation = new StringBuilder(); + + sbInformation.AppendLine("Squash file system"); + sbInformation.AppendLine(littleEndian ? "Little-endian" : "Big-endian"); + sbInformation.AppendFormat("Volume version {0}.{1}", sqSb.s_major, sqSb.s_minor).AppendLine(); + sbInformation.AppendFormat("Volume has {0} bytes", sqSb.bytes_used).AppendLine(); + sbInformation.AppendFormat("Volume has {0} bytes per block", sqSb.block_size).AppendLine(); + + sbInformation.AppendFormat("Volume created on {0}", DateHandlers.UnixUnsignedToDateTime(sqSb.mkfs_time)). + AppendLine(); + + sbInformation.AppendFormat("Volume has {0} inodes", sqSb.inodes).AppendLine(); + + switch(sqSb.compression) { - Encoding = encoding ?? Encoding.UTF8; - information = ""; - ErrorNumber errno = imagePlugin.ReadSector(partition.Start, out byte[] sector); + case (ushort)SquashCompression.Lz4: + sbInformation.AppendLine("Volume is compressed using LZ4"); - if(errno != ErrorNumber.NoError) - return; + break; + case (ushort)SquashCompression.Lzo: + sbInformation.AppendLine("Volume is compressed using LZO"); - uint magic = BitConverter.ToUInt32(sector, 0x00); + break; + case (ushort)SquashCompression.Lzma: + sbInformation.AppendLine("Volume is compressed using LZMA"); - var sqSb = new SuperBlock(); - bool littleEndian = true; + break; + case (ushort)SquashCompression.Xz: + sbInformation.AppendLine("Volume is compressed using XZ"); - switch(magic) - { - case SQUASH_MAGIC: - sqSb = Marshal.ByteArrayToStructureLittleEndian(sector); + break; + case (ushort)SquashCompression.Zlib: + sbInformation.AppendLine("Volume is compressed using GZIP"); - break; - case SQUASH_CIGAM: - sqSb = Marshal.ByteArrayToStructureBigEndian(sector); - littleEndian = false; + break; + case (ushort)SquashCompression.Zstd: + sbInformation.AppendLine("Volume is compressed using Zstandard"); - break; - } + break; + default: + sbInformation.AppendFormat("Volume is compressed using unknown algorithm {0}", sqSb.compression). + AppendLine(); - var sbInformation = new StringBuilder(); - - sbInformation.AppendLine("Squash file system"); - sbInformation.AppendLine(littleEndian ? "Little-endian" : "Big-endian"); - sbInformation.AppendFormat("Volume version {0}.{1}", sqSb.s_major, sqSb.s_minor).AppendLine(); - sbInformation.AppendFormat("Volume has {0} bytes", sqSb.bytes_used).AppendLine(); - sbInformation.AppendFormat("Volume has {0} bytes per block", sqSb.block_size).AppendLine(); - - sbInformation.AppendFormat("Volume created on {0}", DateHandlers.UnixUnsignedToDateTime(sqSb.mkfs_time)). - AppendLine(); - - sbInformation.AppendFormat("Volume has {0} inodes", sqSb.inodes).AppendLine(); - - switch(sqSb.compression) - { - case (ushort)SquashCompression.Lz4: - sbInformation.AppendLine("Volume is compressed using LZ4"); - - break; - case (ushort)SquashCompression.Lzo: - sbInformation.AppendLine("Volume is compressed using LZO"); - - break; - case (ushort)SquashCompression.Lzma: - sbInformation.AppendLine("Volume is compressed using LZMA"); - - break; - case (ushort)SquashCompression.Xz: - sbInformation.AppendLine("Volume is compressed using XZ"); - - break; - case (ushort)SquashCompression.Zlib: - sbInformation.AppendLine("Volume is compressed using GZIP"); - - break; - case (ushort)SquashCompression.Zstd: - sbInformation.AppendLine("Volume is compressed using Zstandard"); - - break; - default: - sbInformation.AppendFormat("Volume is compressed using unknown algorithm {0}", sqSb.compression). - AppendLine(); - - break; - } - - information = sbInformation.ToString(); - - XmlFsType = new FileSystemType - { - Type = "Squash file system", - CreationDate = DateHandlers.UnixUnsignedToDateTime(sqSb.mkfs_time), - CreationDateSpecified = true, - Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / sqSb.block_size, - ClusterSize = sqSb.block_size, - Files = sqSb.inodes, - FilesSpecified = true, - FreeClusters = 0, - FreeClustersSpecified = true - }; + break; } - enum SquashCompression : ushort - { - Zlib = 1, Lzma = 2, Lzo = 3, - Xz = 4, Lz4 = 5, Zstd = 6 - } + information = sbInformation.ToString(); - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock + XmlFsType = new FileSystemType { - public readonly uint magic; - public readonly uint inodes; - public readonly uint mkfs_time; - public readonly uint block_size; - public readonly uint fragments; - public readonly ushort compression; - public readonly ushort block_log; - public readonly ushort flags; - public readonly ushort no_ids; - public readonly ushort s_major; - public readonly ushort s_minor; - public readonly ulong root_inode; - public readonly ulong bytes_used; - public readonly ulong id_table_start; - public readonly ulong xattr_id_table_start; - public readonly ulong inode_table_start; - public readonly ulong directory_table_start; - public readonly ulong fragment_table_start; - public readonly ulong lookup_table_start; - } + Type = "Squash file system", + CreationDate = DateHandlers.UnixUnsignedToDateTime(sqSb.mkfs_time), + CreationDateSpecified = true, + Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / sqSb.block_size, + ClusterSize = sqSb.block_size, + Files = sqSb.inodes, + FilesSpecified = true, + FreeClusters = 0, + FreeClustersSpecified = true + }; + } + + enum SquashCompression : ushort + { + Zlib = 1, Lzma = 2, Lzo = 3, + Xz = 4, Lz4 = 5, Zstd = 6 + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + public readonly uint magic; + public readonly uint inodes; + public readonly uint mkfs_time; + public readonly uint block_size; + public readonly uint fragments; + public readonly ushort compression; + public readonly ushort block_log; + public readonly ushort flags; + public readonly ushort no_ids; + public readonly ushort s_major; + public readonly ushort s_minor; + public readonly ulong root_inode; + public readonly ulong bytes_used; + public readonly ulong id_table_start; + public readonly ulong xattr_id_table_start; + public readonly ulong inode_table_start; + public readonly ulong directory_table_start; + public readonly ulong fragment_table_start; + public readonly ulong lookup_table_start; } } \ No newline at end of file diff --git a/Aaru.Filesystems/SysV.cs b/Aaru.Filesystems/SysV.cs index 2b40fd963..47ee58965 100644 --- a/Aaru.Filesystems/SysV.cs +++ b/Aaru.Filesystems/SysV.cs @@ -42,1107 +42,1106 @@ using Schemas; // ReSharper disable NotAccessedField.Local -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from the Linux kernel +/// +/// Implements detection of the UNIX System V filesystem +[SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "UnusedMember.Local"), + SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed class SysVfs : IFilesystem { - // Information from the Linux kernel + const uint XENIX_MAGIC = 0x002B5544; + const uint XENIX_CIGAM = 0x44552B00; + const uint SYSV_MAGIC = 0xFD187E20; + const uint SYSV_CIGAM = 0x207E18FD; + + // Rest have no magic. + // Per a Linux kernel, Coherent fs has following: + const string COH_FNAME = "noname"; + const string COH_FPACK = "nopack"; + const string COH_XXXXX = "xxxxx"; + const string COH_XXXXS = "xxxxx "; + const string COH_XXXXN = "xxxxx\n"; + + // SCO AFS + const ushort SCO_NFREE = 0xFFFF; + + // UNIX 7th Edition has nothing to detect it, so check for a valid filesystem is a must :( + const ushort V7_NICINOD = 100; + const ushort V7_NICFREE = 100; + const uint V7_MAXSIZE = 0x00FFFFFF; + /// - /// Implements detection of the UNIX System V filesystem - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "UnusedMember.Local"), - SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed class SysVfs : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "UNIX System V filesystem"; + /// + public Guid Id => new("9B8D016A-8561-400E-A12A-A198283C211D"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint XENIX_MAGIC = 0x002B5544; - const uint XENIX_CIGAM = 0x44552B00; - const uint SYSV_MAGIC = 0xFD187E20; - const uint SYSV_CIGAM = 0x207E18FD; + if(2 + partition.Start >= partition.End) + return false; - // Rest have no magic. - // Per a Linux kernel, Coherent fs has following: - const string COH_FNAME = "noname"; - const string COH_FPACK = "nopack"; - const string COH_XXXXX = "xxxxx"; - const string COH_XXXXS = "xxxxx "; - const string COH_XXXXN = "xxxxx\n"; + byte sb_size_in_sectors; - // SCO AFS - const ushort SCO_NFREE = 0xFFFF; + if(imagePlugin.Info.SectorSize <= + 0x400) // Check if underlying device sector size is smaller than SuperBlock size + sb_size_in_sectors = (byte)(0x400 / imagePlugin.Info.SectorSize); + else + sb_size_in_sectors = 1; // If not a single sector can store it - // UNIX 7th Edition has nothing to detect it, so check for a valid filesystem is a must :( - const ushort V7_NICINOD = 100; - const ushort V7_NICFREE = 100; - const uint V7_MAXSIZE = 0x00FFFFFF; + if(partition.End <= + partition.Start + (4 * (ulong)sb_size_in_sectors) + + sb_size_in_sectors) // Device must be bigger than SB location + SB size + offset + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "UNIX System V filesystem"; - /// - public Guid Id => new("9B8D016A-8561-400E-A12A-A198283C211D"); - /// - public string Author => "Natalia Portillo"; + // Sectors in a cylinder + int spc = (int)(imagePlugin.Info.Heads * imagePlugin.Info.SectorsPerTrack); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + // Superblock can start on 0x000, 0x200, 0x600 and 0x800, not aligned, so we assume 16 (128 bytes/sector) sectors as a safe value + int[] locations = { - if(2 + partition.Start >= partition.End) - return false; + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - byte sb_size_in_sectors; + // Superblock can also skip one cylinder (for boot) + spc + }; - if(imagePlugin.Info.SectorSize <= - 0x400) // Check if underlying device sector size is smaller than SuperBlock size - sb_size_in_sectors = (byte)(0x400 / imagePlugin.Info.SectorSize); - else - sb_size_in_sectors = 1; // If not a single sector can store it + foreach(int i in locations.TakeWhile(i => (ulong)i + partition.Start + sb_size_in_sectors < + imagePlugin.Info.Sectors)) + { + ErrorNumber errno = + imagePlugin.ReadSectors((ulong)i + partition.Start, sb_size_in_sectors, out byte[] sb_sector); - if(partition.End <= - partition.Start + (4 * (ulong)sb_size_in_sectors) + - sb_size_in_sectors) // Device must be bigger than SB location + SB size + offset - return false; + if(errno != ErrorNumber.NoError || + sb_sector.Length < 0x400) + continue; - // Sectors in a cylinder - int spc = (int)(imagePlugin.Info.Heads * imagePlugin.Info.SectorsPerTrack); + uint magic = BitConverter.ToUInt32(sb_sector, 0x3F8); - // Superblock can start on 0x000, 0x200, 0x600 and 0x800, not aligned, so we assume 16 (128 bytes/sector) sectors as a safe value - int[] locations = + if(magic == XENIX_MAGIC || + magic == XENIX_CIGAM || + magic == SYSV_MAGIC || + magic == SYSV_CIGAM) + return true; + + magic = BitConverter.ToUInt32(sb_sector, 0x1F8); // System V magic location + + if(magic == SYSV_MAGIC || + magic == SYSV_CIGAM) + return true; + + magic = BitConverter.ToUInt32(sb_sector, 0x1F0); // XENIX 3 magic location + + if(magic == XENIX_MAGIC || + magic == XENIX_CIGAM) + return true; + + byte[] coherent_string = new byte[6]; + Array.Copy(sb_sector, 0x1E4, coherent_string, 0, 6); // Coherent UNIX s_fname location + string s_fname = StringHandlers.CToString(coherent_string); + Array.Copy(sb_sector, 0x1EA, coherent_string, 0, 6); // Coherent UNIX s_fpack location + string s_fpack = StringHandlers.CToString(coherent_string); + + if((s_fname == COH_FNAME && s_fpack == COH_FPACK) || + (s_fname == COH_XXXXX && s_fpack == COH_XXXXX) || + (s_fname == COH_XXXXS && s_fpack == COH_XXXXN)) + return true; + + // Now try to identify 7th edition + uint s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); + ushort s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); + ushort s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); + + if(s_fsize <= 0 || + s_fsize >= 0xFFFFFFFF || + s_nfree <= 0 || + s_nfree >= 0xFFFF || + s_ninode <= 0 || + s_ninode >= 0xFFFF) + continue; + + if((s_fsize & 0xFF) == 0x00 && + (s_nfree & 0xFF) == 0x00 && + (s_ninode & 0xFF) == 0x00) { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + // Byteswap + s_fsize = ((s_fsize & 0xFF) << 24) + ((s_fsize & 0xFF00) << 8) + ((s_fsize & 0xFF0000) >> 8) + + ((s_fsize & 0xFF000000) >> 24); - // Superblock can also skip one cylinder (for boot) - spc - }; - - foreach(int i in locations.TakeWhile(i => (ulong)i + partition.Start + sb_size_in_sectors < - imagePlugin.Info.Sectors)) - { - ErrorNumber errno = - imagePlugin.ReadSectors((ulong)i + partition.Start, sb_size_in_sectors, out byte[] sb_sector); - - if(errno != ErrorNumber.NoError || - sb_sector.Length < 0x400) - continue; - - uint magic = BitConverter.ToUInt32(sb_sector, 0x3F8); - - if(magic == XENIX_MAGIC || - magic == XENIX_CIGAM || - magic == SYSV_MAGIC || - magic == SYSV_CIGAM) - return true; - - magic = BitConverter.ToUInt32(sb_sector, 0x1F8); // System V magic location - - if(magic == SYSV_MAGIC || - magic == SYSV_CIGAM) - return true; - - magic = BitConverter.ToUInt32(sb_sector, 0x1F0); // XENIX 3 magic location - - if(magic == XENIX_MAGIC || - magic == XENIX_CIGAM) - return true; - - byte[] coherent_string = new byte[6]; - Array.Copy(sb_sector, 0x1E4, coherent_string, 0, 6); // Coherent UNIX s_fname location - string s_fname = StringHandlers.CToString(coherent_string); - Array.Copy(sb_sector, 0x1EA, coherent_string, 0, 6); // Coherent UNIX s_fpack location - string s_fpack = StringHandlers.CToString(coherent_string); - - if((s_fname == COH_FNAME && s_fpack == COH_FPACK) || - (s_fname == COH_XXXXX && s_fpack == COH_XXXXX) || - (s_fname == COH_XXXXS && s_fpack == COH_XXXXN)) - return true; - - // Now try to identify 7th edition - uint s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); - ushort s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); - ushort s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); - - if(s_fsize <= 0 || - s_fsize >= 0xFFFFFFFF || - s_nfree <= 0 || - s_nfree >= 0xFFFF || - s_ninode <= 0 || - s_ninode >= 0xFFFF) - continue; - - if((s_fsize & 0xFF) == 0x00 && - (s_nfree & 0xFF) == 0x00 && - (s_ninode & 0xFF) == 0x00) - { - // Byteswap - s_fsize = ((s_fsize & 0xFF) << 24) + ((s_fsize & 0xFF00) << 8) + ((s_fsize & 0xFF0000) >> 8) + - ((s_fsize & 0xFF000000) >> 24); - - s_nfree = (ushort)(s_nfree >> 8); - s_ninode = (ushort)(s_ninode >> 8); - } - - if((s_fsize & 0xFF000000) != 0x00 || - (s_nfree & 0xFF00) != 0x00 || - (s_ninode & 0xFF00) != 0x00) - continue; - - if(s_fsize >= V7_MAXSIZE || - s_nfree >= V7_NICFREE || - s_ninode >= V7_NICINOD) - continue; - - if(s_fsize * 1024 == (partition.End - partition.Start) * imagePlugin.Info.SectorSize || - s_fsize * 512 == (partition.End - partition.Start) * imagePlugin.Info.SectorSize) - return true; + s_nfree = (ushort)(s_nfree >> 8); + s_ninode = (ushort)(s_ninode >> 8); } - return false; + if((s_fsize & 0xFF000000) != 0x00 || + (s_nfree & 0xFF00) != 0x00 || + (s_ninode & 0xFF00) != 0x00) + continue; + + if(s_fsize >= V7_MAXSIZE || + s_nfree >= V7_NICFREE || + s_ninode >= V7_NICINOD) + continue; + + if(s_fsize * 1024 == (partition.End - partition.Start) * imagePlugin.Info.SectorSize || + s_fsize * 512 == (partition.End - partition.Start) * imagePlugin.Info.SectorSize) + return true; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + return false; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + bool bigEndian = false; // Start in little endian until we know what are we handling here + int start = 0; + bool xenix = false; + bool sysv = false; + bool sys7th = false; + bool coherent = false; + bool xenix3 = false; + byte[] sb_sector; + byte sb_size_in_sectors; + int offset = 0; + + if(imagePlugin.Info.SectorSize <= + 0x400) // Check if underlying device sector size is smaller than SuperBlock size + sb_size_in_sectors = (byte)(0x400 / imagePlugin.Info.SectorSize); + else + sb_size_in_sectors = 1; // If not a single sector can store it + + // Sectors in a cylinder + int spc = (int)(imagePlugin.Info.Heads * imagePlugin.Info.SectorsPerTrack); + + // Superblock can start on 0x000, 0x200, 0x600 and 0x800, not aligned, so we assume 16 (128 bytes/sector) sectors as a safe value + int[] locations = { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - var sb = new StringBuilder(); - bool bigEndian = false; // Start in little endian until we know what are we handling here - int start = 0; - bool xenix = false; - bool sysv = false; - bool sys7th = false; - bool coherent = false; - bool xenix3 = false; - byte[] sb_sector; - byte sb_size_in_sectors; - int offset = 0; + // Superblock can also skip one cylinder (for boot) + spc + }; - if(imagePlugin.Info.SectorSize <= - 0x400) // Check if underlying device sector size is smaller than SuperBlock size - sb_size_in_sectors = (byte)(0x400 / imagePlugin.Info.SectorSize); - else - sb_size_in_sectors = 1; // If not a single sector can store it + ErrorNumber errno; - // Sectors in a cylinder - int spc = (int)(imagePlugin.Info.Heads * imagePlugin.Info.SectorsPerTrack); + foreach(int i in locations) + { + errno = imagePlugin.ReadSectors((ulong)i + partition.Start, sb_size_in_sectors, out sb_sector); - // Superblock can start on 0x000, 0x200, 0x600 and 0x800, not aligned, so we assume 16 (128 bytes/sector) sectors as a safe value - int[] locations = + if(errno != ErrorNumber.NoError) + continue; + + uint magic = BitConverter.ToUInt32(sb_sector, 0x3F8); + + if(magic == XENIX_MAGIC || + magic == SYSV_MAGIC) { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - - // Superblock can also skip one cylinder (for boot) - spc - }; - - ErrorNumber errno; - - foreach(int i in locations) - { - errno = imagePlugin.ReadSectors((ulong)i + partition.Start, sb_size_in_sectors, out sb_sector); - - if(errno != ErrorNumber.NoError) - continue; - - uint magic = BitConverter.ToUInt32(sb_sector, 0x3F8); - - if(magic == XENIX_MAGIC || - magic == SYSV_MAGIC) - { - if(magic == SYSV_MAGIC) - { - sysv = true; - offset = 0x200; - } - else - xenix = true; - - start = i; - - break; - } - - if(magic == XENIX_CIGAM || - magic == SYSV_CIGAM) - { - bigEndian = true; // Big endian - - if(magic == SYSV_CIGAM) - { - sysv = true; - offset = 0x200; - } - else - xenix = true; - - start = i; - - break; - } - - magic = BitConverter.ToUInt32(sb_sector, 0x1F0); // XENIX 3 magic location - - if(magic == XENIX_MAGIC) - { - xenix3 = true; - start = i; - - break; - } - - if(magic == XENIX_CIGAM) - { - bigEndian = true; // Big endian - xenix3 = true; - start = i; - - break; - } - - magic = BitConverter.ToUInt32(sb_sector, 0x1F8); // XENIX magic location - if(magic == SYSV_MAGIC) { - sysv = true; - start = i; - - break; + sysv = true; + offset = 0x200; } + else + xenix = true; + + start = i; + + break; + } + + if(magic == XENIX_CIGAM || + magic == SYSV_CIGAM) + { + bigEndian = true; // Big endian if(magic == SYSV_CIGAM) { - bigEndian = true; // Big endian - sysv = true; - start = i; - - break; + sysv = true; + offset = 0x200; } + else + xenix = true; - byte[] coherent_string = new byte[6]; - Array.Copy(sb_sector, 0x1E4, coherent_string, 0, 6); // Coherent UNIX s_fname location - string s_fname = StringHandlers.CToString(coherent_string, Encoding); - Array.Copy(sb_sector, 0x1EA, coherent_string, 0, 6); // Coherent UNIX s_fpack location - string s_fpack = StringHandlers.CToString(coherent_string, Encoding); + start = i; - if((s_fname == COH_FNAME && s_fpack == COH_FPACK) || - (s_fname == COH_XXXXX && s_fpack == COH_XXXXX) || - (s_fname == COH_XXXXS && s_fpack == COH_XXXXN)) - { - coherent = true; - start = i; + break; + } - break; - } + magic = BitConverter.ToUInt32(sb_sector, 0x1F0); // XENIX 3 magic location - // Now try to identify 7th edition - uint s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); - ushort s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); - ushort s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); - - if(s_fsize <= 0 || - s_fsize >= 0xFFFFFFFF || - s_nfree <= 0 || - s_nfree >= 0xFFFF || - s_ninode <= 0 || - s_ninode >= 0xFFFF) - continue; - - if((s_fsize & 0xFF) == 0x00 && - (s_nfree & 0xFF) == 0x00 && - (s_ninode & 0xFF) == 0x00) - { - // Byteswap - s_fsize = ((s_fsize & 0xFF) << 24) + ((s_fsize & 0xFF00) << 8) + ((s_fsize & 0xFF0000) >> 8) + - ((s_fsize & 0xFF000000) >> 24); - - s_nfree = (ushort)(s_nfree >> 8); - s_ninode = (ushort)(s_ninode >> 8); - } - - if((s_fsize & 0xFF000000) != 0x00 || - (s_nfree & 0xFF00) != 0x00 || - (s_ninode & 0xFF00) != 0x00) - continue; - - if(s_fsize >= V7_MAXSIZE || - s_nfree >= V7_NICFREE || - s_ninode >= V7_NICINOD) - continue; - - if(s_fsize * 1024 != (partition.End - partition.Start) * imagePlugin.Info.SectorSize && - s_fsize * 512 != (partition.End - partition.Start) * imagePlugin.Info.SectorSize) - continue; - - sys7th = true; + if(magic == XENIX_MAGIC) + { + xenix3 = true; start = i; break; } - if(!sys7th && - !sysv && - !coherent && - !xenix && - !xenix3) + if(magic == XENIX_CIGAM) + { + bigEndian = true; // Big endian + xenix3 = true; + start = i; + + break; + } + + magic = BitConverter.ToUInt32(sb_sector, 0x1F8); // XENIX magic location + + if(magic == SYSV_MAGIC) + { + sysv = true; + start = i; + + break; + } + + if(magic == SYSV_CIGAM) + { + bigEndian = true; // Big endian + sysv = true; + start = i; + + break; + } + + byte[] coherent_string = new byte[6]; + Array.Copy(sb_sector, 0x1E4, coherent_string, 0, 6); // Coherent UNIX s_fname location + string s_fname = StringHandlers.CToString(coherent_string, Encoding); + Array.Copy(sb_sector, 0x1EA, coherent_string, 0, 6); // Coherent UNIX s_fpack location + string s_fpack = StringHandlers.CToString(coherent_string, Encoding); + + if((s_fname == COH_FNAME && s_fpack == COH_FPACK) || + (s_fname == COH_XXXXX && s_fpack == COH_XXXXX) || + (s_fname == COH_XXXXS && s_fpack == COH_XXXXN)) + { + coherent = true; + start = i; + + break; + } + + // Now try to identify 7th edition + uint s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); + ushort s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); + ushort s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); + + if(s_fsize <= 0 || + s_fsize >= 0xFFFFFFFF || + s_nfree <= 0 || + s_nfree >= 0xFFFF || + s_ninode <= 0 || + s_ninode >= 0xFFFF) + continue; + + if((s_fsize & 0xFF) == 0x00 && + (s_nfree & 0xFF) == 0x00 && + (s_ninode & 0xFF) == 0x00) + { + // Byteswap + s_fsize = ((s_fsize & 0xFF) << 24) + ((s_fsize & 0xFF00) << 8) + ((s_fsize & 0xFF0000) >> 8) + + ((s_fsize & 0xFF000000) >> 24); + + s_nfree = (ushort)(s_nfree >> 8); + s_ninode = (ushort)(s_ninode >> 8); + } + + if((s_fsize & 0xFF000000) != 0x00 || + (s_nfree & 0xFF00) != 0x00 || + (s_ninode & 0xFF00) != 0x00) + continue; + + if(s_fsize >= V7_MAXSIZE || + s_nfree >= V7_NICFREE || + s_ninode >= V7_NICINOD) + continue; + + if(s_fsize * 1024 != (partition.End - partition.Start) * imagePlugin.Info.SectorSize && + s_fsize * 512 != (partition.End - partition.Start) * imagePlugin.Info.SectorSize) + continue; + + sys7th = true; + start = i; + + break; + } + + if(!sys7th && + !sysv && + !coherent && + !xenix && + !xenix3) + return; + + XmlFsType = new FileSystemType(); + + if(xenix || xenix3) + { + byte[] xenix_strings = new byte[6]; + var xnx_sb = new XenixSuperBlock(); + errno = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors, out sb_sector); + + if(errno != ErrorNumber.NoError) return; - XmlFsType = new FileSystemType(); - - if(xenix || xenix3) + if(xenix3) { - byte[] xenix_strings = new byte[6]; - var xnx_sb = new XenixSuperBlock(); - errno = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors, out sb_sector); - - if(errno != ErrorNumber.NoError) - return; - - if(xenix3) - { - xnx_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); - xnx_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); - xnx_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); - xnx_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); - xnx_sb.s_flock = sb_sector[0x19A]; - xnx_sb.s_ilock = sb_sector[0x19B]; - xnx_sb.s_fmod = sb_sector[0x19C]; - xnx_sb.s_ronly = sb_sector[0x19D]; - xnx_sb.s_time = BitConverter.ToInt32(sb_sector, 0x19E); - xnx_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x1A2); - xnx_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1A6); - xnx_sb.s_cylblks = BitConverter.ToUInt16(sb_sector, 0x1A8); - xnx_sb.s_gapblks = BitConverter.ToUInt16(sb_sector, 0x1AA); - xnx_sb.s_dinfo0 = BitConverter.ToUInt16(sb_sector, 0x1AC); - xnx_sb.s_dinfo1 = BitConverter.ToUInt16(sb_sector, 0x1AE); - Array.Copy(sb_sector, 0x1B0, xenix_strings, 0, 6); - xnx_sb.s_fname = StringHandlers.CToString(xenix_strings, Encoding); - Array.Copy(sb_sector, 0x1B6, xenix_strings, 0, 6); - xnx_sb.s_fpack = StringHandlers.CToString(xenix_strings, Encoding); - xnx_sb.s_clean = sb_sector[0x1BC]; - xnx_sb.s_magic = BitConverter.ToUInt32(sb_sector, 0x1F0); - xnx_sb.s_type = BitConverter.ToUInt32(sb_sector, 0x1F4); - } - else - { - xnx_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); - xnx_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); - xnx_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); - xnx_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x198); - xnx_sb.s_flock = sb_sector[0x262]; - xnx_sb.s_ilock = sb_sector[0x263]; - xnx_sb.s_fmod = sb_sector[0x264]; - xnx_sb.s_ronly = sb_sector[0x265]; - xnx_sb.s_time = BitConverter.ToInt32(sb_sector, 0x266); - xnx_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x26A); - xnx_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x26E); - xnx_sb.s_cylblks = BitConverter.ToUInt16(sb_sector, 0x270); - xnx_sb.s_gapblks = BitConverter.ToUInt16(sb_sector, 0x272); - xnx_sb.s_dinfo0 = BitConverter.ToUInt16(sb_sector, 0x274); - xnx_sb.s_dinfo1 = BitConverter.ToUInt16(sb_sector, 0x276); - Array.Copy(sb_sector, 0x278, xenix_strings, 0, 6); - xnx_sb.s_fname = StringHandlers.CToString(xenix_strings, Encoding); - Array.Copy(sb_sector, 0x27E, xenix_strings, 0, 6); - xnx_sb.s_fpack = StringHandlers.CToString(xenix_strings, Encoding); - xnx_sb.s_clean = sb_sector[0x284]; - xnx_sb.s_magic = BitConverter.ToUInt32(sb_sector, 0x3F8); - xnx_sb.s_type = BitConverter.ToUInt32(sb_sector, 0x3FC); - } - - if(bigEndian) - { - xnx_sb.s_isize = Swapping.Swap(xnx_sb.s_isize); - xnx_sb.s_fsize = Swapping.Swap(xnx_sb.s_fsize); - xnx_sb.s_nfree = Swapping.Swap(xnx_sb.s_nfree); - xnx_sb.s_ninode = Swapping.Swap(xnx_sb.s_ninode); - xnx_sb.s_time = Swapping.Swap(xnx_sb.s_time); - xnx_sb.s_tfree = Swapping.Swap(xnx_sb.s_tfree); - xnx_sb.s_tinode = Swapping.Swap(xnx_sb.s_tinode); - xnx_sb.s_cylblks = Swapping.Swap(xnx_sb.s_cylblks); - xnx_sb.s_gapblks = Swapping.Swap(xnx_sb.s_gapblks); - xnx_sb.s_dinfo0 = Swapping.Swap(xnx_sb.s_dinfo0); - xnx_sb.s_dinfo1 = Swapping.Swap(xnx_sb.s_dinfo1); - xnx_sb.s_magic = Swapping.Swap(xnx_sb.s_magic); - xnx_sb.s_type = Swapping.Swap(xnx_sb.s_type); - } - - uint bs = 512; - sb.AppendLine("XENIX filesystem"); - XmlFsType.Type = "XENIX fs"; - - switch(xnx_sb.s_type) - { - case 1: - sb.AppendLine("512 bytes per block"); - XmlFsType.ClusterSize = 512; - - break; - case 2: - sb.AppendLine("1024 bytes per block"); - bs = 1024; - XmlFsType.ClusterSize = 1024; - - break; - case 3: - sb.AppendLine("2048 bytes per block"); - bs = 2048; - XmlFsType.ClusterSize = 2048; - - break; - default: - sb.AppendFormat("Unknown s_type value: 0x{0:X8}", xnx_sb.s_type).AppendLine(); - - break; - } - - if(imagePlugin.Info.SectorSize == 2336 || - imagePlugin.Info.SectorSize == 2352 || - imagePlugin.Info.SectorSize == 2448) - { - if(bs != 2048) - sb. - AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", - bs, 2048).AppendLine(); - } - else - { - if(bs != imagePlugin.Info.SectorSize) - sb. - AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", - bs, imagePlugin.Info.SectorSize).AppendLine(); - } - - sb.AppendFormat("{0} zones on volume ({1} bytes)", xnx_sb.s_fsize, xnx_sb.s_fsize * bs).AppendLine(); - - sb.AppendFormat("{0} free zones on volume ({1} bytes)", xnx_sb.s_tfree, xnx_sb.s_tfree * bs). - AppendLine(); - - sb.AppendFormat("{0} free blocks on list ({1} bytes)", xnx_sb.s_nfree, xnx_sb.s_nfree * bs). - AppendLine(); - - sb.AppendFormat("{0} blocks per cylinder ({1} bytes)", xnx_sb.s_cylblks, xnx_sb.s_cylblks * bs). - AppendLine(); - - sb.AppendFormat("{0} blocks per gap ({1} bytes)", xnx_sb.s_gapblks, xnx_sb.s_gapblks * bs).AppendLine(); - sb.AppendFormat("First data zone: {0}", xnx_sb.s_isize).AppendLine(); - sb.AppendFormat("{0} free inodes on volume", xnx_sb.s_tinode).AppendLine(); - sb.AppendFormat("{0} free inodes on list", xnx_sb.s_ninode).AppendLine(); - - if(xnx_sb.s_flock > 0) - sb.AppendLine("Free block list is locked"); - - if(xnx_sb.s_ilock > 0) - sb.AppendLine("inode cache is locked"); - - if(xnx_sb.s_fmod > 0) - sb.AppendLine("Superblock is being modified"); - - if(xnx_sb.s_ronly > 0) - sb.AppendLine("Volume is mounted read-only"); - - sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixToDateTime(xnx_sb.s_time)). - AppendLine(); - - if(xnx_sb.s_time != 0) - { - XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(xnx_sb.s_time); - XmlFsType.ModificationDateSpecified = true; - } - - sb.AppendFormat("Volume name: {0}", xnx_sb.s_fname).AppendLine(); - XmlFsType.VolumeName = xnx_sb.s_fname; - sb.AppendFormat("Pack name: {0}", xnx_sb.s_fpack).AppendLine(); - - if(xnx_sb.s_clean == 0x46) - sb.AppendLine("Volume is clean"); - else - { - sb.AppendLine("Volume is dirty"); - XmlFsType.Dirty = true; - } + xnx_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); + xnx_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); + xnx_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); + xnx_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); + xnx_sb.s_flock = sb_sector[0x19A]; + xnx_sb.s_ilock = sb_sector[0x19B]; + xnx_sb.s_fmod = sb_sector[0x19C]; + xnx_sb.s_ronly = sb_sector[0x19D]; + xnx_sb.s_time = BitConverter.ToInt32(sb_sector, 0x19E); + xnx_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x1A2); + xnx_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1A6); + xnx_sb.s_cylblks = BitConverter.ToUInt16(sb_sector, 0x1A8); + xnx_sb.s_gapblks = BitConverter.ToUInt16(sb_sector, 0x1AA); + xnx_sb.s_dinfo0 = BitConverter.ToUInt16(sb_sector, 0x1AC); + xnx_sb.s_dinfo1 = BitConverter.ToUInt16(sb_sector, 0x1AE); + Array.Copy(sb_sector, 0x1B0, xenix_strings, 0, 6); + xnx_sb.s_fname = StringHandlers.CToString(xenix_strings, Encoding); + Array.Copy(sb_sector, 0x1B6, xenix_strings, 0, 6); + xnx_sb.s_fpack = StringHandlers.CToString(xenix_strings, Encoding); + xnx_sb.s_clean = sb_sector[0x1BC]; + xnx_sb.s_magic = BitConverter.ToUInt32(sb_sector, 0x1F0); + xnx_sb.s_type = BitConverter.ToUInt32(sb_sector, 0x1F4); + } + else + { + xnx_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); + xnx_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); + xnx_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); + xnx_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x198); + xnx_sb.s_flock = sb_sector[0x262]; + xnx_sb.s_ilock = sb_sector[0x263]; + xnx_sb.s_fmod = sb_sector[0x264]; + xnx_sb.s_ronly = sb_sector[0x265]; + xnx_sb.s_time = BitConverter.ToInt32(sb_sector, 0x266); + xnx_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x26A); + xnx_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x26E); + xnx_sb.s_cylblks = BitConverter.ToUInt16(sb_sector, 0x270); + xnx_sb.s_gapblks = BitConverter.ToUInt16(sb_sector, 0x272); + xnx_sb.s_dinfo0 = BitConverter.ToUInt16(sb_sector, 0x274); + xnx_sb.s_dinfo1 = BitConverter.ToUInt16(sb_sector, 0x276); + Array.Copy(sb_sector, 0x278, xenix_strings, 0, 6); + xnx_sb.s_fname = StringHandlers.CToString(xenix_strings, Encoding); + Array.Copy(sb_sector, 0x27E, xenix_strings, 0, 6); + xnx_sb.s_fpack = StringHandlers.CToString(xenix_strings, Encoding); + xnx_sb.s_clean = sb_sector[0x284]; + xnx_sb.s_magic = BitConverter.ToUInt32(sb_sector, 0x3F8); + xnx_sb.s_type = BitConverter.ToUInt32(sb_sector, 0x3FC); } - if(sysv) + if(bigEndian) { - errno = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors, out sb_sector); - - if(errno != ErrorNumber.NoError) - return; - - byte[] sysv_strings = new byte[6]; - - var sysv_sb = new SystemVRelease4SuperBlock - { - s_type = BitConverter.ToUInt32(sb_sector, 0x1FC + offset) - }; - - if(bigEndian) - sysv_sb.s_type = Swapping.Swap(sysv_sb.s_type); - - uint bs = 512; - - switch(sysv_sb.s_type) - { - case 1: - XmlFsType.ClusterSize = 512; - - break; - case 2: - bs = 1024; - XmlFsType.ClusterSize = 1024; - - break; - case 3: - bs = 2048; - XmlFsType.ClusterSize = 2048; - - break; - default: - sb.AppendFormat("Unknown s_type value: 0x{0:X8}", sysv_sb.s_type).AppendLine(); - - break; - } - - sysv_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002 + offset); - - if(bigEndian) - sysv_sb.s_fsize = Swapping.Swap(sysv_sb.s_fsize); - - bool sysvr4 = sysv_sb.s_fsize * bs <= 0 || sysv_sb.s_fsize * bs != partition.Size; - - if(sysvr4) - { - sysv_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000 + offset); - sysv_sb.s_state = BitConverter.ToUInt32(sb_sector, 0x1F4 + offset); - sysv_sb.s_magic = BitConverter.ToUInt32(sb_sector, 0x1F8 + offset); - sysv_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x004 + offset); - sysv_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x008 + offset); - sysv_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D4 + offset); - sysv_sb.s_flock = sb_sector[0x1A0 + offset]; - sysv_sb.s_ilock = sb_sector[0x1A1 + offset]; - sysv_sb.s_fmod = sb_sector[0x1A2 + offset]; - sysv_sb.s_ronly = sb_sector[0x1A3 + offset]; - sysv_sb.s_time = BitConverter.ToUInt32(sb_sector, 0x1A4 + offset); - sysv_sb.s_cylblks = BitConverter.ToUInt16(sb_sector, 0x1A8 + offset); - sysv_sb.s_gapblks = BitConverter.ToUInt16(sb_sector, 0x1AA + offset); - sysv_sb.s_dinfo0 = BitConverter.ToUInt16(sb_sector, 0x1AC + offset); - sysv_sb.s_dinfo1 = BitConverter.ToUInt16(sb_sector, 0x1AE + offset); - sysv_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x1B0 + offset); - sysv_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1B4 + offset); - Array.Copy(sb_sector, 0x1B6 + offset, sysv_strings, 0, 6); - sysv_sb.s_fname = StringHandlers.CToString(sysv_strings, Encoding); - Array.Copy(sb_sector, 0x1BC + offset, sysv_strings, 0, 6); - sysv_sb.s_fpack = StringHandlers.CToString(sysv_strings, Encoding); - sb.AppendLine("System V Release 4 filesystem"); - XmlFsType.Type = "SVR4 fs"; - } - else - { - sysv_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000 + offset); - sysv_sb.s_state = BitConverter.ToUInt32(sb_sector, 0x1F4 + offset); - sysv_sb.s_magic = BitConverter.ToUInt32(sb_sector, 0x1F8 + offset); - sysv_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002 + offset); - sysv_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006 + offset); - sysv_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0 + offset); - sysv_sb.s_flock = sb_sector[0x19A + offset]; - sysv_sb.s_ilock = sb_sector[0x19B + offset]; - sysv_sb.s_fmod = sb_sector[0x19C + offset]; - sysv_sb.s_ronly = sb_sector[0x19D + offset]; - sysv_sb.s_time = BitConverter.ToUInt32(sb_sector, 0x19E + offset); - sysv_sb.s_cylblks = BitConverter.ToUInt16(sb_sector, 0x1A2 + offset); - sysv_sb.s_gapblks = BitConverter.ToUInt16(sb_sector, 0x1A4 + offset); - sysv_sb.s_dinfo0 = BitConverter.ToUInt16(sb_sector, 0x1A6 + offset); - sysv_sb.s_dinfo1 = BitConverter.ToUInt16(sb_sector, 0x1A8 + offset); - sysv_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x1AA + offset); - sysv_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1AE + offset); - Array.Copy(sb_sector, 0x1B0 + offset, sysv_strings, 0, 6); - sysv_sb.s_fname = StringHandlers.CToString(sysv_strings, Encoding); - Array.Copy(sb_sector, 0x1B6 + offset, sysv_strings, 0, 6); - sysv_sb.s_fpack = StringHandlers.CToString(sysv_strings, Encoding); - sb.AppendLine("System V Release 2 filesystem"); - XmlFsType.Type = "SVR2 fs"; - } - - if(bigEndian) - { - sysv_sb.s_isize = Swapping.Swap(sysv_sb.s_isize); - sysv_sb.s_state = Swapping.Swap(sysv_sb.s_state); - sysv_sb.s_magic = Swapping.Swap(sysv_sb.s_magic); - sysv_sb.s_fsize = Swapping.Swap(sysv_sb.s_fsize); - sysv_sb.s_nfree = Swapping.Swap(sysv_sb.s_nfree); - sysv_sb.s_ninode = Swapping.Swap(sysv_sb.s_ninode); - sysv_sb.s_time = Swapping.Swap(sysv_sb.s_time); - sysv_sb.s_cylblks = Swapping.Swap(sysv_sb.s_cylblks); - sysv_sb.s_gapblks = Swapping.Swap(sysv_sb.s_gapblks); - sysv_sb.s_dinfo0 = Swapping.Swap(sysv_sb.s_dinfo0); - sysv_sb.s_dinfo1 = Swapping.Swap(sysv_sb.s_dinfo1); - sysv_sb.s_tfree = Swapping.Swap(sysv_sb.s_tfree); - sysv_sb.s_tinode = Swapping.Swap(sysv_sb.s_tinode); - } - - sb.AppendFormat("{0} bytes per block", bs).AppendLine(); - - XmlFsType.Clusters = sysv_sb.s_fsize; - sb.AppendFormat("{0} zones on volume ({1} bytes)", sysv_sb.s_fsize, sysv_sb.s_fsize * bs).AppendLine(); - - sb.AppendFormat("{0} free zones on volume ({1} bytes)", sysv_sb.s_tfree, sysv_sb.s_tfree * bs). - AppendLine(); - - sb.AppendFormat("{0} free blocks on list ({1} bytes)", sysv_sb.s_nfree, sysv_sb.s_nfree * bs). - AppendLine(); - - sb.AppendFormat("{0} blocks per cylinder ({1} bytes)", sysv_sb.s_cylblks, sysv_sb.s_cylblks * bs). - AppendLine(); - - sb.AppendFormat("{0} blocks per gap ({1} bytes)", sysv_sb.s_gapblks, sysv_sb.s_gapblks * bs). - AppendLine(); - - sb.AppendFormat("First data zone: {0}", sysv_sb.s_isize).AppendLine(); - sb.AppendFormat("{0} free inodes on volume", sysv_sb.s_tinode).AppendLine(); - sb.AppendFormat("{0} free inodes on list", sysv_sb.s_ninode).AppendLine(); - - if(sysv_sb.s_flock > 0) - sb.AppendLine("Free block list is locked"); - - if(sysv_sb.s_ilock > 0) - sb.AppendLine("inode cache is locked"); - - if(sysv_sb.s_fmod > 0) - sb.AppendLine("Superblock is being modified"); - - if(sysv_sb.s_ronly > 0) - sb.AppendLine("Volume is mounted read-only"); - - sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(sysv_sb.s_time)). - AppendLine(); - - if(sysv_sb.s_time != 0) - { - XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(sysv_sb.s_time); - XmlFsType.ModificationDateSpecified = true; - } - - sb.AppendFormat("Volume name: {0}", sysv_sb.s_fname).AppendLine(); - XmlFsType.VolumeName = sysv_sb.s_fname; - sb.AppendFormat("Pack name: {0}", sysv_sb.s_fpack).AppendLine(); - - if(sysv_sb.s_state == 0x7C269D38 - sysv_sb.s_time) - sb.AppendLine("Volume is clean"); - else - { - sb.AppendLine("Volume is dirty"); - XmlFsType.Dirty = true; - } + xnx_sb.s_isize = Swapping.Swap(xnx_sb.s_isize); + xnx_sb.s_fsize = Swapping.Swap(xnx_sb.s_fsize); + xnx_sb.s_nfree = Swapping.Swap(xnx_sb.s_nfree); + xnx_sb.s_ninode = Swapping.Swap(xnx_sb.s_ninode); + xnx_sb.s_time = Swapping.Swap(xnx_sb.s_time); + xnx_sb.s_tfree = Swapping.Swap(xnx_sb.s_tfree); + xnx_sb.s_tinode = Swapping.Swap(xnx_sb.s_tinode); + xnx_sb.s_cylblks = Swapping.Swap(xnx_sb.s_cylblks); + xnx_sb.s_gapblks = Swapping.Swap(xnx_sb.s_gapblks); + xnx_sb.s_dinfo0 = Swapping.Swap(xnx_sb.s_dinfo0); + xnx_sb.s_dinfo1 = Swapping.Swap(xnx_sb.s_dinfo1); + xnx_sb.s_magic = Swapping.Swap(xnx_sb.s_magic); + xnx_sb.s_type = Swapping.Swap(xnx_sb.s_type); } - if(coherent) + uint bs = 512; + sb.AppendLine("XENIX filesystem"); + XmlFsType.Type = "XENIX fs"; + + switch(xnx_sb.s_type) { - errno = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors, out sb_sector); + case 1: + sb.AppendLine("512 bytes per block"); + XmlFsType.ClusterSize = 512; - if(errno != ErrorNumber.NoError) - return; + break; + case 2: + sb.AppendLine("1024 bytes per block"); + bs = 1024; + XmlFsType.ClusterSize = 1024; - var coh_sb = new CoherentSuperBlock(); - byte[] coh_strings = new byte[6]; + break; + case 3: + sb.AppendLine("2048 bytes per block"); + bs = 2048; + XmlFsType.ClusterSize = 2048; - coh_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); - coh_sb.s_fsize = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x002)); - coh_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); - coh_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x108); - coh_sb.s_flock = sb_sector[0x1D2]; - coh_sb.s_ilock = sb_sector[0x1D3]; - coh_sb.s_fmod = sb_sector[0x1D4]; - coh_sb.s_ronly = sb_sector[0x1D5]; - coh_sb.s_time = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x1D6)); - coh_sb.s_tfree = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x1DA)); - coh_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1DE); - coh_sb.s_int_m = BitConverter.ToUInt16(sb_sector, 0x1E0); - coh_sb.s_int_n = BitConverter.ToUInt16(sb_sector, 0x1E2); - Array.Copy(sb_sector, 0x1E4, coh_strings, 0, 6); - coh_sb.s_fname = StringHandlers.CToString(coh_strings, Encoding); - Array.Copy(sb_sector, 0x1EA, coh_strings, 0, 6); - coh_sb.s_fpack = StringHandlers.CToString(coh_strings, Encoding); + break; + default: + sb.AppendFormat("Unknown s_type value: 0x{0:X8}", xnx_sb.s_type).AppendLine(); - XmlFsType.Type = "Coherent fs"; - XmlFsType.ClusterSize = 512; - XmlFsType.Clusters = coh_sb.s_fsize; + break; + } - sb.AppendLine("Coherent UNIX filesystem"); - - if(imagePlugin.Info.SectorSize != 512) + if(imagePlugin.Info.SectorSize == 2336 || + imagePlugin.Info.SectorSize == 2352 || + imagePlugin.Info.SectorSize == 2448) + { + if(bs != 2048) sb. AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", - 512, 2048).AppendLine(); - - sb.AppendFormat("{0} zones on volume ({1} bytes)", coh_sb.s_fsize, coh_sb.s_fsize * 512).AppendLine(); - - sb.AppendFormat("{0} free zones on volume ({1} bytes)", coh_sb.s_tfree, coh_sb.s_tfree * 512). - AppendLine(); - - sb.AppendFormat("{0} free blocks on list ({1} bytes)", coh_sb.s_nfree, coh_sb.s_nfree * 512). - AppendLine(); - - sb.AppendFormat("First data zone: {0}", coh_sb.s_isize).AppendLine(); - sb.AppendFormat("{0} free inodes on volume", coh_sb.s_tinode).AppendLine(); - sb.AppendFormat("{0} free inodes on list", coh_sb.s_ninode).AppendLine(); - - if(coh_sb.s_flock > 0) - sb.AppendLine("Free block list is locked"); - - if(coh_sb.s_ilock > 0) - sb.AppendLine("inode cache is locked"); - - if(coh_sb.s_fmod > 0) - sb.AppendLine("Superblock is being modified"); - - if(coh_sb.s_ronly > 0) - sb.AppendLine("Volume is mounted read-only"); - - sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(coh_sb.s_time)). - AppendLine(); - - if(coh_sb.s_time != 0) - { - XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(coh_sb.s_time); - XmlFsType.ModificationDateSpecified = true; - } - - sb.AppendFormat("Volume name: {0}", coh_sb.s_fname).AppendLine(); - XmlFsType.VolumeName = coh_sb.s_fname; - sb.AppendFormat("Pack name: {0}", coh_sb.s_fpack).AppendLine(); + bs, 2048).AppendLine(); } - - if(sys7th) + else { - errno = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors, out sb_sector); - - if(errno != ErrorNumber.NoError) - return; - - var v7_sb = new UNIX7thEditionSuperBlock(); - byte[] sys7_strings = new byte[6]; - - v7_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); - v7_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); - v7_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); - v7_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); - v7_sb.s_flock = sb_sector[0x19A]; - v7_sb.s_ilock = sb_sector[0x19B]; - v7_sb.s_fmod = sb_sector[0x19C]; - v7_sb.s_ronly = sb_sector[0x19D]; - v7_sb.s_time = BitConverter.ToUInt32(sb_sector, 0x19E); - v7_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x1A2); - v7_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1A6); - v7_sb.s_int_m = BitConverter.ToUInt16(sb_sector, 0x1A8); - v7_sb.s_int_n = BitConverter.ToUInt16(sb_sector, 0x1AA); - Array.Copy(sb_sector, 0x1AC, sys7_strings, 0, 6); - v7_sb.s_fname = StringHandlers.CToString(sys7_strings, Encoding); - Array.Copy(sb_sector, 0x1B2, sys7_strings, 0, 6); - v7_sb.s_fpack = StringHandlers.CToString(sys7_strings, Encoding); - - XmlFsType.Type = "UNIX 7th Edition fs"; - XmlFsType.ClusterSize = 512; - XmlFsType.Clusters = v7_sb.s_fsize; - sb.AppendLine("UNIX 7th Edition filesystem"); - - if(imagePlugin.Info.SectorSize != 512) + if(bs != imagePlugin.Info.SectorSize) sb. AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", - 512, 2048).AppendLine(); - - sb.AppendFormat("{0} zones on volume ({1} bytes)", v7_sb.s_fsize, v7_sb.s_fsize * 512).AppendLine(); - - sb.AppendFormat("{0} free zones on volume ({1} bytes)", v7_sb.s_tfree, v7_sb.s_tfree * 512). - AppendLine(); - - sb.AppendFormat("{0} free blocks on list ({1} bytes)", v7_sb.s_nfree, v7_sb.s_nfree * 512).AppendLine(); - sb.AppendFormat("First data zone: {0}", v7_sb.s_isize).AppendLine(); - sb.AppendFormat("{0} free inodes on volume", v7_sb.s_tinode).AppendLine(); - sb.AppendFormat("{0} free inodes on list", v7_sb.s_ninode).AppendLine(); - - if(v7_sb.s_flock > 0) - sb.AppendLine("Free block list is locked"); - - if(v7_sb.s_ilock > 0) - sb.AppendLine("inode cache is locked"); - - if(v7_sb.s_fmod > 0) - sb.AppendLine("Superblock is being modified"); - - if(v7_sb.s_ronly > 0) - sb.AppendLine("Volume is mounted read-only"); - - sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(v7_sb.s_time)). - AppendLine(); - - if(v7_sb.s_time != 0) - { - XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(v7_sb.s_time); - XmlFsType.ModificationDateSpecified = true; - } - - sb.AppendFormat("Volume name: {0}", v7_sb.s_fname).AppendLine(); - XmlFsType.VolumeName = v7_sb.s_fname; - sb.AppendFormat("Pack name: {0}", v7_sb.s_fpack).AppendLine(); + bs, imagePlugin.Info.SectorSize).AppendLine(); } - information = sb.ToString(); + sb.AppendFormat("{0} zones on volume ({1} bytes)", xnx_sb.s_fsize, xnx_sb.s_fsize * bs).AppendLine(); + + sb.AppendFormat("{0} free zones on volume ({1} bytes)", xnx_sb.s_tfree, xnx_sb.s_tfree * bs). + AppendLine(); + + sb.AppendFormat("{0} free blocks on list ({1} bytes)", xnx_sb.s_nfree, xnx_sb.s_nfree * bs). + AppendLine(); + + sb.AppendFormat("{0} blocks per cylinder ({1} bytes)", xnx_sb.s_cylblks, xnx_sb.s_cylblks * bs). + AppendLine(); + + sb.AppendFormat("{0} blocks per gap ({1} bytes)", xnx_sb.s_gapblks, xnx_sb.s_gapblks * bs).AppendLine(); + sb.AppendFormat("First data zone: {0}", xnx_sb.s_isize).AppendLine(); + sb.AppendFormat("{0} free inodes on volume", xnx_sb.s_tinode).AppendLine(); + sb.AppendFormat("{0} free inodes on list", xnx_sb.s_ninode).AppendLine(); + + if(xnx_sb.s_flock > 0) + sb.AppendLine("Free block list is locked"); + + if(xnx_sb.s_ilock > 0) + sb.AppendLine("inode cache is locked"); + + if(xnx_sb.s_fmod > 0) + sb.AppendLine("Superblock is being modified"); + + if(xnx_sb.s_ronly > 0) + sb.AppendLine("Volume is mounted read-only"); + + sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixToDateTime(xnx_sb.s_time)). + AppendLine(); + + if(xnx_sb.s_time != 0) + { + XmlFsType.ModificationDate = DateHandlers.UnixToDateTime(xnx_sb.s_time); + XmlFsType.ModificationDateSpecified = true; + } + + sb.AppendFormat("Volume name: {0}", xnx_sb.s_fname).AppendLine(); + XmlFsType.VolumeName = xnx_sb.s_fname; + sb.AppendFormat("Pack name: {0}", xnx_sb.s_fpack).AppendLine(); + + if(xnx_sb.s_clean == 0x46) + sb.AppendLine("Volume is clean"); + else + { + sb.AppendLine("Volume is dirty"); + XmlFsType.Dirty = true; + } } - // Old XENIX use different offsets - struct XenixSuperBlock + if(sysv) { - /// 0x000, index of first data zone - public ushort s_isize; - /// 0x002, total number of zones of this volume - public uint s_fsize; + errno = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors, out sb_sector); - // the start of the free block list: - /// 0x006, blocks in s_free, <=100 - public ushort s_nfree; - /// 0x008, 100 entries, 50 entries for Xenix 3, first free block list chunk - public uint[] s_free; + if(errno != ErrorNumber.NoError) + return; - // the cache of free inodes: - /// 0x198 (0xD0), number of inodes in s_inode, <= 100 - public ushort s_ninode; - /// 0x19A (0xD2), 100 entries, some free inodes - public ushort[] s_inode; - /// 0x262 (0x19A), free block list manipulation lock - public byte s_flock; - /// 0x263 (0x19B), inode cache manipulation lock - public byte s_ilock; - /// 0x264 (0x19C), superblock modification flag - public byte s_fmod; - /// 0x265 (0x19D), read-only mounted flag - public byte s_ronly; - /// 0x266 (0x19E), time of last superblock update - public int s_time; - /// 0x26A (0x1A2), total number of free zones - public uint s_tfree; - /// 0x26E (0x1A6), total number of free inodes - public ushort s_tinode; - /// 0x270 (0x1A8), blocks per cylinder - public ushort s_cylblks; - /// 0x272 (0x1AA), blocks per gap - public ushort s_gapblks; - /// 0x274 (0x1AC), device information ?? - public ushort s_dinfo0; - /// 0x276 (0x1AE), device information ?? - public ushort s_dinfo1; - /// 0x278 (0x1B0), 6 bytes, volume name - public string s_fname; - /// 0x27E (0x1B6), 6 bytes, pack name - public string s_fpack; - /// 0x284 (0x1BC), 0x46 if volume is clean - public byte s_clean; - /// 0x285 (0x1BD), 371 bytes, 51 bytes for Xenix 3 - public byte[] s_fill; - /// 0x3F8 (0x1F0), magic - public uint s_magic; - /// 0x3FC (0x1F4), filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk, 3 = 2048 bytes/blk) - public uint s_type; + byte[] sysv_strings = new byte[6]; + + var sysv_sb = new SystemVRelease4SuperBlock + { + s_type = BitConverter.ToUInt32(sb_sector, 0x1FC + offset) + }; + + if(bigEndian) + sysv_sb.s_type = Swapping.Swap(sysv_sb.s_type); + + uint bs = 512; + + switch(sysv_sb.s_type) + { + case 1: + XmlFsType.ClusterSize = 512; + + break; + case 2: + bs = 1024; + XmlFsType.ClusterSize = 1024; + + break; + case 3: + bs = 2048; + XmlFsType.ClusterSize = 2048; + + break; + default: + sb.AppendFormat("Unknown s_type value: 0x{0:X8}", sysv_sb.s_type).AppendLine(); + + break; + } + + sysv_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002 + offset); + + if(bigEndian) + sysv_sb.s_fsize = Swapping.Swap(sysv_sb.s_fsize); + + bool sysvr4 = sysv_sb.s_fsize * bs <= 0 || sysv_sb.s_fsize * bs != partition.Size; + + if(sysvr4) + { + sysv_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000 + offset); + sysv_sb.s_state = BitConverter.ToUInt32(sb_sector, 0x1F4 + offset); + sysv_sb.s_magic = BitConverter.ToUInt32(sb_sector, 0x1F8 + offset); + sysv_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x004 + offset); + sysv_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x008 + offset); + sysv_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D4 + offset); + sysv_sb.s_flock = sb_sector[0x1A0 + offset]; + sysv_sb.s_ilock = sb_sector[0x1A1 + offset]; + sysv_sb.s_fmod = sb_sector[0x1A2 + offset]; + sysv_sb.s_ronly = sb_sector[0x1A3 + offset]; + sysv_sb.s_time = BitConverter.ToUInt32(sb_sector, 0x1A4 + offset); + sysv_sb.s_cylblks = BitConverter.ToUInt16(sb_sector, 0x1A8 + offset); + sysv_sb.s_gapblks = BitConverter.ToUInt16(sb_sector, 0x1AA + offset); + sysv_sb.s_dinfo0 = BitConverter.ToUInt16(sb_sector, 0x1AC + offset); + sysv_sb.s_dinfo1 = BitConverter.ToUInt16(sb_sector, 0x1AE + offset); + sysv_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x1B0 + offset); + sysv_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1B4 + offset); + Array.Copy(sb_sector, 0x1B6 + offset, sysv_strings, 0, 6); + sysv_sb.s_fname = StringHandlers.CToString(sysv_strings, Encoding); + Array.Copy(sb_sector, 0x1BC + offset, sysv_strings, 0, 6); + sysv_sb.s_fpack = StringHandlers.CToString(sysv_strings, Encoding); + sb.AppendLine("System V Release 4 filesystem"); + XmlFsType.Type = "SVR4 fs"; + } + else + { + sysv_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000 + offset); + sysv_sb.s_state = BitConverter.ToUInt32(sb_sector, 0x1F4 + offset); + sysv_sb.s_magic = BitConverter.ToUInt32(sb_sector, 0x1F8 + offset); + sysv_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002 + offset); + sysv_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006 + offset); + sysv_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0 + offset); + sysv_sb.s_flock = sb_sector[0x19A + offset]; + sysv_sb.s_ilock = sb_sector[0x19B + offset]; + sysv_sb.s_fmod = sb_sector[0x19C + offset]; + sysv_sb.s_ronly = sb_sector[0x19D + offset]; + sysv_sb.s_time = BitConverter.ToUInt32(sb_sector, 0x19E + offset); + sysv_sb.s_cylblks = BitConverter.ToUInt16(sb_sector, 0x1A2 + offset); + sysv_sb.s_gapblks = BitConverter.ToUInt16(sb_sector, 0x1A4 + offset); + sysv_sb.s_dinfo0 = BitConverter.ToUInt16(sb_sector, 0x1A6 + offset); + sysv_sb.s_dinfo1 = BitConverter.ToUInt16(sb_sector, 0x1A8 + offset); + sysv_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x1AA + offset); + sysv_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1AE + offset); + Array.Copy(sb_sector, 0x1B0 + offset, sysv_strings, 0, 6); + sysv_sb.s_fname = StringHandlers.CToString(sysv_strings, Encoding); + Array.Copy(sb_sector, 0x1B6 + offset, sysv_strings, 0, 6); + sysv_sb.s_fpack = StringHandlers.CToString(sysv_strings, Encoding); + sb.AppendLine("System V Release 2 filesystem"); + XmlFsType.Type = "SVR2 fs"; + } + + if(bigEndian) + { + sysv_sb.s_isize = Swapping.Swap(sysv_sb.s_isize); + sysv_sb.s_state = Swapping.Swap(sysv_sb.s_state); + sysv_sb.s_magic = Swapping.Swap(sysv_sb.s_magic); + sysv_sb.s_fsize = Swapping.Swap(sysv_sb.s_fsize); + sysv_sb.s_nfree = Swapping.Swap(sysv_sb.s_nfree); + sysv_sb.s_ninode = Swapping.Swap(sysv_sb.s_ninode); + sysv_sb.s_time = Swapping.Swap(sysv_sb.s_time); + sysv_sb.s_cylblks = Swapping.Swap(sysv_sb.s_cylblks); + sysv_sb.s_gapblks = Swapping.Swap(sysv_sb.s_gapblks); + sysv_sb.s_dinfo0 = Swapping.Swap(sysv_sb.s_dinfo0); + sysv_sb.s_dinfo1 = Swapping.Swap(sysv_sb.s_dinfo1); + sysv_sb.s_tfree = Swapping.Swap(sysv_sb.s_tfree); + sysv_sb.s_tinode = Swapping.Swap(sysv_sb.s_tinode); + } + + sb.AppendFormat("{0} bytes per block", bs).AppendLine(); + + XmlFsType.Clusters = sysv_sb.s_fsize; + sb.AppendFormat("{0} zones on volume ({1} bytes)", sysv_sb.s_fsize, sysv_sb.s_fsize * bs).AppendLine(); + + sb.AppendFormat("{0} free zones on volume ({1} bytes)", sysv_sb.s_tfree, sysv_sb.s_tfree * bs). + AppendLine(); + + sb.AppendFormat("{0} free blocks on list ({1} bytes)", sysv_sb.s_nfree, sysv_sb.s_nfree * bs). + AppendLine(); + + sb.AppendFormat("{0} blocks per cylinder ({1} bytes)", sysv_sb.s_cylblks, sysv_sb.s_cylblks * bs). + AppendLine(); + + sb.AppendFormat("{0} blocks per gap ({1} bytes)", sysv_sb.s_gapblks, sysv_sb.s_gapblks * bs). + AppendLine(); + + sb.AppendFormat("First data zone: {0}", sysv_sb.s_isize).AppendLine(); + sb.AppendFormat("{0} free inodes on volume", sysv_sb.s_tinode).AppendLine(); + sb.AppendFormat("{0} free inodes on list", sysv_sb.s_ninode).AppendLine(); + + if(sysv_sb.s_flock > 0) + sb.AppendLine("Free block list is locked"); + + if(sysv_sb.s_ilock > 0) + sb.AppendLine("inode cache is locked"); + + if(sysv_sb.s_fmod > 0) + sb.AppendLine("Superblock is being modified"); + + if(sysv_sb.s_ronly > 0) + sb.AppendLine("Volume is mounted read-only"); + + sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(sysv_sb.s_time)). + AppendLine(); + + if(sysv_sb.s_time != 0) + { + XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(sysv_sb.s_time); + XmlFsType.ModificationDateSpecified = true; + } + + sb.AppendFormat("Volume name: {0}", sysv_sb.s_fname).AppendLine(); + XmlFsType.VolumeName = sysv_sb.s_fname; + sb.AppendFormat("Pack name: {0}", sysv_sb.s_fpack).AppendLine(); + + if(sysv_sb.s_state == 0x7C269D38 - sysv_sb.s_time) + sb.AppendLine("Volume is clean"); + else + { + sb.AppendLine("Volume is dirty"); + XmlFsType.Dirty = true; + } } - struct SystemVRelease4SuperBlock + if(coherent) { - /// 0x000, index of first data zone - public ushort s_isize; - /// 0x002, padding - public ushort s_pad0; - /// 0x004, total number of zones of this volume - public uint s_fsize; + errno = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors, out sb_sector); - // the start of the free block list: - /// 0x008, blocks in s_free, <=100 - public ushort s_nfree; - /// 0x00A, padding - public ushort s_pad1; - /// 0x00C, 50 entries, first free block list chunk - public uint[] s_free; + if(errno != ErrorNumber.NoError) + return; - // the cache of free inodes: - /// 0x0D4, number of inodes in s_inode, <= 100 - public ushort s_ninode; - /// 0x0D6, padding - public ushort s_pad2; - /// 0x0D8, 100 entries, some free inodes - public ushort[] s_inode; - /// 0x1A0, free block list manipulation lock - public byte s_flock; - /// 0x1A1, inode cache manipulation lock - public byte s_ilock; - /// 0x1A2, superblock modification flag - public byte s_fmod; - /// 0x1A3, read-only mounted flag - public byte s_ronly; - /// 0x1A4, time of last superblock update - public uint s_time; - /// 0x1A8, blocks per cylinder - public ushort s_cylblks; - /// 0x1AA, blocks per gap - public ushort s_gapblks; - /// 0x1AC, device information ?? - public ushort s_dinfo0; - /// 0x1AE, device information ?? - public ushort s_dinfo1; - /// 0x1B0, total number of free zones - public uint s_tfree; - /// 0x1B4, total number of free inodes - public ushort s_tinode; - /// 0x1B6, padding - public ushort s_pad3; - /// 0x1B8, 6 bytes, volume name - public string s_fname; - /// 0x1BE, 6 bytes, pack name - public string s_fpack; - /// 0x1C4, 48 bytes - public byte[] s_fill; - /// 0x1F4, if s_state == (0x7C269D38 - s_time) then filesystem is clean - public uint s_state; - /// 0x1F8, magic - public uint s_magic; - /// 0x1FC, filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk) - public uint s_type; + var coh_sb = new CoherentSuperBlock(); + byte[] coh_strings = new byte[6]; + + coh_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); + coh_sb.s_fsize = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x002)); + coh_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); + coh_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x108); + coh_sb.s_flock = sb_sector[0x1D2]; + coh_sb.s_ilock = sb_sector[0x1D3]; + coh_sb.s_fmod = sb_sector[0x1D4]; + coh_sb.s_ronly = sb_sector[0x1D5]; + coh_sb.s_time = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x1D6)); + coh_sb.s_tfree = Swapping.PDPFromLittleEndian(BitConverter.ToUInt32(sb_sector, 0x1DA)); + coh_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1DE); + coh_sb.s_int_m = BitConverter.ToUInt16(sb_sector, 0x1E0); + coh_sb.s_int_n = BitConverter.ToUInt16(sb_sector, 0x1E2); + Array.Copy(sb_sector, 0x1E4, coh_strings, 0, 6); + coh_sb.s_fname = StringHandlers.CToString(coh_strings, Encoding); + Array.Copy(sb_sector, 0x1EA, coh_strings, 0, 6); + coh_sb.s_fpack = StringHandlers.CToString(coh_strings, Encoding); + + XmlFsType.Type = "Coherent fs"; + XmlFsType.ClusterSize = 512; + XmlFsType.Clusters = coh_sb.s_fsize; + + sb.AppendLine("Coherent UNIX filesystem"); + + if(imagePlugin.Info.SectorSize != 512) + sb. + AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", + 512, 2048).AppendLine(); + + sb.AppendFormat("{0} zones on volume ({1} bytes)", coh_sb.s_fsize, coh_sb.s_fsize * 512).AppendLine(); + + sb.AppendFormat("{0} free zones on volume ({1} bytes)", coh_sb.s_tfree, coh_sb.s_tfree * 512). + AppendLine(); + + sb.AppendFormat("{0} free blocks on list ({1} bytes)", coh_sb.s_nfree, coh_sb.s_nfree * 512). + AppendLine(); + + sb.AppendFormat("First data zone: {0}", coh_sb.s_isize).AppendLine(); + sb.AppendFormat("{0} free inodes on volume", coh_sb.s_tinode).AppendLine(); + sb.AppendFormat("{0} free inodes on list", coh_sb.s_ninode).AppendLine(); + + if(coh_sb.s_flock > 0) + sb.AppendLine("Free block list is locked"); + + if(coh_sb.s_ilock > 0) + sb.AppendLine("inode cache is locked"); + + if(coh_sb.s_fmod > 0) + sb.AppendLine("Superblock is being modified"); + + if(coh_sb.s_ronly > 0) + sb.AppendLine("Volume is mounted read-only"); + + sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(coh_sb.s_time)). + AppendLine(); + + if(coh_sb.s_time != 0) + { + XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(coh_sb.s_time); + XmlFsType.ModificationDateSpecified = true; + } + + sb.AppendFormat("Volume name: {0}", coh_sb.s_fname).AppendLine(); + XmlFsType.VolumeName = coh_sb.s_fname; + sb.AppendFormat("Pack name: {0}", coh_sb.s_fpack).AppendLine(); } - struct SystemVRelease2SuperBlock + if(sys7th) { - /// 0x000, index of first data zone - public ushort s_isize; - /// 0x002, total number of zones of this volume - public uint s_fsize; + errno = imagePlugin.ReadSectors((ulong)start + partition.Start, sb_size_in_sectors, out sb_sector); - // the start of the free block list: - /// 0x006, blocks in s_free, <=100 - public ushort s_nfree; - /// 0x008, 50 entries, first free block list chunk - public uint[] s_free; + if(errno != ErrorNumber.NoError) + return; - // the cache of free inodes: - /// 0x0D0, number of inodes in s_inode, <= 100 - public ushort s_ninode; - /// 0x0D2, 100 entries, some free inodes - public ushort[] s_inode; - /// 0x19A, free block list manipulation lock - public byte s_flock; - /// 0x19B, inode cache manipulation lock - public byte s_ilock; - /// 0x19C, superblock modification flag - public byte s_fmod; - /// 0x19D, read-only mounted flag - public byte s_ronly; - /// 0x19E, time of last superblock update - public uint s_time; - /// 0x1A2, blocks per cylinder - public ushort s_cylblks; - /// 0x1A4, blocks per gap - public ushort s_gapblks; - /// 0x1A6, device information ?? - public ushort s_dinfo0; - /// 0x1A8, device information ?? - public ushort s_dinfo1; - /// 0x1AA, total number of free zones - public uint s_tfree; - /// 0x1AE, total number of free inodes - public ushort s_tinode; - /// 0x1B0, 6 bytes, volume name - public string s_fname; - /// 0x1B6, 6 bytes, pack name - public string s_fpack; - /// 0x1BC, 56 bytes - public byte[] s_fill; - /// 0x1F4, if s_state == (0x7C269D38 - s_time) then filesystem is clean - public uint s_state; - /// 0x1F8, magic - public uint s_magic; - /// 0x1FC, filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk) - public uint s_type; + var v7_sb = new UNIX7thEditionSuperBlock(); + byte[] sys7_strings = new byte[6]; + + v7_sb.s_isize = BitConverter.ToUInt16(sb_sector, 0x000); + v7_sb.s_fsize = BitConverter.ToUInt32(sb_sector, 0x002); + v7_sb.s_nfree = BitConverter.ToUInt16(sb_sector, 0x006); + v7_sb.s_ninode = BitConverter.ToUInt16(sb_sector, 0x0D0); + v7_sb.s_flock = sb_sector[0x19A]; + v7_sb.s_ilock = sb_sector[0x19B]; + v7_sb.s_fmod = sb_sector[0x19C]; + v7_sb.s_ronly = sb_sector[0x19D]; + v7_sb.s_time = BitConverter.ToUInt32(sb_sector, 0x19E); + v7_sb.s_tfree = BitConverter.ToUInt32(sb_sector, 0x1A2); + v7_sb.s_tinode = BitConverter.ToUInt16(sb_sector, 0x1A6); + v7_sb.s_int_m = BitConverter.ToUInt16(sb_sector, 0x1A8); + v7_sb.s_int_n = BitConverter.ToUInt16(sb_sector, 0x1AA); + Array.Copy(sb_sector, 0x1AC, sys7_strings, 0, 6); + v7_sb.s_fname = StringHandlers.CToString(sys7_strings, Encoding); + Array.Copy(sb_sector, 0x1B2, sys7_strings, 0, 6); + v7_sb.s_fpack = StringHandlers.CToString(sys7_strings, Encoding); + + XmlFsType.Type = "UNIX 7th Edition fs"; + XmlFsType.ClusterSize = 512; + XmlFsType.Clusters = v7_sb.s_fsize; + sb.AppendLine("UNIX 7th Edition filesystem"); + + if(imagePlugin.Info.SectorSize != 512) + sb. + AppendFormat("WARNING: Filesystem indicates {0} bytes/block while device indicates {1} bytes/sector", + 512, 2048).AppendLine(); + + sb.AppendFormat("{0} zones on volume ({1} bytes)", v7_sb.s_fsize, v7_sb.s_fsize * 512).AppendLine(); + + sb.AppendFormat("{0} free zones on volume ({1} bytes)", v7_sb.s_tfree, v7_sb.s_tfree * 512). + AppendLine(); + + sb.AppendFormat("{0} free blocks on list ({1} bytes)", v7_sb.s_nfree, v7_sb.s_nfree * 512).AppendLine(); + sb.AppendFormat("First data zone: {0}", v7_sb.s_isize).AppendLine(); + sb.AppendFormat("{0} free inodes on volume", v7_sb.s_tinode).AppendLine(); + sb.AppendFormat("{0} free inodes on list", v7_sb.s_ninode).AppendLine(); + + if(v7_sb.s_flock > 0) + sb.AppendLine("Free block list is locked"); + + if(v7_sb.s_ilock > 0) + sb.AppendLine("inode cache is locked"); + + if(v7_sb.s_fmod > 0) + sb.AppendLine("Superblock is being modified"); + + if(v7_sb.s_ronly > 0) + sb.AppendLine("Volume is mounted read-only"); + + sb.AppendFormat("Superblock last updated on {0}", DateHandlers.UnixUnsignedToDateTime(v7_sb.s_time)). + AppendLine(); + + if(v7_sb.s_time != 0) + { + XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(v7_sb.s_time); + XmlFsType.ModificationDateSpecified = true; + } + + sb.AppendFormat("Volume name: {0}", v7_sb.s_fname).AppendLine(); + XmlFsType.VolumeName = v7_sb.s_fname; + sb.AppendFormat("Pack name: {0}", v7_sb.s_fpack).AppendLine(); } - struct UNIX7thEditionSuperBlock - { - /// 0x000, index of first data zone - public ushort s_isize; - /// 0x002, total number of zones of this volume - public uint s_fsize; + information = sb.ToString(); + } - // the start of the free block list: - /// 0x006, blocks in s_free, <=100 - public ushort s_nfree; - /// 0x008, 50 entries, first free block list chunk - public uint[] s_free; + // Old XENIX use different offsets + struct XenixSuperBlock + { + /// 0x000, index of first data zone + public ushort s_isize; + /// 0x002, total number of zones of this volume + public uint s_fsize; - // the cache of free inodes: - /// 0x0D0, number of inodes in s_inode, <= 100 - public ushort s_ninode; - /// 0x0D2, 100 entries, some free inodes - public ushort[] s_inode; - /// 0x19A, free block list manipulation lock - public byte s_flock; - /// 0x19B, inode cache manipulation lock - public byte s_ilock; - /// 0x19C, superblock modification flag - public byte s_fmod; - /// 0x19D, read-only mounted flag - public byte s_ronly; - /// 0x19E, time of last superblock update - public uint s_time; - /// 0x1A2, total number of free zones - public uint s_tfree; - /// 0x1A6, total number of free inodes - public ushort s_tinode; - /// 0x1A8, interleave factor - public ushort s_int_m; - /// 0x1AA, interleave factor - public ushort s_int_n; - /// 0x1AC, 6 bytes, volume name - public string s_fname; - /// 0x1B2, 6 bytes, pack name - public string s_fpack; - } + // the start of the free block list: + /// 0x006, blocks in s_free, <=100 + public ushort s_nfree; + /// 0x008, 100 entries, 50 entries for Xenix 3, first free block list chunk + public uint[] s_free; - struct CoherentSuperBlock - { - /// 0x000, index of first data zone - public ushort s_isize; - /// 0x002, total number of zones of this volume - public uint s_fsize; + // the cache of free inodes: + /// 0x198 (0xD0), number of inodes in s_inode, <= 100 + public ushort s_ninode; + /// 0x19A (0xD2), 100 entries, some free inodes + public ushort[] s_inode; + /// 0x262 (0x19A), free block list manipulation lock + public byte s_flock; + /// 0x263 (0x19B), inode cache manipulation lock + public byte s_ilock; + /// 0x264 (0x19C), superblock modification flag + public byte s_fmod; + /// 0x265 (0x19D), read-only mounted flag + public byte s_ronly; + /// 0x266 (0x19E), time of last superblock update + public int s_time; + /// 0x26A (0x1A2), total number of free zones + public uint s_tfree; + /// 0x26E (0x1A6), total number of free inodes + public ushort s_tinode; + /// 0x270 (0x1A8), blocks per cylinder + public ushort s_cylblks; + /// 0x272 (0x1AA), blocks per gap + public ushort s_gapblks; + /// 0x274 (0x1AC), device information ?? + public ushort s_dinfo0; + /// 0x276 (0x1AE), device information ?? + public ushort s_dinfo1; + /// 0x278 (0x1B0), 6 bytes, volume name + public string s_fname; + /// 0x27E (0x1B6), 6 bytes, pack name + public string s_fpack; + /// 0x284 (0x1BC), 0x46 if volume is clean + public byte s_clean; + /// 0x285 (0x1BD), 371 bytes, 51 bytes for Xenix 3 + public byte[] s_fill; + /// 0x3F8 (0x1F0), magic + public uint s_magic; + /// 0x3FC (0x1F4), filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk, 3 = 2048 bytes/blk) + public uint s_type; + } - // the start of the free block list: - /// 0x006, blocks in s_free, <=100 - public ushort s_nfree; - /// 0x008, 64 entries, first free block list chunk - public uint[] s_free; + struct SystemVRelease4SuperBlock + { + /// 0x000, index of first data zone + public ushort s_isize; + /// 0x002, padding + public ushort s_pad0; + /// 0x004, total number of zones of this volume + public uint s_fsize; - // the cache of free inodes: - /// 0x108, number of inodes in s_inode, <= 100 - public ushort s_ninode; - /// 0x10A, 100 entries, some free inodes - public ushort[] s_inode; - /// 0x1D2, free block list manipulation lock - public byte s_flock; - /// 0x1D3, inode cache manipulation lock - public byte s_ilock; - /// 0x1D4, superblock modification flag - public byte s_fmod; - /// 0x1D5, read-only mounted flag - public byte s_ronly; - /// 0x1D6, time of last superblock update - public uint s_time; - /// 0x1DE, total number of free zones - public uint s_tfree; - /// 0x1E2, total number of free inodes - public ushort s_tinode; - /// 0x1E4, interleave factor - public ushort s_int_m; - /// 0x1E6, interleave factor - public ushort s_int_n; - /// 0x1E8, 6 bytes, volume name - public string s_fname; - /// 0x1EE, 6 bytes, pack name - public string s_fpack; - /// 0x1F4, zero-filled - public uint s_unique; - } + // the start of the free block list: + /// 0x008, blocks in s_free, <=100 + public ushort s_nfree; + /// 0x00A, padding + public ushort s_pad1; + /// 0x00C, 50 entries, first free block list chunk + public uint[] s_free; + + // the cache of free inodes: + /// 0x0D4, number of inodes in s_inode, <= 100 + public ushort s_ninode; + /// 0x0D6, padding + public ushort s_pad2; + /// 0x0D8, 100 entries, some free inodes + public ushort[] s_inode; + /// 0x1A0, free block list manipulation lock + public byte s_flock; + /// 0x1A1, inode cache manipulation lock + public byte s_ilock; + /// 0x1A2, superblock modification flag + public byte s_fmod; + /// 0x1A3, read-only mounted flag + public byte s_ronly; + /// 0x1A4, time of last superblock update + public uint s_time; + /// 0x1A8, blocks per cylinder + public ushort s_cylblks; + /// 0x1AA, blocks per gap + public ushort s_gapblks; + /// 0x1AC, device information ?? + public ushort s_dinfo0; + /// 0x1AE, device information ?? + public ushort s_dinfo1; + /// 0x1B0, total number of free zones + public uint s_tfree; + /// 0x1B4, total number of free inodes + public ushort s_tinode; + /// 0x1B6, padding + public ushort s_pad3; + /// 0x1B8, 6 bytes, volume name + public string s_fname; + /// 0x1BE, 6 bytes, pack name + public string s_fpack; + /// 0x1C4, 48 bytes + public byte[] s_fill; + /// 0x1F4, if s_state == (0x7C269D38 - s_time) then filesystem is clean + public uint s_state; + /// 0x1F8, magic + public uint s_magic; + /// 0x1FC, filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk) + public uint s_type; + } + + struct SystemVRelease2SuperBlock + { + /// 0x000, index of first data zone + public ushort s_isize; + /// 0x002, total number of zones of this volume + public uint s_fsize; + + // the start of the free block list: + /// 0x006, blocks in s_free, <=100 + public ushort s_nfree; + /// 0x008, 50 entries, first free block list chunk + public uint[] s_free; + + // the cache of free inodes: + /// 0x0D0, number of inodes in s_inode, <= 100 + public ushort s_ninode; + /// 0x0D2, 100 entries, some free inodes + public ushort[] s_inode; + /// 0x19A, free block list manipulation lock + public byte s_flock; + /// 0x19B, inode cache manipulation lock + public byte s_ilock; + /// 0x19C, superblock modification flag + public byte s_fmod; + /// 0x19D, read-only mounted flag + public byte s_ronly; + /// 0x19E, time of last superblock update + public uint s_time; + /// 0x1A2, blocks per cylinder + public ushort s_cylblks; + /// 0x1A4, blocks per gap + public ushort s_gapblks; + /// 0x1A6, device information ?? + public ushort s_dinfo0; + /// 0x1A8, device information ?? + public ushort s_dinfo1; + /// 0x1AA, total number of free zones + public uint s_tfree; + /// 0x1AE, total number of free inodes + public ushort s_tinode; + /// 0x1B0, 6 bytes, volume name + public string s_fname; + /// 0x1B6, 6 bytes, pack name + public string s_fpack; + /// 0x1BC, 56 bytes + public byte[] s_fill; + /// 0x1F4, if s_state == (0x7C269D38 - s_time) then filesystem is clean + public uint s_state; + /// 0x1F8, magic + public uint s_magic; + /// 0x1FC, filesystem type (1 = 512 bytes/blk, 2 = 1024 bytes/blk) + public uint s_type; + } + + struct UNIX7thEditionSuperBlock + { + /// 0x000, index of first data zone + public ushort s_isize; + /// 0x002, total number of zones of this volume + public uint s_fsize; + + // the start of the free block list: + /// 0x006, blocks in s_free, <=100 + public ushort s_nfree; + /// 0x008, 50 entries, first free block list chunk + public uint[] s_free; + + // the cache of free inodes: + /// 0x0D0, number of inodes in s_inode, <= 100 + public ushort s_ninode; + /// 0x0D2, 100 entries, some free inodes + public ushort[] s_inode; + /// 0x19A, free block list manipulation lock + public byte s_flock; + /// 0x19B, inode cache manipulation lock + public byte s_ilock; + /// 0x19C, superblock modification flag + public byte s_fmod; + /// 0x19D, read-only mounted flag + public byte s_ronly; + /// 0x19E, time of last superblock update + public uint s_time; + /// 0x1A2, total number of free zones + public uint s_tfree; + /// 0x1A6, total number of free inodes + public ushort s_tinode; + /// 0x1A8, interleave factor + public ushort s_int_m; + /// 0x1AA, interleave factor + public ushort s_int_n; + /// 0x1AC, 6 bytes, volume name + public string s_fname; + /// 0x1B2, 6 bytes, pack name + public string s_fpack; + } + + struct CoherentSuperBlock + { + /// 0x000, index of first data zone + public ushort s_isize; + /// 0x002, total number of zones of this volume + public uint s_fsize; + + // the start of the free block list: + /// 0x006, blocks in s_free, <=100 + public ushort s_nfree; + /// 0x008, 64 entries, first free block list chunk + public uint[] s_free; + + // the cache of free inodes: + /// 0x108, number of inodes in s_inode, <= 100 + public ushort s_ninode; + /// 0x10A, 100 entries, some free inodes + public ushort[] s_inode; + /// 0x1D2, free block list manipulation lock + public byte s_flock; + /// 0x1D3, inode cache manipulation lock + public byte s_ilock; + /// 0x1D4, superblock modification flag + public byte s_fmod; + /// 0x1D5, read-only mounted flag + public byte s_ronly; + /// 0x1D6, time of last superblock update + public uint s_time; + /// 0x1DE, total number of free zones + public uint s_tfree; + /// 0x1E2, total number of free inodes + public ushort s_tinode; + /// 0x1E4, interleave factor + public ushort s_int_m; + /// 0x1E6, interleave factor + public ushort s_int_n; + /// 0x1E8, 6 bytes, volume name + public string s_fname; + /// 0x1EE, 6 bytes, pack name + public string s_fpack; + /// 0x1F4, zero-filled + public uint s_unique; } } \ No newline at end of file diff --git a/Aaru.Filesystems/UCSDPascal/Consts.cs b/Aaru.Filesystems/UCSDPascal/Consts.cs index 5db49ca17..c497573e2 100644 --- a/Aaru.Filesystems/UCSDPascal/Consts.cs +++ b/Aaru.Filesystems/UCSDPascal/Consts.cs @@ -32,32 +32,31 @@ using System.Diagnostics.CodeAnalysis; -namespace Aaru.Filesystems.UCSDPascal +namespace Aaru.Filesystems.UCSDPascal; + +// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed partial class PascalPlugin { - // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed partial class PascalPlugin + enum PascalFileKind : short { - enum PascalFileKind : short - { - /// Disk volume entry - Volume = 0, - /// File containing bad blocks - Bad, - /// Code file, machine executable - Code, - /// Text file, human readable - Text, - /// Information file for debugger - Info, - /// Data file - Data, - /// Graphics vectors - Graf, - /// Graphics screen image - Foto, - /// Security, not used - Secure - } + /// Disk volume entry + Volume = 0, + /// File containing bad blocks + Bad, + /// Code file, machine executable + Code, + /// Text file, human readable + Text, + /// Information file for debugger + Info, + /// Data file + Data, + /// Graphics vectors + Graf, + /// Graphics screen image + Foto, + /// Security, not used + Secure } } \ No newline at end of file diff --git a/Aaru.Filesystems/UCSDPascal/Dir.cs b/Aaru.Filesystems/UCSDPascal/Dir.cs index c3a43fb09..7ab468411 100644 --- a/Aaru.Filesystems/UCSDPascal/Dir.cs +++ b/Aaru.Filesystems/UCSDPascal/Dir.cs @@ -36,34 +36,33 @@ using System.Linq; using Aaru.CommonTypes.Enums; using Aaru.Helpers; -namespace Aaru.Filesystems.UCSDPascal +namespace Aaru.Filesystems.UCSDPascal; + +// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure +public sealed partial class PascalPlugin { - // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure - public sealed partial class PascalPlugin + /// + public ErrorNumber ReadDir(string path, out List contents) { - /// - public ErrorNumber ReadDir(string path, out List contents) + contents = null; + + if(!_mounted) + return ErrorNumber.AccessDenied; + + if(!string.IsNullOrEmpty(path) && + string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) + return ErrorNumber.NotSupported; + + contents = _fileEntries.Select(ent => StringHandlers.PascalToString(ent.Filename, Encoding)).ToList(); + + if(_debug) { - contents = null; - - if(!_mounted) - return ErrorNumber.AccessDenied; - - if(!string.IsNullOrEmpty(path) && - string.Compare(path, "/", StringComparison.OrdinalIgnoreCase) != 0) - return ErrorNumber.NotSupported; - - contents = _fileEntries.Select(ent => StringHandlers.PascalToString(ent.Filename, Encoding)).ToList(); - - if(_debug) - { - contents.Add("$"); - contents.Add("$Boot"); - } - - contents.Sort(); - - return ErrorNumber.NoError; + contents.Add("$"); + contents.Add("$Boot"); } + + contents.Sort(); + + return ErrorNumber.NoError; } } \ No newline at end of file diff --git a/Aaru.Filesystems/UCSDPascal/File.cs b/Aaru.Filesystems/UCSDPascal/File.cs index 4fe01b3d9..fc85ea599 100644 --- a/Aaru.Filesystems/UCSDPascal/File.cs +++ b/Aaru.Filesystems/UCSDPascal/File.cs @@ -36,172 +36,171 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; using Aaru.Helpers; -namespace Aaru.Filesystems.UCSDPascal +namespace Aaru.Filesystems.UCSDPascal; + +// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure +public sealed partial class PascalPlugin { - // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure - public sealed partial class PascalPlugin + /// + public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) { - /// - public ErrorNumber MapBlock(string path, long fileBlock, out long deviceBlock) + deviceBlock = 0; + + return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotImplemented; + } + + /// + public ErrorNumber GetAttributes(string path, out FileAttributes attributes) + { + attributes = new FileAttributes(); + + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - deviceBlock = 0; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - return !_mounted ? ErrorNumber.AccessDenied : ErrorNumber.NotImplemented; - } + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - /// - public ErrorNumber GetAttributes(string path, out FileAttributes attributes) - { - attributes = new FileAttributes(); - - if(!_mounted) - return ErrorNumber.AccessDenied; - - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - ErrorNumber error = GetFileEntry(path, out _); - - if(error != ErrorNumber.NoError) - return error; - - attributes = FileAttributes.File; + ErrorNumber error = GetFileEntry(path, out _); + if(error != ErrorNumber.NoError) return error; - } - /// - public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + attributes = FileAttributes.File; + + return error; + } + + /// + public ErrorNumber Read(string path, long offset, long size, ref byte[] buf) + { + if(!_mounted) + return ErrorNumber.AccessDenied; + + string[] pathElements = path.Split(new[] { - if(!_mounted) - return ErrorNumber.AccessDenied; + '/' + }, StringSplitOptions.RemoveEmptyEntries); - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; + byte[] file; - byte[] file; - - if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0)) - file = string.Compare(path, "$", StringComparison.InvariantCulture) == 0 ? _catalogBlocks : _bootBlocks; - else - { - ErrorNumber error = GetFileEntry(path, out PascalFileEntry entry); - - if(error != ErrorNumber.NoError) - return error; - - error = _device.ReadSectors((ulong)entry.FirstBlock * _multiplier, - (uint)(entry.LastBlock - entry.FirstBlock) * _multiplier, out byte[] tmp); - - if(error != ErrorNumber.NoError) - return error; - - file = new byte[((entry.LastBlock - entry.FirstBlock - 1) * _device.Info.SectorSize * _multiplier) + - entry.LastBytes]; - - Array.Copy(tmp, 0, file, 0, file.Length); - } - - if(offset >= file.Length) - return ErrorNumber.EINVAL; - - if(size + offset >= file.Length) - size = file.Length - offset; - - buf = new byte[size]; - - Array.Copy(file, offset, buf, 0, size); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Stat(string path, out FileEntryInfo stat) + if(_debug && (string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0)) + file = string.Compare(path, "$", StringComparison.InvariantCulture) == 0 ? _catalogBlocks : _bootBlocks; + else { - stat = null; - - string[] pathElements = path.Split(new[] - { - '/' - }, StringSplitOptions.RemoveEmptyEntries); - - if(pathElements.Length != 1) - return ErrorNumber.NotSupported; - - if(_debug) - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || - string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0) - { - stat = new FileEntryInfo - { - Attributes = FileAttributes.System, - BlockSize = _device.Info.SectorSize * _multiplier, - Links = 1 - }; - - if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) - { - stat.Blocks = (_catalogBlocks.Length / stat.BlockSize) + - (_catalogBlocks.Length % stat.BlockSize); - - stat.Length = _catalogBlocks.Length; - } - else - { - stat.Blocks = (_bootBlocks.Length / stat.BlockSize) + (_catalogBlocks.Length % stat.BlockSize); - stat.Length = _bootBlocks.Length; - } - - return ErrorNumber.NoError; - } - ErrorNumber error = GetFileEntry(path, out PascalFileEntry entry); if(error != ErrorNumber.NoError) return error; - stat = new FileEntryInfo - { - Attributes = FileAttributes.File, - Blocks = entry.LastBlock - entry.FirstBlock, - BlockSize = _device.Info.SectorSize * _multiplier, - LastWriteTimeUtc = DateHandlers.UcsdPascalToDateTime(entry.ModificationTime), - Length = ((entry.LastBlock - entry.FirstBlock) * _device.Info.SectorSize * _multiplier) + - entry.LastBytes, - Links = 1 - }; + error = _device.ReadSectors((ulong)entry.FirstBlock * _multiplier, + (uint)(entry.LastBlock - entry.FirstBlock) * _multiplier, out byte[] tmp); - return ErrorNumber.NoError; + if(error != ErrorNumber.NoError) + return error; + + file = new byte[((entry.LastBlock - entry.FirstBlock - 1) * _device.Info.SectorSize * _multiplier) + + entry.LastBytes]; + + Array.Copy(tmp, 0, file, 0, file.Length); } - ErrorNumber GetFileEntry(string path, out PascalFileEntry entry) - { - entry = new PascalFileEntry(); + if(offset >= file.Length) + return ErrorNumber.EINVAL; - foreach(PascalFileEntry ent in _fileEntries.Where(ent => - string.Compare(path, - StringHandlers.PascalToString(ent.Filename, - Encoding), - StringComparison. - InvariantCultureIgnoreCase) == 0)) + if(size + offset >= file.Length) + size = file.Length - offset; + + buf = new byte[size]; + + Array.Copy(file, offset, buf, 0, size); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Stat(string path, out FileEntryInfo stat) + { + stat = null; + + string[] pathElements = path.Split(new[] + { + '/' + }, StringSplitOptions.RemoveEmptyEntries); + + if(pathElements.Length != 1) + return ErrorNumber.NotSupported; + + if(_debug) + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0 || + string.Compare(path, "$Boot", StringComparison.InvariantCulture) == 0) { - entry = ent; + stat = new FileEntryInfo + { + Attributes = FileAttributes.System, + BlockSize = _device.Info.SectorSize * _multiplier, + Links = 1 + }; + + if(string.Compare(path, "$", StringComparison.InvariantCulture) == 0) + { + stat.Blocks = (_catalogBlocks.Length / stat.BlockSize) + + (_catalogBlocks.Length % stat.BlockSize); + + stat.Length = _catalogBlocks.Length; + } + else + { + stat.Blocks = (_bootBlocks.Length / stat.BlockSize) + (_catalogBlocks.Length % stat.BlockSize); + stat.Length = _bootBlocks.Length; + } return ErrorNumber.NoError; } - return ErrorNumber.NoSuchFile; + ErrorNumber error = GetFileEntry(path, out PascalFileEntry entry); + + if(error != ErrorNumber.NoError) + return error; + + stat = new FileEntryInfo + { + Attributes = FileAttributes.File, + Blocks = entry.LastBlock - entry.FirstBlock, + BlockSize = _device.Info.SectorSize * _multiplier, + LastWriteTimeUtc = DateHandlers.UcsdPascalToDateTime(entry.ModificationTime), + Length = ((entry.LastBlock - entry.FirstBlock) * _device.Info.SectorSize * _multiplier) + + entry.LastBytes, + Links = 1 + }; + + return ErrorNumber.NoError; + } + + ErrorNumber GetFileEntry(string path, out PascalFileEntry entry) + { + entry = new PascalFileEntry(); + + foreach(PascalFileEntry ent in _fileEntries.Where(ent => + string.Compare(path, + StringHandlers.PascalToString(ent.Filename, + Encoding), + StringComparison. + InvariantCultureIgnoreCase) == 0)) + { + entry = ent; + + return ErrorNumber.NoError; } + + return ErrorNumber.NoSuchFile; } } \ No newline at end of file diff --git a/Aaru.Filesystems/UCSDPascal/Info.cs b/Aaru.Filesystems/UCSDPascal/Info.cs index 5225fba33..6a871acd6 100644 --- a/Aaru.Filesystems/UCSDPascal/Info.cs +++ b/Aaru.Filesystems/UCSDPascal/Info.cs @@ -41,177 +41,176 @@ using Claunia.Encoding; using Schemas; using Encoding = System.Text.Encoding; -namespace Aaru.Filesystems.UCSDPascal +namespace Aaru.Filesystems.UCSDPascal; + +// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure +public sealed partial class PascalPlugin { - // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure - public sealed partial class PascalPlugin + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(partition.Length < 3) + return false; + + _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); + + // Blocks 0 and 1 are boot code + ErrorNumber errno = + imagePlugin.ReadSectors((_multiplier * 2) + partition.Start, _multiplier, out byte[] volBlock); + + if(errno != ErrorNumber.NoError) + return false; + + // On Apple II, it's little endian + // TODO: Fix + /*BigEndianBitConverter.IsLittleEndian = + multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian;*/ + + var volEntry = new PascalVolumeEntry { - if(partition.Length < 3) - return false; + FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00), + LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02), + EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04), + VolumeName = new byte[8], + Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E), + Files = BigEndianBitConverter.ToInt16(volBlock, 0x10), + Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12), + LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14), + Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16) + }; - _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); + Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); - // Blocks 0 and 1 are boot code - ErrorNumber errno = - imagePlugin.ReadSectors((_multiplier * 2) + partition.Start, _multiplier, out byte[] volBlock); + AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.firstBlock = {0}", volEntry.FirstBlock); + AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBlock = {0}", volEntry.LastBlock); + AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.entryType = {0}", volEntry.EntryType); + // AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.volumeName = {0}", volEntry.VolumeName); + AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.blocks = {0}", volEntry.Blocks); + AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.files = {0}", volEntry.Files); + AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.dummy = {0}", volEntry.Dummy); + AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBoot = {0}", volEntry.LastBoot); + AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.tail = {0}", volEntry.Tail); - if(errno != ErrorNumber.NoError) - return false; + // First block is always 0 (even is it's sector 2) + if(volEntry.FirstBlock != 0) + return false; - // On Apple II, it's little endian - // TODO: Fix - /*BigEndianBitConverter.IsLittleEndian = - multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian;*/ + // Last volume record block must be after first block, and before end of device + if(volEntry.LastBlock <= volEntry.FirstBlock || + (ulong)volEntry.LastBlock > (imagePlugin.Info.Sectors / _multiplier) - 2) + return false; - var volEntry = new PascalVolumeEntry - { - FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00), - LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02), - EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04), - VolumeName = new byte[8], - Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E), - Files = BigEndianBitConverter.ToInt16(volBlock, 0x10), - Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12), - LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14), - Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16) - }; + // Volume record entry type must be volume or secure + if(volEntry.EntryType != PascalFileKind.Volume && + volEntry.EntryType != PascalFileKind.Secure) + return false; - Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); + // Volume name is max 7 characters + if(volEntry.VolumeName[0] > 7) + return false; - AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.firstBlock = {0}", volEntry.FirstBlock); - AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBlock = {0}", volEntry.LastBlock); - AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.entryType = {0}", volEntry.EntryType); -// AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.volumeName = {0}", volEntry.VolumeName); - AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.blocks = {0}", volEntry.Blocks); - AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.files = {0}", volEntry.Files); - AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.dummy = {0}", volEntry.Dummy); - AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.lastBoot = {0}", volEntry.LastBoot); - AaruConsole.DebugWriteLine("UCSD Pascal Plugin", "volEntry.tail = {0}", volEntry.Tail); + // Volume blocks is equal to volume sectors + if(volEntry.Blocks < 0 || + (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / _multiplier) + return false; - // First block is always 0 (even is it's sector 2) - if(volEntry.FirstBlock != 0) - return false; + // There can be not less than zero files + return volEntry.Files >= 0; + } - // Last volume record block must be after first block, and before end of device - if(volEntry.LastBlock <= volEntry.FirstBlock || - (ulong)volEntry.LastBlock > (imagePlugin.Info.Sectors / _multiplier) - 2) - return false; + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? new Apple2(); + var sbInformation = new StringBuilder(); + information = ""; + _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); - // Volume record entry type must be volume or secure - if(volEntry.EntryType != PascalFileKind.Volume && - volEntry.EntryType != PascalFileKind.Secure) - return false; + if(imagePlugin.Info.Sectors < 3) + return; - // Volume name is max 7 characters - if(volEntry.VolumeName[0] > 7) - return false; + // Blocks 0 and 1 are boot code + ErrorNumber errno = + imagePlugin.ReadSectors((_multiplier * 2) + partition.Start, _multiplier, out byte[] volBlock); - // Volume blocks is equal to volume sectors - if(volEntry.Blocks < 0 || - (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / _multiplier) - return false; + if(errno != ErrorNumber.NoError) + return; - // There can be not less than zero files - return volEntry.Files >= 0; - } + // On Apple //, it's little endian + // TODO: Fix + //BigEndianBitConverter.IsLittleEndian = + // multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + var volEntry = new PascalVolumeEntry { - Encoding = encoding ?? new Apple2(); - var sbInformation = new StringBuilder(); - information = ""; - _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); + FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00), + LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02), + EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04), + VolumeName = new byte[8], + Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E), + Files = BigEndianBitConverter.ToInt16(volBlock, 0x10), + Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12), + LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14), + Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16) + }; - if(imagePlugin.Info.Sectors < 3) - return; + Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); - // Blocks 0 and 1 are boot code - ErrorNumber errno = - imagePlugin.ReadSectors((_multiplier * 2) + partition.Start, _multiplier, out byte[] volBlock); + // First block is always 0 (even is it's sector 2) + if(volEntry.FirstBlock != 0) + return; - if(errno != ErrorNumber.NoError) - return; + // Last volume record block must be after first block, and before end of device + if(volEntry.LastBlock <= volEntry.FirstBlock || + (ulong)volEntry.LastBlock > (imagePlugin.Info.Sectors / _multiplier) - 2) + return; - // On Apple //, it's little endian - // TODO: Fix - //BigEndianBitConverter.IsLittleEndian = - // multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; + // Volume record entry type must be volume or secure + if(volEntry.EntryType != PascalFileKind.Volume && + volEntry.EntryType != PascalFileKind.Secure) + return; - var volEntry = new PascalVolumeEntry - { - FirstBlock = BigEndianBitConverter.ToInt16(volBlock, 0x00), - LastBlock = BigEndianBitConverter.ToInt16(volBlock, 0x02), - EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(volBlock, 0x04), - VolumeName = new byte[8], - Blocks = BigEndianBitConverter.ToInt16(volBlock, 0x0E), - Files = BigEndianBitConverter.ToInt16(volBlock, 0x10), - Dummy = BigEndianBitConverter.ToInt16(volBlock, 0x12), - LastBoot = BigEndianBitConverter.ToInt16(volBlock, 0x14), - Tail = BigEndianBitConverter.ToInt32(volBlock, 0x16) - }; + // Volume name is max 7 characters + if(volEntry.VolumeName[0] > 7) + return; - Array.Copy(volBlock, 0x06, volEntry.VolumeName, 0, 8); + // Volume blocks is equal to volume sectors + if(volEntry.Blocks < 0 || + (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / _multiplier) + return; - // First block is always 0 (even is it's sector 2) - if(volEntry.FirstBlock != 0) - return; + // There can be not less than zero files + if(volEntry.Files < 0) + return; - // Last volume record block must be after first block, and before end of device - if(volEntry.LastBlock <= volEntry.FirstBlock || - (ulong)volEntry.LastBlock > (imagePlugin.Info.Sectors / _multiplier) - 2) - return; + sbInformation.AppendFormat("Volume record spans from block {0} to block {1}", volEntry.FirstBlock, + volEntry.LastBlock).AppendLine(); - // Volume record entry type must be volume or secure - if(volEntry.EntryType != PascalFileKind.Volume && - volEntry.EntryType != PascalFileKind.Secure) - return; + sbInformation. + AppendFormat("Volume name: {0}", StringHandlers.PascalToString(volEntry.VolumeName, Encoding)). + AppendLine(); - // Volume name is max 7 characters - if(volEntry.VolumeName[0] > 7) - return; + sbInformation.AppendFormat("Volume has {0} blocks", volEntry.Blocks).AppendLine(); + sbInformation.AppendFormat("Volume has {0} files", volEntry.Files).AppendLine(); - // Volume blocks is equal to volume sectors - if(volEntry.Blocks < 0 || - (ulong)volEntry.Blocks != imagePlugin.Info.Sectors / _multiplier) - return; + sbInformation. + AppendFormat("Volume last booted at {0}", DateHandlers.UcsdPascalToDateTime(volEntry.LastBoot)). + AppendLine(); - // There can be not less than zero files - if(volEntry.Files < 0) - return; + information = sbInformation.ToString(); - sbInformation.AppendFormat("Volume record spans from block {0} to block {1}", volEntry.FirstBlock, - volEntry.LastBlock).AppendLine(); + imagePlugin.ReadSectors(partition.Start, _multiplier * 2, out byte[] boot); - sbInformation. - AppendFormat("Volume name: {0}", StringHandlers.PascalToString(volEntry.VolumeName, Encoding)). - AppendLine(); - - sbInformation.AppendFormat("Volume has {0} blocks", volEntry.Blocks).AppendLine(); - sbInformation.AppendFormat("Volume has {0} files", volEntry.Files).AppendLine(); - - sbInformation. - AppendFormat("Volume last booted at {0}", DateHandlers.UcsdPascalToDateTime(volEntry.LastBoot)). - AppendLine(); - - information = sbInformation.ToString(); - - imagePlugin.ReadSectors(partition.Start, _multiplier * 2, out byte[] boot); - - XmlFsType = new FileSystemType - { - Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(boot), - Clusters = (ulong)volEntry.Blocks, - ClusterSize = imagePlugin.Info.SectorSize, - Files = (ulong)volEntry.Files, - FilesSpecified = true, - Type = "UCSD Pascal", - VolumeName = StringHandlers.PascalToString(volEntry.VolumeName, Encoding) - }; - } + XmlFsType = new FileSystemType + { + Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(boot), + Clusters = (ulong)volEntry.Blocks, + ClusterSize = imagePlugin.Info.SectorSize, + Files = (ulong)volEntry.Files, + FilesSpecified = true, + Type = "UCSD Pascal", + VolumeName = StringHandlers.PascalToString(volEntry.VolumeName, Encoding) + }; } } \ No newline at end of file diff --git a/Aaru.Filesystems/UCSDPascal/Structs.cs b/Aaru.Filesystems/UCSDPascal/Structs.cs index 45747aff1..2aec998ee 100644 --- a/Aaru.Filesystems/UCSDPascal/Structs.cs +++ b/Aaru.Filesystems/UCSDPascal/Structs.cs @@ -32,48 +32,47 @@ using System.Diagnostics.CodeAnalysis; -namespace Aaru.Filesystems.UCSDPascal -{ - // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure - [SuppressMessage("ReSharper", "NotAccessedField.Local")] - public sealed partial class PascalPlugin - { - struct PascalVolumeEntry - { - /// 0x00, first block of volume entry - public short FirstBlock; - /// 0x02, last block of volume entry - public short LastBlock; - /// 0x04, entry type - public PascalFileKind EntryType; - /// 0x06, volume name - public byte[] VolumeName; - /// 0x0E, block in volume - public short Blocks; - /// 0x10, files in volume - public short Files; - /// 0x12, dummy - public short Dummy; - /// 0x14, last booted - public short LastBoot; - /// 0x16, tail to make record same size as - public int Tail; - } +namespace Aaru.Filesystems.UCSDPascal; - struct PascalFileEntry - { - /// 0x00, first block of file - public short FirstBlock; - /// 0x02, last block of file - public short LastBlock; - /// 0x04, entry type - public PascalFileKind EntryType; - /// 0x06, file name - public byte[] Filename; - /// 0x16, bytes used in last block - public short LastBytes; - /// 0x18, modification time - public short ModificationTime; - } +// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure +[SuppressMessage("ReSharper", "NotAccessedField.Local")] +public sealed partial class PascalPlugin +{ + struct PascalVolumeEntry + { + /// 0x00, first block of volume entry + public short FirstBlock; + /// 0x02, last block of volume entry + public short LastBlock; + /// 0x04, entry type + public PascalFileKind EntryType; + /// 0x06, volume name + public byte[] VolumeName; + /// 0x0E, block in volume + public short Blocks; + /// 0x10, files in volume + public short Files; + /// 0x12, dummy + public short Dummy; + /// 0x14, last booted + public short LastBoot; + /// 0x16, tail to make record same size as + public int Tail; + } + + struct PascalFileEntry + { + /// 0x00, first block of file + public short FirstBlock; + /// 0x02, last block of file + public short LastBlock; + /// 0x04, entry type + public PascalFileKind EntryType; + /// 0x06, file name + public byte[] Filename; + /// 0x16, bytes used in last block + public short LastBytes; + /// 0x18, modification time + public short ModificationTime; } } \ No newline at end of file diff --git a/Aaru.Filesystems/UCSDPascal/Super.cs b/Aaru.Filesystems/UCSDPascal/Super.cs index fcd85b4b5..ea23397d4 100644 --- a/Aaru.Filesystems/UCSDPascal/Super.cs +++ b/Aaru.Filesystems/UCSDPascal/Super.cs @@ -41,143 +41,142 @@ using Claunia.Encoding; using Schemas; using Encoding = System.Text.Encoding; -namespace Aaru.Filesystems.UCSDPascal +namespace Aaru.Filesystems.UCSDPascal; + +// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure +public sealed partial class PascalPlugin { - // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure - public sealed partial class PascalPlugin + /// + public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, + Dictionary options, string @namespace) { - /// - public ErrorNumber Mount(IMediaImage imagePlugin, Partition partition, Encoding encoding, - Dictionary options, string @namespace) + _device = imagePlugin; + Encoding = encoding ?? new Apple2(); + + options ??= GetDefaultOptions(); + + if(options.TryGetValue("debug", out string debugString)) + bool.TryParse(debugString, out _debug); + + if(_device.Info.Sectors < 3) + return ErrorNumber.InvalidArgument; + + _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); + + // Blocks 0 and 1 are boot code + ErrorNumber errno = _device.ReadSectors(_multiplier * 2, _multiplier, out _catalogBlocks); + + if(errno != ErrorNumber.NoError) + return errno; + + // On Apple //, it's little endian + // TODO: Fix + //BigEndianBitConverter.IsLittleEndian = + // multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; + + _mountedVolEntry.FirstBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x00); + _mountedVolEntry.LastBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x02); + _mountedVolEntry.EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(_catalogBlocks, 0x04); + _mountedVolEntry.VolumeName = new byte[8]; + Array.Copy(_catalogBlocks, 0x06, _mountedVolEntry.VolumeName, 0, 8); + _mountedVolEntry.Blocks = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x0E); + _mountedVolEntry.Files = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x10); + _mountedVolEntry.Dummy = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x12); + _mountedVolEntry.LastBoot = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x14); + _mountedVolEntry.Tail = BigEndianBitConverter.ToInt32(_catalogBlocks, 0x16); + + if(_mountedVolEntry.FirstBlock != 0 || + _mountedVolEntry.LastBlock <= _mountedVolEntry.FirstBlock || + (ulong)_mountedVolEntry.LastBlock > (_device.Info.Sectors / _multiplier) - 2 || + (_mountedVolEntry.EntryType != PascalFileKind.Volume && + _mountedVolEntry.EntryType != PascalFileKind.Secure) || + _mountedVolEntry.VolumeName[0] > 7 || + _mountedVolEntry.Blocks < 0 || + (ulong)_mountedVolEntry.Blocks != _device.Info.Sectors / _multiplier || + _mountedVolEntry.Files < 0) + return ErrorNumber.InvalidArgument; + + errno = _device.ReadSectors(_multiplier * 2, + (uint)(_mountedVolEntry.LastBlock - _mountedVolEntry.FirstBlock - 2) * + _multiplier, out _catalogBlocks); + + if(errno != ErrorNumber.NoError) + return errno; + + int offset = 26; + + _fileEntries = new List(); + + while(offset + 26 < _catalogBlocks.Length) { - _device = imagePlugin; - Encoding = encoding ?? new Apple2(); - - options ??= GetDefaultOptions(); - - if(options.TryGetValue("debug", out string debugString)) - bool.TryParse(debugString, out _debug); - - if(_device.Info.Sectors < 3) - return ErrorNumber.InvalidArgument; - - _multiplier = (uint)(imagePlugin.Info.SectorSize == 256 ? 2 : 1); - - // Blocks 0 and 1 are boot code - ErrorNumber errno = _device.ReadSectors(_multiplier * 2, _multiplier, out _catalogBlocks); - - if(errno != ErrorNumber.NoError) - return errno; - - // On Apple //, it's little endian - // TODO: Fix - //BigEndianBitConverter.IsLittleEndian = - // multiplier == 2 ? !BitConverter.IsLittleEndian : BitConverter.IsLittleEndian; - - _mountedVolEntry.FirstBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x00); - _mountedVolEntry.LastBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x02); - _mountedVolEntry.EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(_catalogBlocks, 0x04); - _mountedVolEntry.VolumeName = new byte[8]; - Array.Copy(_catalogBlocks, 0x06, _mountedVolEntry.VolumeName, 0, 8); - _mountedVolEntry.Blocks = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x0E); - _mountedVolEntry.Files = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x10); - _mountedVolEntry.Dummy = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x12); - _mountedVolEntry.LastBoot = BigEndianBitConverter.ToInt16(_catalogBlocks, 0x14); - _mountedVolEntry.Tail = BigEndianBitConverter.ToInt32(_catalogBlocks, 0x16); - - if(_mountedVolEntry.FirstBlock != 0 || - _mountedVolEntry.LastBlock <= _mountedVolEntry.FirstBlock || - (ulong)_mountedVolEntry.LastBlock > (_device.Info.Sectors / _multiplier) - 2 || - (_mountedVolEntry.EntryType != PascalFileKind.Volume && - _mountedVolEntry.EntryType != PascalFileKind.Secure) || - _mountedVolEntry.VolumeName[0] > 7 || - _mountedVolEntry.Blocks < 0 || - (ulong)_mountedVolEntry.Blocks != _device.Info.Sectors / _multiplier || - _mountedVolEntry.Files < 0) - return ErrorNumber.InvalidArgument; - - errno = _device.ReadSectors(_multiplier * 2, - (uint)(_mountedVolEntry.LastBlock - _mountedVolEntry.FirstBlock - 2) * - _multiplier, out _catalogBlocks); - - if(errno != ErrorNumber.NoError) - return errno; - - int offset = 26; - - _fileEntries = new List(); - - while(offset + 26 < _catalogBlocks.Length) + var entry = new PascalFileEntry { - var entry = new PascalFileEntry - { - Filename = new byte[16], - FirstBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x00), - LastBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x02), - EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x04), - LastBytes = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x16), - ModificationTime = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x18) - }; - - Array.Copy(_catalogBlocks, offset + 0x06, entry.Filename, 0, 16); - - if(entry.Filename[0] <= 15 && - entry.Filename[0] > 0) - _fileEntries.Add(entry); - - offset += 26; - } - - errno = _device.ReadSectors(0, 2 * _multiplier, out _bootBlocks); - - if(errno != ErrorNumber.NoError) - return errno; - - XmlFsType = new FileSystemType - { - Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(_bootBlocks), - Clusters = (ulong)_mountedVolEntry.Blocks, - ClusterSize = _device.Info.SectorSize, - Files = (ulong)_mountedVolEntry.Files, - FilesSpecified = true, - Type = "UCSD Pascal", - VolumeName = StringHandlers.PascalToString(_mountedVolEntry.VolumeName, Encoding) + Filename = new byte[16], + FirstBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x00), + LastBlock = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x02), + EntryType = (PascalFileKind)BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x04), + LastBytes = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x16), + ModificationTime = BigEndianBitConverter.ToInt16(_catalogBlocks, offset + 0x18) }; - _mounted = true; + Array.Copy(_catalogBlocks, offset + 0x06, entry.Filename, 0, 16); - return ErrorNumber.NoError; + if(entry.Filename[0] <= 15 && + entry.Filename[0] > 0) + _fileEntries.Add(entry); + + offset += 26; } - /// - public ErrorNumber Unmount() + errno = _device.ReadSectors(0, 2 * _multiplier, out _bootBlocks); + + if(errno != ErrorNumber.NoError) + return errno; + + XmlFsType = new FileSystemType { - _mounted = false; - _fileEntries = null; + Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(_bootBlocks), + Clusters = (ulong)_mountedVolEntry.Blocks, + ClusterSize = _device.Info.SectorSize, + Files = (ulong)_mountedVolEntry.Files, + FilesSpecified = true, + Type = "UCSD Pascal", + VolumeName = StringHandlers.PascalToString(_mountedVolEntry.VolumeName, Encoding) + }; - return ErrorNumber.NoError; - } + _mounted = true; - /// - public ErrorNumber StatFs(out FileSystemInfo stat) + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Unmount() + { + _mounted = false; + _fileEntries = null; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber StatFs(out FileSystemInfo stat) + { + stat = new FileSystemInfo { - stat = new FileSystemInfo - { - Blocks = (ulong)_mountedVolEntry.Blocks, - FilenameLength = 16, - Files = (ulong)_mountedVolEntry.Files, - FreeBlocks = 0, - PluginId = Id, - Type = "UCSD Pascal" - }; + Blocks = (ulong)_mountedVolEntry.Blocks, + FilenameLength = 16, + Files = (ulong)_mountedVolEntry.Files, + FreeBlocks = 0, + PluginId = Id, + Type = "UCSD Pascal" + }; - stat.FreeBlocks = - (ulong)(_mountedVolEntry.Blocks - (_mountedVolEntry.LastBlock - _mountedVolEntry.FirstBlock)); + stat.FreeBlocks = + (ulong)(_mountedVolEntry.Blocks - (_mountedVolEntry.LastBlock - _mountedVolEntry.FirstBlock)); - foreach(PascalFileEntry entry in _fileEntries) - stat.FreeBlocks -= (ulong)(entry.LastBlock - entry.FirstBlock); + foreach(PascalFileEntry entry in _fileEntries) + stat.FreeBlocks -= (ulong)(entry.LastBlock - entry.FirstBlock); - return ErrorNumber.NotImplemented; - } + return ErrorNumber.NotImplemented; } } \ No newline at end of file diff --git a/Aaru.Filesystems/UCSDPascal/UCSDPascal.cs b/Aaru.Filesystems/UCSDPascal/UCSDPascal.cs index 9af259db2..2527e2b35 100644 --- a/Aaru.Filesystems/UCSDPascal/UCSDPascal.cs +++ b/Aaru.Filesystems/UCSDPascal/UCSDPascal.cs @@ -37,66 +37,65 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Filesystems.UCSDPascal +namespace Aaru.Filesystems.UCSDPascal; + +// Information from Call-A.P.P.L.E. Pascal Disk Directory Structure +/// +/// Implements the U.C.S.D. Pascal filesystem +public sealed partial class PascalPlugin : IReadOnlyFilesystem { - // Information from Call-A.P.P.L.E. Pascal Disk Directory Structure + byte[] _bootBlocks; + byte[] _catalogBlocks; + bool _debug; + IMediaImage _device; + List _fileEntries; + bool _mounted; + PascalVolumeEntry _mountedVolEntry; + /// Apple II disks use 256 bytes / sector, but filesystem assumes it's 512 bytes / sector + uint _multiplier; + /// - /// Implements the U.C.S.D. Pascal filesystem - public sealed partial class PascalPlugin : IReadOnlyFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public string Name => "U.C.S.D. Pascal filesystem"; + /// + public Guid Id => new("B0AC2CB5-72AA-473A-9200-270B5A2C2D53"); + /// + public Encoding Encoding { get; private set; } + /// + public string Author => "Natalia Portillo"; + + /// + public ErrorNumber ListXAttr(string path, out List xattrs) { - byte[] _bootBlocks; - byte[] _catalogBlocks; - bool _debug; - IMediaImage _device; - List _fileEntries; - bool _mounted; - PascalVolumeEntry _mountedVolEntry; - /// Apple II disks use 256 bytes / sector, but filesystem assumes it's 512 bytes / sector - uint _multiplier; + xattrs = null; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Name => "U.C.S.D. Pascal filesystem"; - /// - public Guid Id => new("B0AC2CB5-72AA-473A-9200-270B5A2C2D53"); - /// - public Encoding Encoding { get; private set; } - /// - public string Author => "Natalia Portillo"; - - /// - public ErrorNumber ListXAttr(string path, out List xattrs) - { - xattrs = null; - - return ErrorNumber.NotSupported; - } - - /// - public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) => ErrorNumber.NotSupported; - - /// - public ErrorNumber ReadLink(string path, out string dest) - { - dest = null; - - return ErrorNumber.NotSupported; - } - - /// - public IEnumerable<(string name, Type type, string description)> SupportedOptions => - new (string name, Type type, string description)[] - {}; - - /// - public Dictionary Namespaces => null; - - static Dictionary GetDefaultOptions() => new() - { - { - "debug", false.ToString() - } - }; + return ErrorNumber.NotSupported; } + + /// + public ErrorNumber GetXattr(string path, string xattr, ref byte[] buf) => ErrorNumber.NotSupported; + + /// + public ErrorNumber ReadLink(string path, out string dest) + { + dest = null; + + return ErrorNumber.NotSupported; + } + + /// + public IEnumerable<(string name, Type type, string description)> SupportedOptions => + new (string name, Type type, string description)[] + {}; + + /// + public Dictionary Namespaces => null; + + static Dictionary GetDefaultOptions() => new() + { + { + "debug", false.ToString() + } + }; } \ No newline at end of file diff --git a/Aaru.Filesystems/UDF.cs b/Aaru.Filesystems/UDF.cs index 1cee8f5e2..39f8e47bc 100644 --- a/Aaru.Filesystems/UDF.cs +++ b/Aaru.Filesystems/UDF.cs @@ -43,539 +43,538 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// TODO: Detect bootable +/// +/// Implements detection of the Universal Disk Format filesystem +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class UDF : IFilesystem { - // TODO: Detect bootable - /// - /// Implements detection of the Universal Disk Format filesystem - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class UDF : IFilesystem + readonly byte[] _magic = { - readonly byte[] _magic = + 0x2A, 0x4F, 0x53, 0x54, 0x41, 0x20, 0x55, 0x44, 0x46, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x6C, 0x69, 0x61, 0x6E, + 0x74, 0x00, 0x00, 0x00, 0x00 + }; + + /// + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Universal Disk Format"; + /// + public Guid Id => new("83976FEC-A91B-464B-9293-56C719461BAB"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + // UDF needs at least that + if(partition.End - partition.Start < 256) + return false; + + // UDF needs at least that + if(imagePlugin.Info.SectorSize < 512) + return false; + + byte[] sector; + var anchor = new AnchorVolumeDescriptorPointer(); + + // All positions where anchor may reside, with the ratio between 512 and 2048bps + ulong[][] positions = { - 0x2A, 0x4F, 0x53, 0x54, 0x41, 0x20, 0x55, 0x44, 0x46, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x6C, 0x69, 0x61, 0x6E, - 0x74, 0x00, 0x00, 0x00, 0x00 + new ulong[] + { + 256, 1 + }, + new ulong[] + { + 512, 1 + }, + new ulong[] + { + partition.End - 256, 1 + }, + new ulong[] + { + partition.End, 1 + }, + new ulong[] + { + 1024, 4 + }, + new ulong[] + { + 2048, 4 + }, + new ulong[] + { + partition.End - 1024, 4 + }, + new ulong[] + { + partition.End - 4, 4 + } }; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Universal Disk Format"; - /// - public Guid Id => new("83976FEC-A91B-464B-9293-56C719461BAB"); - /// - public string Author => "Natalia Portillo"; + bool anchorFound = false; + uint ratio = 1; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + foreach(ulong[] position in positions.Where(position => position[0] + partition.Start + position[1] <= + partition.End && position[0] < partition.End)) { - // UDF needs at least that - if(partition.End - partition.Start < 256) - return false; - - // UDF needs at least that - if(imagePlugin.Info.SectorSize < 512) - return false; - - byte[] sector; - var anchor = new AnchorVolumeDescriptorPointer(); - - // All positions where anchor may reside, with the ratio between 512 and 2048bps - ulong[][] positions = - { - new ulong[] - { - 256, 1 - }, - new ulong[] - { - 512, 1 - }, - new ulong[] - { - partition.End - 256, 1 - }, - new ulong[] - { - partition.End, 1 - }, - new ulong[] - { - 1024, 4 - }, - new ulong[] - { - 2048, 4 - }, - new ulong[] - { - partition.End - 1024, 4 - }, - new ulong[] - { - partition.End - 4, 4 - } - }; - - bool anchorFound = false; - uint ratio = 1; - - foreach(ulong[] position in positions.Where(position => position[0] + partition.Start + position[1] <= - partition.End && position[0] < partition.End)) - { - ErrorNumber errno = imagePlugin.ReadSectors(position[0], (uint)position[1], out sector); - - if(errno != ErrorNumber.NoError) - continue; - - anchor = Marshal.ByteArrayToStructureLittleEndian(sector); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagIdentifier = {0}", anchor.tag.tagIdentifier); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorVersion = {0}", - anchor.tag.descriptorVersion); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagChecksum = 0x{0:X2}", anchor.tag.tagChecksum); - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.reserved = {0}", anchor.tag.reserved); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagSerialNumber = {0}", - anchor.tag.tagSerialNumber); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorCrc = 0x{0:X4}", - anchor.tag.descriptorCrc); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorCrcLength = {0}", - anchor.tag.descriptorCrcLength); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagLocation = {0}", anchor.tag.tagLocation); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.mainVolumeDescriptorSequenceExtent.length = {0}", - anchor.mainVolumeDescriptorSequenceExtent.length); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.mainVolumeDescriptorSequenceExtent.location = {0}", - anchor.mainVolumeDescriptorSequenceExtent.location); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.reserveVolumeDescriptorSequenceExtent.length = {0}", - anchor.reserveVolumeDescriptorSequenceExtent.length); - - AaruConsole.DebugWriteLine("UDF Plugin", "anchor.reserveVolumeDescriptorSequenceExtent.location = {0}", - anchor.reserveVolumeDescriptorSequenceExtent.location); - - if(anchor.tag.tagIdentifier != TagIdentifier.AnchorVolumeDescriptorPointer || - anchor.tag.tagLocation != position[0] / position[1] || - (anchor.mainVolumeDescriptorSequenceExtent.location * position[1]) + partition.Start >= - partition.End) - continue; - - anchorFound = true; - ratio = (uint)position[1]; - - break; - } - - if(!anchorFound) - return false; - - ulong count = 0; - - while(count < 256) - { - ErrorNumber errno = - imagePlugin. - ReadSectors(partition.Start + (anchor.mainVolumeDescriptorSequenceExtent.location * ratio) + (count * ratio), - ratio, out sector); - - if(errno != ErrorNumber.NoError) - { - count++; - - continue; - } - - var tagId = (TagIdentifier)BitConverter.ToUInt16(sector, 0); - uint location = BitConverter.ToUInt32(sector, 0x0C); - - if(location == (partition.Start / ratio) + anchor.mainVolumeDescriptorSequenceExtent.location + count) - { - if(tagId == TagIdentifier.TerminatingDescriptor) - break; - - if(tagId == TagIdentifier.LogicalVolumeDescriptor) - { - LogicalVolumeDescriptor lvd = - Marshal.ByteArrayToStructureLittleEndian(sector); - - return _magic.SequenceEqual(lvd.domainIdentifier.identifier); - } - } - else - break; - - count++; - } - - return false; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - information = ""; - ErrorNumber errno; - - // UDF is always UTF-8 - Encoding = Encoding.UTF8; - byte[] sector; - - var sbInformation = new StringBuilder(); - - sbInformation.AppendLine("Universal Disk Format"); - - var anchor = new AnchorVolumeDescriptorPointer(); - - // All positions where anchor may reside, with the ratio between 512 and 2048bps - ulong[][] positions = - { - new ulong[] - { - 256, 1 - }, - new ulong[] - { - 512, 1 - }, - new ulong[] - { - partition.End - 256, 1 - }, - new ulong[] - { - partition.End, 1 - }, - new ulong[] - { - 1024, 4 - }, - new ulong[] - { - 2048, 4 - }, - new ulong[] - { - partition.End - 1024, 4 - }, - new ulong[] - { - partition.End - 4, 4 - } - }; - - uint ratio = 1; - - foreach(ulong[] position in positions) - { - errno = imagePlugin.ReadSectors(position[0], (uint)position[1], out sector); - - if(errno != ErrorNumber.NoError) - continue; - - anchor = Marshal.ByteArrayToStructureLittleEndian(sector); - - if(anchor.tag.tagIdentifier != TagIdentifier.AnchorVolumeDescriptorPointer || - anchor.tag.tagLocation != position[0] / position[1] || - anchor.mainVolumeDescriptorSequenceExtent.location + partition.Start >= partition.End) - continue; - - ratio = (uint)position[1]; - - break; - } - - ulong count = 0; - - var pvd = new PrimaryVolumeDescriptor(); - var lvd = new LogicalVolumeDescriptor(); - LogicalVolumeIntegrityDescriptor lvid; - var lvidiu = new LogicalVolumeIntegrityDescriptorImplementationUse(); - - while(count < 256) - { - errno = - imagePlugin. - ReadSectors(partition.Start + (anchor.mainVolumeDescriptorSequenceExtent.location * ratio) + (count * ratio), - ratio, out sector); - - if(errno != ErrorNumber.NoError) - continue; - - var tagId = (TagIdentifier)BitConverter.ToUInt16(sector, 0); - uint location = BitConverter.ToUInt32(sector, 0x0C); - - if(location == (partition.Start / ratio) + anchor.mainVolumeDescriptorSequenceExtent.location + count) - { - if(tagId == TagIdentifier.TerminatingDescriptor) - break; - - switch(tagId) - { - case TagIdentifier.LogicalVolumeDescriptor: - lvd = Marshal.ByteArrayToStructureLittleEndian(sector); - - break; - case TagIdentifier.PrimaryVolumeDescriptor: - pvd = Marshal.ByteArrayToStructureLittleEndian(sector); - - break; - } - } - else - break; - - count++; - } - - errno = imagePlugin.ReadSectors(lvd.integritySequenceExtent.location * ratio, ratio, out sector); + ErrorNumber errno = imagePlugin.ReadSectors(position[0], (uint)position[1], out sector); if(errno != ErrorNumber.NoError) - return; + continue; - lvid = Marshal.ByteArrayToStructureLittleEndian(sector); + anchor = Marshal.ByteArrayToStructureLittleEndian(sector); - if(lvid.tag.tagIdentifier == TagIdentifier.LogicalVolumeIntegrityDescriptor && - lvid.tag.tagLocation == lvd.integritySequenceExtent.location) - lvidiu = - Marshal.ByteArrayToStructureLittleEndian(sector, - (int)((lvid.numberOfPartitions * 8) + 80), - System.Runtime.InteropServices.Marshal.SizeOf(lvidiu)); - else - lvid = new LogicalVolumeIntegrityDescriptor(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagIdentifier = {0}", anchor.tag.tagIdentifier); - sbInformation.AppendFormat("Volume is number {0} of {1}", pvd.volumeSequenceNumber, - pvd.maximumVolumeSequenceNumber).AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorVersion = {0}", + anchor.tag.descriptorVersion); - sbInformation.AppendFormat("Volume set identifier: {0}", - StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier)).AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagChecksum = 0x{0:X2}", anchor.tag.tagChecksum); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.reserved = {0}", anchor.tag.reserved); - sbInformation. - AppendFormat("Volume name: {0}", StringHandlers.DecompressUnicode(lvd.logicalVolumeIdentifier)). - AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagSerialNumber = {0}", + anchor.tag.tagSerialNumber); - sbInformation.AppendFormat("Volume uses {0} bytes per block", lvd.logicalBlockSize).AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorCrc = 0x{0:X4}", + anchor.tag.descriptorCrc); - sbInformation.AppendFormat("Volume was last written in {0}", EcmaToDateTime(lvid.recordingDateTime)). - AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.descriptorCrcLength = {0}", + anchor.tag.descriptorCrcLength); - sbInformation.AppendFormat("Volume contains {0} partitions", lvid.numberOfPartitions).AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.tag.tagLocation = {0}", anchor.tag.tagLocation); - sbInformation. - AppendFormat("Volume contains {0} files and {1} directories", lvidiu.files, lvidiu.directories). - AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.mainVolumeDescriptorSequenceExtent.length = {0}", + anchor.mainVolumeDescriptorSequenceExtent.length); - sbInformation.AppendFormat("Volume conforms to {0}", - Encoding.GetString(lvd.domainIdentifier.identifier).TrimEnd('\u0000')). - AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.mainVolumeDescriptorSequenceExtent.location = {0}", + anchor.mainVolumeDescriptorSequenceExtent.location); - sbInformation.AppendFormat("Volume was last written by: {0}", - Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000')). - AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.reserveVolumeDescriptorSequenceExtent.length = {0}", + anchor.reserveVolumeDescriptorSequenceExtent.length); - sbInformation.AppendFormat("Volume requires UDF version {0}.{1:X2} to be read", - Convert.ToInt32($"{(lvidiu.minimumReadUDF & 0xFF00) >> 8}", 10), - Convert.ToInt32($"{lvidiu.minimumReadUDF & 0xFF}", 10)).AppendLine(); + AaruConsole.DebugWriteLine("UDF Plugin", "anchor.reserveVolumeDescriptorSequenceExtent.location = {0}", + anchor.reserveVolumeDescriptorSequenceExtent.location); - sbInformation.AppendFormat("Volume requires UDF version {0}.{1:X2} to be written to", - Convert.ToInt32($"{(lvidiu.minimumWriteUDF & 0xFF00) >> 8}", 10), - Convert.ToInt32($"{lvidiu.minimumWriteUDF & 0xFF}", 10)).AppendLine(); + if(anchor.tag.tagIdentifier != TagIdentifier.AnchorVolumeDescriptorPointer || + anchor.tag.tagLocation != position[0] / position[1] || + (anchor.mainVolumeDescriptorSequenceExtent.location * position[1]) + partition.Start >= + partition.End) + continue; - sbInformation.AppendFormat("Volume cannot be written by any UDF version higher than {0}.{1:X2}", - Convert.ToInt32($"{(lvidiu.maximumWriteUDF & 0xFF00) >> 8}", 10), - Convert.ToInt32($"{lvidiu.maximumWriteUDF & 0xFF}", 10)).AppendLine(); + anchorFound = true; + ratio = (uint)position[1]; - XmlFsType = new FileSystemType + break; + } + + if(!anchorFound) + return false; + + ulong count = 0; + + while(count < 256) + { + ErrorNumber errno = + imagePlugin. + ReadSectors(partition.Start + (anchor.mainVolumeDescriptorSequenceExtent.location * ratio) + (count * ratio), + ratio, out sector); + + if(errno != ErrorNumber.NoError) { - Type = - $"UDF v{Convert.ToInt32($"{(lvidiu.maximumWriteUDF & 0xFF00) >> 8}", 10)}.{Convert.ToInt32($"{lvidiu.maximumWriteUDF & 0xFF}", 10):X2}", - ApplicationIdentifier = Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000'), - ClusterSize = lvd.logicalBlockSize, - ModificationDate = EcmaToDateTime(lvid.recordingDateTime), - ModificationDateSpecified = true, - Files = lvidiu.files, - FilesSpecified = true, - VolumeName = StringHandlers.DecompressUnicode(lvd.logicalVolumeIdentifier), - VolumeSetIdentifier = StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier), - VolumeSerial = StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier), - SystemIdentifier = Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000') - }; + count++; - XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / - XmlFsType.ClusterSize; + continue; + } - information = sbInformation.ToString(); + var tagId = (TagIdentifier)BitConverter.ToUInt16(sector, 0); + uint location = BitConverter.ToUInt32(sector, 0x0C); + + if(location == (partition.Start / ratio) + anchor.mainVolumeDescriptorSequenceExtent.location + count) + { + if(tagId == TagIdentifier.TerminatingDescriptor) + break; + + if(tagId == TagIdentifier.LogicalVolumeDescriptor) + { + LogicalVolumeDescriptor lvd = + Marshal.ByteArrayToStructureLittleEndian(sector); + + return _magic.SequenceEqual(lvd.domainIdentifier.identifier); + } + } + else + break; + + count++; } - static DateTime EcmaToDateTime(Timestamp timestamp) => DateHandlers.EcmaToDateTime(timestamp.typeAndZone, - timestamp.year, timestamp.month, timestamp.day, timestamp.hour, timestamp.minute, timestamp.second, - timestamp.centiseconds, timestamp.hundredsMicroseconds, timestamp.microseconds); + return false; + } - [Flags] - enum EntityFlags : byte + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + information = ""; + ErrorNumber errno; + + // UDF is always UTF-8 + Encoding = Encoding.UTF8; + byte[] sector; + + var sbInformation = new StringBuilder(); + + sbInformation.AppendLine("Universal Disk Format"); + + var anchor = new AnchorVolumeDescriptorPointer(); + + // All positions where anchor may reside, with the ratio between 512 and 2048bps + ulong[][] positions = { - Dirty = 0x01, Protected = 0x02 - } + new ulong[] + { + 256, 1 + }, + new ulong[] + { + 512, 1 + }, + new ulong[] + { + partition.End - 256, 1 + }, + new ulong[] + { + partition.End, 1 + }, + new ulong[] + { + 1024, 4 + }, + new ulong[] + { + 2048, 4 + }, + new ulong[] + { + partition.End - 1024, 4 + }, + new ulong[] + { + partition.End - 4, 4 + } + }; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct EntityIdentifier + uint ratio = 1; + + foreach(ulong[] position in positions) { - /// Entity flags - public readonly EntityFlags flags; - /// Structure identifier - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 23)] - public readonly byte[] identifier; - /// Structure data - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] identifierSuffix; + errno = imagePlugin.ReadSectors(position[0], (uint)position[1], out sector); + + if(errno != ErrorNumber.NoError) + continue; + + anchor = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(anchor.tag.tagIdentifier != TagIdentifier.AnchorVolumeDescriptorPointer || + anchor.tag.tagLocation != position[0] / position[1] || + anchor.mainVolumeDescriptorSequenceExtent.location + partition.Start >= partition.End) + continue; + + ratio = (uint)position[1]; + + break; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Timestamp + ulong count = 0; + + var pvd = new PrimaryVolumeDescriptor(); + var lvd = new LogicalVolumeDescriptor(); + LogicalVolumeIntegrityDescriptor lvid; + var lvidiu = new LogicalVolumeIntegrityDescriptorImplementationUse(); + + while(count < 256) { - public readonly ushort typeAndZone; - public readonly short year; - public readonly byte month; - public readonly byte day; - public readonly byte hour; - public readonly byte minute; - public readonly byte second; - public readonly byte centiseconds; - public readonly byte hundredsMicroseconds; - public readonly byte microseconds; + errno = + imagePlugin. + ReadSectors(partition.Start + (anchor.mainVolumeDescriptorSequenceExtent.location * ratio) + (count * ratio), + ratio, out sector); + + if(errno != ErrorNumber.NoError) + continue; + + var tagId = (TagIdentifier)BitConverter.ToUInt16(sector, 0); + uint location = BitConverter.ToUInt32(sector, 0x0C); + + if(location == (partition.Start / ratio) + anchor.mainVolumeDescriptorSequenceExtent.location + count) + { + if(tagId == TagIdentifier.TerminatingDescriptor) + break; + + switch(tagId) + { + case TagIdentifier.LogicalVolumeDescriptor: + lvd = Marshal.ByteArrayToStructureLittleEndian(sector); + + break; + case TagIdentifier.PrimaryVolumeDescriptor: + pvd = Marshal.ByteArrayToStructureLittleEndian(sector); + + break; + } + } + else + break; + + count++; } - enum TagIdentifier : ushort + errno = imagePlugin.ReadSectors(lvd.integritySequenceExtent.location * ratio, ratio, out sector); + + if(errno != ErrorNumber.NoError) + return; + + lvid = Marshal.ByteArrayToStructureLittleEndian(sector); + + if(lvid.tag.tagIdentifier == TagIdentifier.LogicalVolumeIntegrityDescriptor && + lvid.tag.tagLocation == lvd.integritySequenceExtent.location) + lvidiu = + Marshal.ByteArrayToStructureLittleEndian(sector, + (int)((lvid.numberOfPartitions * 8) + 80), + System.Runtime.InteropServices.Marshal.SizeOf(lvidiu)); + else + lvid = new LogicalVolumeIntegrityDescriptor(); + + sbInformation.AppendFormat("Volume is number {0} of {1}", pvd.volumeSequenceNumber, + pvd.maximumVolumeSequenceNumber).AppendLine(); + + sbInformation.AppendFormat("Volume set identifier: {0}", + StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier)).AppendLine(); + + sbInformation. + AppendFormat("Volume name: {0}", StringHandlers.DecompressUnicode(lvd.logicalVolumeIdentifier)). + AppendLine(); + + sbInformation.AppendFormat("Volume uses {0} bytes per block", lvd.logicalBlockSize).AppendLine(); + + sbInformation.AppendFormat("Volume was last written in {0}", EcmaToDateTime(lvid.recordingDateTime)). + AppendLine(); + + sbInformation.AppendFormat("Volume contains {0} partitions", lvid.numberOfPartitions).AppendLine(); + + sbInformation. + AppendFormat("Volume contains {0} files and {1} directories", lvidiu.files, lvidiu.directories). + AppendLine(); + + sbInformation.AppendFormat("Volume conforms to {0}", + Encoding.GetString(lvd.domainIdentifier.identifier).TrimEnd('\u0000')). + AppendLine(); + + sbInformation.AppendFormat("Volume was last written by: {0}", + Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000')). + AppendLine(); + + sbInformation.AppendFormat("Volume requires UDF version {0}.{1:X2} to be read", + Convert.ToInt32($"{(lvidiu.minimumReadUDF & 0xFF00) >> 8}", 10), + Convert.ToInt32($"{lvidiu.minimumReadUDF & 0xFF}", 10)).AppendLine(); + + sbInformation.AppendFormat("Volume requires UDF version {0}.{1:X2} to be written to", + Convert.ToInt32($"{(lvidiu.minimumWriteUDF & 0xFF00) >> 8}", 10), + Convert.ToInt32($"{lvidiu.minimumWriteUDF & 0xFF}", 10)).AppendLine(); + + sbInformation.AppendFormat("Volume cannot be written by any UDF version higher than {0}.{1:X2}", + Convert.ToInt32($"{(lvidiu.maximumWriteUDF & 0xFF00) >> 8}", 10), + Convert.ToInt32($"{lvidiu.maximumWriteUDF & 0xFF}", 10)).AppendLine(); + + XmlFsType = new FileSystemType { - PrimaryVolumeDescriptor = 1, AnchorVolumeDescriptorPointer = 2, VolumeDescriptorPointer = 3, - ImplementationUseVolumeDescriptor = 4, PartitionDescriptor = 5, LogicalVolumeDescriptor = 6, - UnallocatedSpaceDescriptor = 7, TerminatingDescriptor = 8, LogicalVolumeIntegrityDescriptor = 9 - } + Type = + $"UDF v{Convert.ToInt32($"{(lvidiu.maximumWriteUDF & 0xFF00) >> 8}", 10)}.{Convert.ToInt32($"{lvidiu.maximumWriteUDF & 0xFF}", 10):X2}", + ApplicationIdentifier = Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000'), + ClusterSize = lvd.logicalBlockSize, + ModificationDate = EcmaToDateTime(lvid.recordingDateTime), + ModificationDateSpecified = true, + Files = lvidiu.files, + FilesSpecified = true, + VolumeName = StringHandlers.DecompressUnicode(lvd.logicalVolumeIdentifier), + VolumeSetIdentifier = StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier), + VolumeSerial = StringHandlers.DecompressUnicode(pvd.volumeSetIdentifier), + SystemIdentifier = Encoding.GetString(pvd.implementationIdentifier.identifier).TrimEnd('\u0000') + }; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DescriptorTag - { - public readonly TagIdentifier tagIdentifier; - public readonly ushort descriptorVersion; - public readonly byte tagChecksum; - public readonly byte reserved; - public readonly ushort tagSerialNumber; - public readonly ushort descriptorCrc; - public readonly ushort descriptorCrcLength; - public readonly uint tagLocation; - } + XmlFsType.Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / + XmlFsType.ClusterSize; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ExtentDescriptor - { - public readonly uint length; - public readonly uint location; - } + information = sbInformation.ToString(); + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct CharacterSpecification - { - public readonly byte type; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] - public readonly byte[] information; - } + static DateTime EcmaToDateTime(Timestamp timestamp) => DateHandlers.EcmaToDateTime(timestamp.typeAndZone, + timestamp.year, timestamp.month, timestamp.day, timestamp.hour, timestamp.minute, timestamp.second, + timestamp.centiseconds, timestamp.hundredsMicroseconds, timestamp.microseconds); - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct AnchorVolumeDescriptorPointer - { - public readonly DescriptorTag tag; - public readonly ExtentDescriptor mainVolumeDescriptorSequenceExtent; - public readonly ExtentDescriptor reserveVolumeDescriptorSequenceExtent; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 480)] - public readonly byte[] reserved; - } + [Flags] + enum EntityFlags : byte + { + Dirty = 0x01, Protected = 0x02 + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct PrimaryVolumeDescriptor - { - public readonly DescriptorTag tag; - public readonly uint volumeDescriptorSequenceNumber; - public readonly uint primaryVolumeDescriptorNumber; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] volumeIdentifier; - public readonly ushort volumeSequenceNumber; - public readonly ushort maximumVolumeSequenceNumber; - public readonly ushort interchangeLevel; - public readonly ushort maximumInterchangeLevel; - public readonly uint characterSetList; - public readonly uint maximumCharacterSetList; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] volumeSetIdentifier; - public readonly CharacterSpecification descriptorCharacterSet; - public readonly CharacterSpecification explanatoryCharacterSet; - public readonly ExtentDescriptor volumeAbstract; - public readonly ExtentDescriptor volumeCopyright; - public readonly EntityIdentifier applicationIdentifier; - public readonly Timestamp recordingDateTime; - public readonly EntityIdentifier implementationIdentifier; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] implementationUse; - public readonly uint predecessorVolumeDescriptorSequenceLocation; - public readonly ushort flags; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)] - public readonly byte[] reserved; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct EntityIdentifier + { + /// Entity flags + public readonly EntityFlags flags; + /// Structure identifier + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 23)] + public readonly byte[] identifier; + /// Structure data + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] identifierSuffix; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct LogicalVolumeDescriptor - { - public readonly DescriptorTag tag; - public readonly uint volumeDescriptorSequenceNumber; - public readonly CharacterSpecification descriptorCharacterSet; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] logicalVolumeIdentifier; - public readonly uint logicalBlockSize; - public readonly EntityIdentifier domainIdentifier; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] logicalVolumeContentsUse; - public readonly uint mapTableLength; - public readonly uint numberOfPartitionMaps; - public readonly EntityIdentifier implementationIdentifier; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly byte[] implementationUse; - public readonly ExtentDescriptor integritySequenceExtent; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Timestamp + { + public readonly ushort typeAndZone; + public readonly short year; + public readonly byte month; + public readonly byte day; + public readonly byte hour; + public readonly byte minute; + public readonly byte second; + public readonly byte centiseconds; + public readonly byte hundredsMicroseconds; + public readonly byte microseconds; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct LogicalVolumeIntegrityDescriptor - { - public readonly DescriptorTag tag; - public readonly Timestamp recordingDateTime; - public readonly uint integrityType; - public readonly ExtentDescriptor nextIntegrityExtent; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] logicalVolumeContentsUse; - public readonly uint numberOfPartitions; - public readonly uint lengthOfImplementationUse; + enum TagIdentifier : ushort + { + PrimaryVolumeDescriptor = 1, AnchorVolumeDescriptorPointer = 2, VolumeDescriptorPointer = 3, + ImplementationUseVolumeDescriptor = 4, PartitionDescriptor = 5, LogicalVolumeDescriptor = 6, + UnallocatedSpaceDescriptor = 7, TerminatingDescriptor = 8, LogicalVolumeIntegrityDescriptor = 9 + } - // Follows uint[numberOfPartitions] freeSpaceTable; - // Follows uint[numberOfPartitions] sizeTable; - // Follows byte[lengthOfImplementationUse] implementationUse; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DescriptorTag + { + public readonly TagIdentifier tagIdentifier; + public readonly ushort descriptorVersion; + public readonly byte tagChecksum; + public readonly byte reserved; + public readonly ushort tagSerialNumber; + public readonly ushort descriptorCrc; + public readonly ushort descriptorCrcLength; + public readonly uint tagLocation; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct LogicalVolumeIntegrityDescriptorImplementationUse - { - public readonly EntityIdentifier implementationId; - public readonly uint files; - public readonly uint directories; - public readonly ushort minimumReadUDF; - public readonly ushort minimumWriteUDF; - public readonly ushort maximumWriteUDF; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ExtentDescriptor + { + public readonly uint length; + public readonly uint location; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct CharacterSpecification + { + public readonly byte type; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 63)] + public readonly byte[] information; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct AnchorVolumeDescriptorPointer + { + public readonly DescriptorTag tag; + public readonly ExtentDescriptor mainVolumeDescriptorSequenceExtent; + public readonly ExtentDescriptor reserveVolumeDescriptorSequenceExtent; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 480)] + public readonly byte[] reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct PrimaryVolumeDescriptor + { + public readonly DescriptorTag tag; + public readonly uint volumeDescriptorSequenceNumber; + public readonly uint primaryVolumeDescriptorNumber; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] volumeIdentifier; + public readonly ushort volumeSequenceNumber; + public readonly ushort maximumVolumeSequenceNumber; + public readonly ushort interchangeLevel; + public readonly ushort maximumInterchangeLevel; + public readonly uint characterSetList; + public readonly uint maximumCharacterSetList; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] volumeSetIdentifier; + public readonly CharacterSpecification descriptorCharacterSet; + public readonly CharacterSpecification explanatoryCharacterSet; + public readonly ExtentDescriptor volumeAbstract; + public readonly ExtentDescriptor volumeCopyright; + public readonly EntityIdentifier applicationIdentifier; + public readonly Timestamp recordingDateTime; + public readonly EntityIdentifier implementationIdentifier; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] implementationUse; + public readonly uint predecessorVolumeDescriptorSequenceLocation; + public readonly ushort flags; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 22)] + public readonly byte[] reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct LogicalVolumeDescriptor + { + public readonly DescriptorTag tag; + public readonly uint volumeDescriptorSequenceNumber; + public readonly CharacterSpecification descriptorCharacterSet; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] logicalVolumeIdentifier; + public readonly uint logicalBlockSize; + public readonly EntityIdentifier domainIdentifier; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] logicalVolumeContentsUse; + public readonly uint mapTableLength; + public readonly uint numberOfPartitionMaps; + public readonly EntityIdentifier implementationIdentifier; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly byte[] implementationUse; + public readonly ExtentDescriptor integritySequenceExtent; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct LogicalVolumeIntegrityDescriptor + { + public readonly DescriptorTag tag; + public readonly Timestamp recordingDateTime; + public readonly uint integrityType; + public readonly ExtentDescriptor nextIntegrityExtent; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] logicalVolumeContentsUse; + public readonly uint numberOfPartitions; + public readonly uint lengthOfImplementationUse; + + // Follows uint[numberOfPartitions] freeSpaceTable; + // Follows uint[numberOfPartitions] sizeTable; + // Follows byte[lengthOfImplementationUse] implementationUse; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct LogicalVolumeIntegrityDescriptorImplementationUse + { + public readonly EntityIdentifier implementationId; + public readonly uint files; + public readonly uint directories; + public readonly ushort minimumReadUDF; + public readonly ushort minimumWriteUDF; + public readonly ushort maximumWriteUDF; } } \ No newline at end of file diff --git a/Aaru.Filesystems/UNICOS.cs b/Aaru.Filesystems/UNICOS.cs index 4b65157ce..f47053b48 100644 --- a/Aaru.Filesystems/UNICOS.cs +++ b/Aaru.Filesystems/UNICOS.cs @@ -50,202 +50,201 @@ using ino_t = System.Int64; using Marshal = Aaru.Helpers.Marshal; using time_t = System.Int64; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection for the Cray UNICOS filesystem +public sealed class UNICOS : IFilesystem { + const int NC1_MAXPART = 64; + const int NC1_MAXIREG = 4; + + const ulong UNICOS_MAGIC = 0x6e6331667331636e; + const ulong UNICOS_SECURE = 0xcd076d1771d670cd; + /// - /// Implements detection for the Cray UNICOS filesystem - public sealed class UNICOS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "UNICOS Filesystem Plugin"; + /// + public Guid Id => new("61712F04-066C-44D5-A2A0-1E44C66B33F0"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const int NC1_MAXPART = 64; - const int NC1_MAXIREG = 4; + if(imagePlugin.Info.SectorSize < 512) + return false; - const ulong UNICOS_MAGIC = 0x6e6331667331636e; - const ulong UNICOS_SECURE = 0xcd076d1771d670cd; + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "UNICOS Filesystem Plugin"; - /// - public Guid Id => new("61712F04-066C-44D5-A2A0-1E44C66B33F0"); - /// - public string Author => "Natalia Portillo"; + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(partition.Start + sbSize >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + if(sector.Length < Marshal.SizeOf()) + return false; + + Superblock unicosSb = Marshal.ByteArrayToStructureBigEndian(sector); + + AaruConsole.DebugWriteLine("UNICOS plugin", "magic = 0x{0:X16} (expected 0x{1:X16})", unicosSb.s_magic, + UNICOS_MAGIC); + + return unicosSb.s_magic == UNICOS_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < Marshal.SizeOf()) + return; + + Superblock unicosSb = Marshal.ByteArrayToStructureBigEndian(sector); + + if(unicosSb.s_magic != UNICOS_MAGIC) + return; + + var sb = new StringBuilder(); + + sb.AppendLine("UNICOS filesystem"); + + if(unicosSb.s_secure == UNICOS_SECURE) + sb.AppendLine("Volume is secure"); + + sb.AppendFormat("Volume contains {0} partitions", unicosSb.s_npart).AppendLine(); + sb.AppendFormat("{0} bytes per sector", unicosSb.s_iounit).AppendLine(); + sb.AppendLine("4096 bytes per block"); + sb.AppendFormat("{0} data blocks in volume", unicosSb.s_fsize).AppendLine(); + sb.AppendFormat("Root resides on inode {0}", unicosSb.s_root).AppendLine(); + sb.AppendFormat("{0} inodes in volume", unicosSb.s_isize).AppendLine(); + sb.AppendFormat("Volume last updated on {0}", DateHandlers.UnixToDateTime(unicosSb.s_time)).AppendLine(); + + if(unicosSb.s_error > 0) + sb.AppendFormat("Volume is dirty, error code = 0x{0:X16}", unicosSb.s_error).AppendLine(); + + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(unicosSb.s_fname, Encoding)).AppendLine(); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - if(imagePlugin.Info.SectorSize < 512) - return false; + Type = "UNICOS filesystem", + ClusterSize = 4096, + Clusters = (ulong)unicosSb.s_fsize, + VolumeName = StringHandlers.CToString(unicosSb.s_fname, Encoding), + ModificationDate = DateHandlers.UnixToDateTime(unicosSb.s_time), + ModificationDateSpecified = true + }; - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + XmlFsType.Dirty |= unicosSb.s_error > 0; + } - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; + [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] + readonly struct nc1ireg_sb + { + public readonly ushort i_unused; /* reserved */ + public readonly ushort i_nblk; /* number of blocks */ + public readonly uint i_sblk; /* start block number */ + } - if(partition.Start + sbSize >= partition.End) - return false; + [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] + readonly struct nc1fdev_sb + { + public readonly long fd_name; /* Physical device name */ + public readonly uint fd_sblk; /* Start block number */ + public readonly uint fd_nblk; /* Number of blocks */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NC1_MAXIREG)] + public readonly nc1ireg_sb[] fd_ireg; /* Inode regions */ + } - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming"), + SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")] + readonly struct Superblock + { + public readonly ulong s_magic; /* magic number to indicate file system type */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] s_fname; /* file system name */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] s_fpack; /* file system pack name */ + public readonly dev_t s_dev; /* major/minor device, for verification */ - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - Superblock unicosSb = Marshal.ByteArrayToStructureBigEndian(sector); - - AaruConsole.DebugWriteLine("UNICOS plugin", "magic = 0x{0:X16} (expected 0x{1:X16})", unicosSb.s_magic, - UNICOS_MAGIC); - - return unicosSb.s_magic == UNICOS_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - if(imagePlugin.Info.SectorSize < 512) - return; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < Marshal.SizeOf()) - return; - - Superblock unicosSb = Marshal.ByteArrayToStructureBigEndian(sector); - - if(unicosSb.s_magic != UNICOS_MAGIC) - return; - - var sb = new StringBuilder(); - - sb.AppendLine("UNICOS filesystem"); - - if(unicosSb.s_secure == UNICOS_SECURE) - sb.AppendLine("Volume is secure"); - - sb.AppendFormat("Volume contains {0} partitions", unicosSb.s_npart).AppendLine(); - sb.AppendFormat("{0} bytes per sector", unicosSb.s_iounit).AppendLine(); - sb.AppendLine("4096 bytes per block"); - sb.AppendFormat("{0} data blocks in volume", unicosSb.s_fsize).AppendLine(); - sb.AppendFormat("Root resides on inode {0}", unicosSb.s_root).AppendLine(); - sb.AppendFormat("{0} inodes in volume", unicosSb.s_isize).AppendLine(); - sb.AppendFormat("Volume last updated on {0}", DateHandlers.UnixToDateTime(unicosSb.s_time)).AppendLine(); - - if(unicosSb.s_error > 0) - sb.AppendFormat("Volume is dirty, error code = 0x{0:X16}", unicosSb.s_error).AppendLine(); - - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(unicosSb.s_fname, Encoding)).AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "UNICOS filesystem", - ClusterSize = 4096, - Clusters = (ulong)unicosSb.s_fsize, - VolumeName = StringHandlers.CToString(unicosSb.s_fname, Encoding), - ModificationDate = DateHandlers.UnixToDateTime(unicosSb.s_time), - ModificationDateSpecified = true - }; - - XmlFsType.Dirty |= unicosSb.s_error > 0; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] - readonly struct nc1ireg_sb - { - public readonly ushort i_unused; /* reserved */ - public readonly ushort i_nblk; /* number of blocks */ - public readonly uint i_sblk; /* start block number */ - } - - [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] - readonly struct nc1fdev_sb - { - public readonly long fd_name; /* Physical device name */ - public readonly uint fd_sblk; /* Start block number */ - public readonly uint fd_nblk; /* Number of blocks */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NC1_MAXIREG)] - public readonly nc1ireg_sb[] fd_ireg; /* Inode regions */ - } - - [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming"), - SuppressMessage("ReSharper", "BuiltInTypeReferenceStyle")] - readonly struct Superblock - { - public readonly ulong s_magic; /* magic number to indicate file system type */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] s_fname; /* file system name */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] s_fpack; /* file system pack name */ - public readonly dev_t s_dev; /* major/minor device, for verification */ - - public readonly daddr_t s_fsize; /* size in blocks of entire volume */ - public readonly long s_isize; /* Number of total inodes */ - public readonly long s_bigfile; /* number of bytes at which a file is big */ - public readonly long s_bigunit; /* minimum number of blocks allocated for big files */ - public readonly ulong s_secure; /* security: secure FS label */ - public readonly long s_maxlvl; /* security: maximum security level */ - public readonly long s_minlvl; /* security: minimum security level */ - public readonly long s_valcmp; /* security: valid security compartments */ - public readonly time_t s_time; /* last super block update */ - public readonly blkno_t s_dboff; /* Dynamic block number */ - public readonly ino_t s_root; /* root inode */ - public readonly long s_error; /* Type of file system error detected */ - public readonly blkno_t s_mapoff; /* Start map block number */ - public readonly long s_mapblks; /* Last map block number */ - public readonly long s_nscpys; /* Number of copies of s.b per partition */ - public readonly long s_npart; /* Number of partitions */ - public readonly long s_ifract; /* Ratio of inodes to blocks */ - public readonly extent_t s_sfs; /* SFS only blocks */ - public readonly long s_flag; /* Flag word */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NC1_MAXPART)] - public readonly nc1fdev_sb[] s_part; /* Partition descriptors */ - public readonly long s_iounit; /* Physical block size */ - public readonly long s_numiresblks; /* number of inode reservation blocks */ - /* per region (currently 1) */ - /* 0 = 1*(AU) words, n = (n+1)*(AU) words */ - public readonly long s_priparts; /* bitmap of primary partitions */ - public readonly long s_priblock; /* block size of primary partition(s) */ - /* 0 = 1*512 words, n = (n+1)*512 words */ - public readonly long s_prinblks; /* number of 512 wds blocks in primary */ - public readonly long s_secparts; /* bitmap of secondary partitions */ - public readonly long s_secblock; /* block size of secondary partition(s) */ - /* 0 = 1*512 words, n = (n+1)*512 words */ - public readonly long s_secnblks; /* number of 512 wds blocks in secondary */ - public readonly long s_sbdbparts; /* bitmap of partitions with file system data */ - /* including super blocks, dynamic block */ - /* and free block bitmaps (only primary */ - /* partitions may contain these) */ - public readonly long s_rootdparts; /* bitmap of partitions with root directory */ - /* (only primary partitions) */ - public readonly long s_nudparts; /* bitmap of no-user-data partitions */ - /* (only primary partitions) */ - public readonly long s_nsema; /* SFS: # fs semaphores to allocate */ - public readonly long s_priactive; /* bitmap of primary partitions which contain */ - /* active (up to date) dynamic blocks and */ - /* free block bitmaps. All bits set indicate */ - /* that all primary partitions are active, */ - /* and no kernel manipulation of active flag */ - /* is allowed. */ - public readonly long s_sfs_arbiterid; /* SFS Arbiter ID */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 91)] - public readonly long[] s_fill; /* reserved */ - } + public readonly daddr_t s_fsize; /* size in blocks of entire volume */ + public readonly long s_isize; /* Number of total inodes */ + public readonly long s_bigfile; /* number of bytes at which a file is big */ + public readonly long s_bigunit; /* minimum number of blocks allocated for big files */ + public readonly ulong s_secure; /* security: secure FS label */ + public readonly long s_maxlvl; /* security: maximum security level */ + public readonly long s_minlvl; /* security: minimum security level */ + public readonly long s_valcmp; /* security: valid security compartments */ + public readonly time_t s_time; /* last super block update */ + public readonly blkno_t s_dboff; /* Dynamic block number */ + public readonly ino_t s_root; /* root inode */ + public readonly long s_error; /* Type of file system error detected */ + public readonly blkno_t s_mapoff; /* Start map block number */ + public readonly long s_mapblks; /* Last map block number */ + public readonly long s_nscpys; /* Number of copies of s.b per partition */ + public readonly long s_npart; /* Number of partitions */ + public readonly long s_ifract; /* Ratio of inodes to blocks */ + public readonly extent_t s_sfs; /* SFS only blocks */ + public readonly long s_flag; /* Flag word */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NC1_MAXPART)] + public readonly nc1fdev_sb[] s_part; /* Partition descriptors */ + public readonly long s_iounit; /* Physical block size */ + public readonly long s_numiresblks; /* number of inode reservation blocks */ + /* per region (currently 1) */ + /* 0 = 1*(AU) words, n = (n+1)*(AU) words */ + public readonly long s_priparts; /* bitmap of primary partitions */ + public readonly long s_priblock; /* block size of primary partition(s) */ + /* 0 = 1*512 words, n = (n+1)*512 words */ + public readonly long s_prinblks; /* number of 512 wds blocks in primary */ + public readonly long s_secparts; /* bitmap of secondary partitions */ + public readonly long s_secblock; /* block size of secondary partition(s) */ + /* 0 = 1*512 words, n = (n+1)*512 words */ + public readonly long s_secnblks; /* number of 512 wds blocks in secondary */ + public readonly long s_sbdbparts; /* bitmap of partitions with file system data */ + /* including super blocks, dynamic block */ + /* and free block bitmaps (only primary */ + /* partitions may contain these) */ + public readonly long s_rootdparts; /* bitmap of partitions with root directory */ + /* (only primary partitions) */ + public readonly long s_nudparts; /* bitmap of no-user-data partitions */ + /* (only primary partitions) */ + public readonly long s_nsema; /* SFS: # fs semaphores to allocate */ + public readonly long s_priactive; /* bitmap of primary partitions which contain */ + /* active (up to date) dynamic blocks and */ + /* free block bitmaps. All bits set indicate */ + /* that all primary partitions are active, */ + /* and no kernel manipulation of active flag */ + /* is allowed. */ + public readonly long s_sfs_arbiterid; /* SFS Arbiter ID */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 91)] + public readonly long[] s_fill; /* reserved */ } } \ No newline at end of file diff --git a/Aaru.Filesystems/UNIXBFS.cs b/Aaru.Filesystems/UNIXBFS.cs index b9b981953..fe00d33f8 100644 --- a/Aaru.Filesystems/UNIXBFS.cs +++ b/Aaru.Filesystems/UNIXBFS.cs @@ -40,123 +40,122 @@ using Aaru.Console; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from the Linux kernel +/// +/// Implements detection of the UNIX boot filesystem +public sealed class BFS : IFilesystem { - // Information from the Linux kernel + const uint BFS_MAGIC = 0x1BADFACE; + /// - /// Implements detection of the UNIX boot filesystem - public sealed class BFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "UNIX Boot filesystem"; + /// + public Guid Id => new("1E6E0DA6-F7E4-494C-80C6-CB5929E96155"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint BFS_MAGIC = 0x1BADFACE; + if(2 + partition.Start >= partition.End) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "UNIX Boot filesystem"; - /// - public Guid Id => new("1E6E0DA6-F7E4-494C-80C6-CB5929E96155"); - /// - public string Author => "Natalia Portillo"; + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] tmp); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(errno != ErrorNumber.NoError) + return false; + + uint magic = BitConverter.ToUInt32(tmp, 0); + + return magic == BFS_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bfsSbSector); + + if(errno != ErrorNumber.NoError) + return; + + byte[] sbStrings = new byte[6]; + + var bfsSb = new SuperBlock { - if(2 + partition.Start >= partition.End) - return false; + s_magic = BitConverter.ToUInt32(bfsSbSector, 0x00), + s_start = BitConverter.ToUInt32(bfsSbSector, 0x04), + s_end = BitConverter.ToUInt32(bfsSbSector, 0x08), + s_from = BitConverter.ToUInt32(bfsSbSector, 0x0C), + s_to = BitConverter.ToUInt32(bfsSbSector, 0x10), + s_bfrom = BitConverter.ToInt32(bfsSbSector, 0x14), + s_bto = BitConverter.ToInt32(bfsSbSector, 0x18) + }; - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] tmp); + Array.Copy(bfsSbSector, 0x1C, sbStrings, 0, 6); + bfsSb.s_fsname = StringHandlers.CToString(sbStrings, Encoding); + Array.Copy(bfsSbSector, 0x22, sbStrings, 0, 6); + bfsSb.s_volume = StringHandlers.CToString(sbStrings, Encoding); - if(errno != ErrorNumber.NoError) - return false; + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_magic: 0x{0:X8}", bfsSb.s_magic); + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_start: 0x{0:X8}", bfsSb.s_start); + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_end: 0x{0:X8}", bfsSb.s_end); + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_from: 0x{0:X8}", bfsSb.s_from); + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_to: 0x{0:X8}", bfsSb.s_to); + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_bfrom: 0x{0:X8}", bfsSb.s_bfrom); + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_bto: 0x{0:X8}", bfsSb.s_bto); + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_fsname: 0x{0}", bfsSb.s_fsname); + AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_volume: 0x{0}", bfsSb.s_volume); - uint magic = BitConverter.ToUInt32(tmp, 0); + sb.AppendLine("UNIX Boot filesystem"); - return magic == BFS_MAGIC; - } + sb.AppendFormat("Volume goes from byte {0} to byte {1}, for {2} bytes", bfsSb.s_start, bfsSb.s_end, + bfsSb.s_end - bfsSb.s_start).AppendLine(); - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + sb.AppendFormat("Filesystem name: {0}", bfsSb.s_fsname).AppendLine(); + sb.AppendFormat("Volume name: {0}", bfsSb.s_volume).AppendLine(); + + XmlFsType = new FileSystemType { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + Type = "BFS", + VolumeName = bfsSb.s_volume, + ClusterSize = imagePlugin.Info.SectorSize, + Clusters = partition.End - partition.Start + 1 + }; - var sb = new StringBuilder(); - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] bfsSbSector); + information = sb.ToString(); + } - if(errno != ErrorNumber.NoError) - return; - - byte[] sbStrings = new byte[6]; - - var bfsSb = new SuperBlock - { - s_magic = BitConverter.ToUInt32(bfsSbSector, 0x00), - s_start = BitConverter.ToUInt32(bfsSbSector, 0x04), - s_end = BitConverter.ToUInt32(bfsSbSector, 0x08), - s_from = BitConverter.ToUInt32(bfsSbSector, 0x0C), - s_to = BitConverter.ToUInt32(bfsSbSector, 0x10), - s_bfrom = BitConverter.ToInt32(bfsSbSector, 0x14), - s_bto = BitConverter.ToInt32(bfsSbSector, 0x18) - }; - - Array.Copy(bfsSbSector, 0x1C, sbStrings, 0, 6); - bfsSb.s_fsname = StringHandlers.CToString(sbStrings, Encoding); - Array.Copy(bfsSbSector, 0x22, sbStrings, 0, 6); - bfsSb.s_volume = StringHandlers.CToString(sbStrings, Encoding); - - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_magic: 0x{0:X8}", bfsSb.s_magic); - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_start: 0x{0:X8}", bfsSb.s_start); - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_end: 0x{0:X8}", bfsSb.s_end); - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_from: 0x{0:X8}", bfsSb.s_from); - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_to: 0x{0:X8}", bfsSb.s_to); - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_bfrom: 0x{0:X8}", bfsSb.s_bfrom); - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_bto: 0x{0:X8}", bfsSb.s_bto); - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_fsname: 0x{0}", bfsSb.s_fsname); - AaruConsole.DebugWriteLine("BFS plugin", "bfs_sb.s_volume: 0x{0}", bfsSb.s_volume); - - sb.AppendLine("UNIX Boot filesystem"); - - sb.AppendFormat("Volume goes from byte {0} to byte {1}, for {2} bytes", bfsSb.s_start, bfsSb.s_end, - bfsSb.s_end - bfsSb.s_start).AppendLine(); - - sb.AppendFormat("Filesystem name: {0}", bfsSb.s_fsname).AppendLine(); - sb.AppendFormat("Volume name: {0}", bfsSb.s_volume).AppendLine(); - - XmlFsType = new FileSystemType - { - Type = "BFS", - VolumeName = bfsSb.s_volume, - ClusterSize = imagePlugin.Info.SectorSize, - Clusters = partition.End - partition.Start + 1 - }; - - information = sb.ToString(); - } - - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct SuperBlock - { - /// 0x00, 0x1BADFACE - public uint s_magic; - /// 0x04, start in bytes of volume - public uint s_start; - /// 0x08, end in bytes of volume - public uint s_end; - /// 0x0C, unknown :p - public uint s_from; - /// 0x10, unknown :p - public uint s_to; - /// 0x14, unknown :p - public int s_bfrom; - /// 0x18, unknown :p - public int s_bto; - /// 0x1C, 6 bytes, filesystem name - public string s_fsname; - /// 0x22, 6 bytes, volume name - public string s_volume; - } + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct SuperBlock + { + /// 0x00, 0x1BADFACE + public uint s_magic; + /// 0x04, start in bytes of volume + public uint s_start; + /// 0x08, end in bytes of volume + public uint s_end; + /// 0x0C, unknown :p + public uint s_from; + /// 0x10, unknown :p + public uint s_to; + /// 0x14, unknown :p + public int s_bfrom; + /// 0x18, unknown :p + public int s_bto; + /// 0x1C, 6 bytes, filesystem name + public string s_fsname; + /// 0x22, 6 bytes, volume name + public string s_volume; } } \ No newline at end of file diff --git a/Aaru.Filesystems/VMfs.cs b/Aaru.Filesystems/VMfs.cs index dcaac2ea1..e2425f1c3 100644 --- a/Aaru.Filesystems/VMfs.cs +++ b/Aaru.Filesystems/VMfs.cs @@ -41,129 +41,128 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the VMware filesystem +[SuppressMessage("ReSharper", "UnusedType.Local"), SuppressMessage("ReSharper", "IdentifierTypo"), + SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class VMfs : IFilesystem { + /// Identifier for VMfs + const uint VMFS_MAGIC = 0xC001D00D; + const uint VMFS_BASE = 0x00100000; + /// - /// Implements detection of the VMware filesystem - [SuppressMessage("ReSharper", "UnusedType.Local"), SuppressMessage("ReSharper", "IdentifierTypo"), - SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class VMfs : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "VMware filesystem"; + /// + public Guid Id => new("EE52BDB8-B49C-4122-A3DA-AD21CBE79843"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Identifier for VMfs - const uint VMFS_MAGIC = 0xC001D00D; - const uint VMFS_BASE = 0x00100000; + if(partition.Start >= partition.End) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "VMware filesystem"; - /// - public Guid Id => new("EE52BDB8-B49C-4122-A3DA-AD21CBE79843"); - /// - public string Author => "Natalia Portillo"; + ulong vmfsSuperOff = VMFS_BASE / imagePlugin.Info.SectorSize; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(partition.Start + vmfsSuperOff > partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + vmfsSuperOff, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + uint magic = BitConverter.ToUInt32(sector, 0x00); + + return magic == VMFS_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.UTF8; + information = ""; + ulong vmfsSuperOff = VMFS_BASE / imagePlugin.Info.SectorSize; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + vmfsSuperOff, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + VolumeInfo volInfo = Marshal.ByteArrayToStructureLittleEndian(sector); + + var sbInformation = new StringBuilder(); + + sbInformation.AppendLine("VMware file system"); + + uint ctimeSecs = (uint)(volInfo.ctime / 1000000); + uint ctimeNanoSecs = (uint)(volInfo.ctime % 1000000); + uint mtimeSecs = (uint)(volInfo.mtime / 1000000); + uint mtimeNanoSecs = (uint)(volInfo.mtime % 1000000); + + sbInformation.AppendFormat("Volume version {0}", volInfo.version).AppendLine(); + + sbInformation.AppendFormat("Volume name {0}", StringHandlers.CToString(volInfo.name, Encoding)). + AppendLine(); + + sbInformation.AppendFormat("Volume size {0} bytes", volInfo.size * 256).AppendLine(); + sbInformation.AppendFormat("Volume UUID {0}", volInfo.uuid).AppendLine(); + + sbInformation. + AppendFormat("Volume created on {0}", DateHandlers.UnixUnsignedToDateTime(ctimeSecs, ctimeNanoSecs)). + AppendLine(); + + sbInformation.AppendFormat("Volume last modified on {0}", + DateHandlers.UnixUnsignedToDateTime(mtimeSecs, mtimeNanoSecs)).AppendLine(); + + information = sbInformation.ToString(); + + XmlFsType = new FileSystemType { - if(partition.Start >= partition.End) - return false; + Type = "VMware file system", + CreationDate = DateHandlers.UnixUnsignedToDateTime(ctimeSecs, ctimeNanoSecs), + CreationDateSpecified = true, + ModificationDate = DateHandlers.UnixUnsignedToDateTime(mtimeSecs, mtimeNanoSecs), + ModificationDateSpecified = true, + Clusters = volInfo.size * 256 / imagePlugin.Info.SectorSize, + ClusterSize = imagePlugin.Info.SectorSize, + VolumeSerial = volInfo.uuid.ToString() + }; + } - ulong vmfsSuperOff = VMFS_BASE / imagePlugin.Info.SectorSize; + [Flags] + enum Flags : byte + { + RecyledFolder = 64, CaseSensitive = 128 + } - if(partition.Start + vmfsSuperOff > partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + vmfsSuperOff, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - uint magic = BitConverter.ToUInt32(sector, 0x00); - - return magic == VMFS_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.UTF8; - information = ""; - ulong vmfsSuperOff = VMFS_BASE / imagePlugin.Info.SectorSize; - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + vmfsSuperOff, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - VolumeInfo volInfo = Marshal.ByteArrayToStructureLittleEndian(sector); - - var sbInformation = new StringBuilder(); - - sbInformation.AppendLine("VMware file system"); - - uint ctimeSecs = (uint)(volInfo.ctime / 1000000); - uint ctimeNanoSecs = (uint)(volInfo.ctime % 1000000); - uint mtimeSecs = (uint)(volInfo.mtime / 1000000); - uint mtimeNanoSecs = (uint)(volInfo.mtime % 1000000); - - sbInformation.AppendFormat("Volume version {0}", volInfo.version).AppendLine(); - - sbInformation.AppendFormat("Volume name {0}", StringHandlers.CToString(volInfo.name, Encoding)). - AppendLine(); - - sbInformation.AppendFormat("Volume size {0} bytes", volInfo.size * 256).AppendLine(); - sbInformation.AppendFormat("Volume UUID {0}", volInfo.uuid).AppendLine(); - - sbInformation. - AppendFormat("Volume created on {0}", DateHandlers.UnixUnsignedToDateTime(ctimeSecs, ctimeNanoSecs)). - AppendLine(); - - sbInformation.AppendFormat("Volume last modified on {0}", - DateHandlers.UnixUnsignedToDateTime(mtimeSecs, mtimeNanoSecs)).AppendLine(); - - information = sbInformation.ToString(); - - XmlFsType = new FileSystemType - { - Type = "VMware file system", - CreationDate = DateHandlers.UnixUnsignedToDateTime(ctimeSecs, ctimeNanoSecs), - CreationDateSpecified = true, - ModificationDate = DateHandlers.UnixUnsignedToDateTime(mtimeSecs, mtimeNanoSecs), - ModificationDateSpecified = true, - Clusters = volInfo.size * 256 / imagePlugin.Info.SectorSize, - ClusterSize = imagePlugin.Info.SectorSize, - VolumeSerial = volInfo.uuid.ToString() - }; - } - - [Flags] - enum Flags : byte - { - RecyledFolder = 64, CaseSensitive = 128 - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct VolumeInfo - { - public readonly uint magic; - public readonly uint version; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] unknown1; - public readonly byte lun; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] unknown2; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] - public readonly byte[] name; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 49)] - public readonly byte[] unknown3; - public readonly uint size; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)] - public readonly byte[] unknown4; - public readonly Guid uuid; - public readonly ulong ctime; - public readonly ulong mtime; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct VolumeInfo + { + public readonly uint magic; + public readonly uint version; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] unknown1; + public readonly byte lun; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] unknown2; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 28)] + public readonly byte[] name; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 49)] + public readonly byte[] unknown3; + public readonly uint size; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 31)] + public readonly byte[] unknown4; + public readonly Guid uuid; + public readonly ulong ctime; + public readonly ulong mtime; } } \ No newline at end of file diff --git a/Aaru.Filesystems/VxFS.cs b/Aaru.Filesystems/VxFS.cs index 2847cf24e..dd2c88a78 100644 --- a/Aaru.Filesystems/VxFS.cs +++ b/Aaru.Filesystems/VxFS.cs @@ -40,246 +40,245 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of the Veritas filesystem +public sealed class VxFS : IFilesystem { + /// Identifier for VxFS + const uint VXFS_MAGIC = 0xA501FCF5; + const uint VXFS_BASE = 0x400; + /// - /// Implements detection of the Veritas filesystem - public sealed class VxFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Veritas filesystem"; + /// + public Guid Id => new("EC372605-7687-453C-8BEA-7E0DFF79CB03"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Identifier for VxFS - const uint VXFS_MAGIC = 0xA501FCF5; - const uint VXFS_BASE = 0x400; + ulong vmfsSuperOff = VXFS_BASE / imagePlugin.Info.SectorSize; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Veritas filesystem"; - /// - public Guid Id => new("EC372605-7687-453C-8BEA-7E0DFF79CB03"); - /// - public string Author => "Natalia Portillo"; + if(partition.Start + vmfsSuperOff >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + vmfsSuperOff, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return false; + + uint magic = BitConverter.ToUInt32(sector, 0x00); + + return magic == VXFS_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.UTF8; + information = ""; + ulong vmfsSuperOff = VXFS_BASE / imagePlugin.Info.SectorSize; + ErrorNumber errno = imagePlugin.ReadSector(partition.Start + vmfsSuperOff, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + SuperBlock vxSb = Marshal.ByteArrayToStructureLittleEndian(sector); + + var sbInformation = new StringBuilder(); + + sbInformation.AppendLine("Veritas file system"); + + sbInformation.AppendFormat("Volume version {0}", vxSb.vs_version).AppendLine(); + + sbInformation.AppendFormat("Volume name {0}", StringHandlers.CToString(vxSb.vs_fname, Encoding)). + AppendLine(); + + sbInformation.AppendFormat("Volume has {0} blocks of {1} bytes each", vxSb.vs_bsize, vxSb.vs_size). + AppendLine(); + + sbInformation.AppendFormat("Volume has {0} inodes per block", vxSb.vs_inopb).AppendLine(); + sbInformation.AppendFormat("Volume has {0} free inodes", vxSb.vs_ifree).AppendLine(); + sbInformation.AppendFormat("Volume has {0} free blocks", vxSb.vs_free).AppendLine(); + + sbInformation.AppendFormat("Volume created on {0}", + DateHandlers.UnixUnsignedToDateTime(vxSb.vs_ctime, vxSb.vs_cutime)).AppendLine(); + + sbInformation.AppendFormat("Volume last modified on {0}", + DateHandlers.UnixUnsignedToDateTime(vxSb.vs_wtime, vxSb.vs_wutime)).AppendLine(); + + if(vxSb.vs_clean != 0) + sbInformation.AppendLine("Volume is dirty"); + + information = sbInformation.ToString(); + + XmlFsType = new FileSystemType { - ulong vmfsSuperOff = VXFS_BASE / imagePlugin.Info.SectorSize; + Type = "Veritas file system", + CreationDate = DateHandlers.UnixUnsignedToDateTime(vxSb.vs_ctime, vxSb.vs_cutime), + CreationDateSpecified = true, + ModificationDate = DateHandlers.UnixUnsignedToDateTime(vxSb.vs_wtime, vxSb.vs_wutime), + ModificationDateSpecified = true, + Clusters = (ulong)vxSb.vs_size, + ClusterSize = (uint)vxSb.vs_bsize, + Dirty = vxSb.vs_clean != 0, + FreeClusters = (ulong)vxSb.vs_free, + FreeClustersSpecified = true + }; + } - if(partition.Start + vmfsSuperOff >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + vmfsSuperOff, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - uint magic = BitConverter.ToUInt32(sector, 0x00); - - return magic == VXFS_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.UTF8; - information = ""; - ulong vmfsSuperOff = VXFS_BASE / imagePlugin.Info.SectorSize; - ErrorNumber errno = imagePlugin.ReadSector(partition.Start + vmfsSuperOff, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - SuperBlock vxSb = Marshal.ByteArrayToStructureLittleEndian(sector); - - var sbInformation = new StringBuilder(); - - sbInformation.AppendLine("Veritas file system"); - - sbInformation.AppendFormat("Volume version {0}", vxSb.vs_version).AppendLine(); - - sbInformation.AppendFormat("Volume name {0}", StringHandlers.CToString(vxSb.vs_fname, Encoding)). - AppendLine(); - - sbInformation.AppendFormat("Volume has {0} blocks of {1} bytes each", vxSb.vs_bsize, vxSb.vs_size). - AppendLine(); - - sbInformation.AppendFormat("Volume has {0} inodes per block", vxSb.vs_inopb).AppendLine(); - sbInformation.AppendFormat("Volume has {0} free inodes", vxSb.vs_ifree).AppendLine(); - sbInformation.AppendFormat("Volume has {0} free blocks", vxSb.vs_free).AppendLine(); - - sbInformation.AppendFormat("Volume created on {0}", - DateHandlers.UnixUnsignedToDateTime(vxSb.vs_ctime, vxSb.vs_cutime)).AppendLine(); - - sbInformation.AppendFormat("Volume last modified on {0}", - DateHandlers.UnixUnsignedToDateTime(vxSb.vs_wtime, vxSb.vs_wutime)).AppendLine(); - - if(vxSb.vs_clean != 0) - sbInformation.AppendLine("Volume is dirty"); - - information = sbInformation.ToString(); - - XmlFsType = new FileSystemType - { - Type = "Veritas file system", - CreationDate = DateHandlers.UnixUnsignedToDateTime(vxSb.vs_ctime, vxSb.vs_cutime), - CreationDateSpecified = true, - ModificationDate = DateHandlers.UnixUnsignedToDateTime(vxSb.vs_wtime, vxSb.vs_wutime), - ModificationDateSpecified = true, - Clusters = (ulong)vxSb.vs_size, - ClusterSize = (uint)vxSb.vs_bsize, - Dirty = vxSb.vs_clean != 0, - FreeClusters = (ulong)vxSb.vs_free, - FreeClustersSpecified = true - }; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - /// Magic number - public readonly uint vs_magic; - /// VxFS version - public readonly int vs_version; - /// create time - secs - public readonly uint vs_ctime; - /// create time - usecs - public readonly uint vs_cutime; - /// unused - public readonly int __unused1; - /// unused - public readonly int __unused2; - /// obsolete - public readonly int vs_old_logstart; - /// obsolete - public readonly int vs_old_logend; - /// block size - public readonly int vs_bsize; - /// number of blocks - public readonly int vs_size; - /// number of data blocks - public readonly int vs_dsize; - /// obsolete - public readonly uint vs_old_ninode; - /// obsolete - public readonly int vs_old_nau; - /// unused - public readonly int __unused3; - /// obsolete - public readonly int vs_old_defiextsize; - /// obsolete - public readonly int vs_old_ilbsize; - /// size of immediate data area - public readonly int vs_immedlen; - /// number of direct extentes - public readonly int vs_ndaddr; - /// address of first AU - public readonly int vs_firstau; - /// offset of extent map in AU - public readonly int vs_emap; - /// offset of inode map in AU - public readonly int vs_imap; - /// offset of ExtOp. map in AU - public readonly int vs_iextop; - /// offset of inode list in AU - public readonly int vs_istart; - /// offset of fdblock in AU - public readonly int vs_bstart; - /// aufirst + emap - public readonly int vs_femap; - /// aufirst + imap - public readonly int vs_fimap; - /// aufirst + iextop - public readonly int vs_fiextop; - /// aufirst + istart - public readonly int vs_fistart; - /// aufirst + bstart - public readonly int vs_fbstart; - /// number of entries in indir - public readonly int vs_nindir; - /// length of AU in blocks - public readonly int vs_aulen; - /// length of imap in blocks - public readonly int vs_auimlen; - /// length of emap in blocks - public readonly int vs_auemlen; - /// length of ilist in blocks - public readonly int vs_auilen; - /// length of pad in blocks - public readonly int vs_aupad; - /// data blocks in AU - public readonly int vs_aublocks; - /// log base 2 of aublocks - public readonly int vs_maxtier; - /// number of inodes per blk - public readonly int vs_inopb; - /// obsolete - public readonly int vs_old_inopau; - /// obsolete - public readonly int vs_old_inopilb; - /// obsolete - public readonly int vs_old_ndiripau; - /// size of indirect addr ext. - public readonly int vs_iaddrlen; - /// log base 2 of bsize - public readonly int vs_bshift; - /// log base 2 of inobp - public readonly int vs_inoshift; - /// ~( bsize - 1 ) - public readonly int vs_bmask; - /// bsize - 1 - public readonly int vs_boffmask; - /// old_inopilb - 1 - public readonly int vs_old_inomask; - /// checksum of V1 data - public readonly int vs_checksum; - /// number of free blocks - public readonly int vs_free; - /// number of free inodes - public readonly int vs_ifree; - /// number of free extents by size - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly int[] vs_efree; - /// flags ?!? - public readonly int vs_flags; - /// filesystem has been changed - public readonly byte vs_mod; - /// clean FS - public readonly byte vs_clean; - /// unused - public readonly ushort __unused4; - /// mount time log ID - public readonly uint vs_firstlogid; - /// last time written - sec - public readonly uint vs_wtime; - /// last time written - usec - public readonly uint vs_wutime; - /// FS name - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] vs_fname; - /// FS pack name - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] - public readonly byte[] vs_fpack; - /// log format version - public readonly int vs_logversion; - /// unused - public readonly int __unused5; - /// OLT extent and replica - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly int[] vs_oltext; - /// OLT extent size - public readonly int vs_oltsize; - /// size of inode map - public readonly int vs_iauimlen; - /// size of IAU in blocks - public readonly int vs_iausize; - /// size of inode in bytes - public readonly int vs_dinosize; - /// indir levels per inode - public readonly int vs_old_dniaddr; - /// checksum of V2 RO - public readonly int vs_checksum2; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + /// Magic number + public readonly uint vs_magic; + /// VxFS version + public readonly int vs_version; + /// create time - secs + public readonly uint vs_ctime; + /// create time - usecs + public readonly uint vs_cutime; + /// unused + public readonly int __unused1; + /// unused + public readonly int __unused2; + /// obsolete + public readonly int vs_old_logstart; + /// obsolete + public readonly int vs_old_logend; + /// block size + public readonly int vs_bsize; + /// number of blocks + public readonly int vs_size; + /// number of data blocks + public readonly int vs_dsize; + /// obsolete + public readonly uint vs_old_ninode; + /// obsolete + public readonly int vs_old_nau; + /// unused + public readonly int __unused3; + /// obsolete + public readonly int vs_old_defiextsize; + /// obsolete + public readonly int vs_old_ilbsize; + /// size of immediate data area + public readonly int vs_immedlen; + /// number of direct extentes + public readonly int vs_ndaddr; + /// address of first AU + public readonly int vs_firstau; + /// offset of extent map in AU + public readonly int vs_emap; + /// offset of inode map in AU + public readonly int vs_imap; + /// offset of ExtOp. map in AU + public readonly int vs_iextop; + /// offset of inode list in AU + public readonly int vs_istart; + /// offset of fdblock in AU + public readonly int vs_bstart; + /// aufirst + emap + public readonly int vs_femap; + /// aufirst + imap + public readonly int vs_fimap; + /// aufirst + iextop + public readonly int vs_fiextop; + /// aufirst + istart + public readonly int vs_fistart; + /// aufirst + bstart + public readonly int vs_fbstart; + /// number of entries in indir + public readonly int vs_nindir; + /// length of AU in blocks + public readonly int vs_aulen; + /// length of imap in blocks + public readonly int vs_auimlen; + /// length of emap in blocks + public readonly int vs_auemlen; + /// length of ilist in blocks + public readonly int vs_auilen; + /// length of pad in blocks + public readonly int vs_aupad; + /// data blocks in AU + public readonly int vs_aublocks; + /// log base 2 of aublocks + public readonly int vs_maxtier; + /// number of inodes per blk + public readonly int vs_inopb; + /// obsolete + public readonly int vs_old_inopau; + /// obsolete + public readonly int vs_old_inopilb; + /// obsolete + public readonly int vs_old_ndiripau; + /// size of indirect addr ext. + public readonly int vs_iaddrlen; + /// log base 2 of bsize + public readonly int vs_bshift; + /// log base 2 of inobp + public readonly int vs_inoshift; + /// ~( bsize - 1 ) + public readonly int vs_bmask; + /// bsize - 1 + public readonly int vs_boffmask; + /// old_inopilb - 1 + public readonly int vs_old_inomask; + /// checksum of V1 data + public readonly int vs_checksum; + /// number of free blocks + public readonly int vs_free; + /// number of free inodes + public readonly int vs_ifree; + /// number of free extents by size + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly int[] vs_efree; + /// flags ?!? + public readonly int vs_flags; + /// filesystem has been changed + public readonly byte vs_mod; + /// clean FS + public readonly byte vs_clean; + /// unused + public readonly ushort __unused4; + /// mount time log ID + public readonly uint vs_firstlogid; + /// last time written - sec + public readonly uint vs_wtime; + /// last time written - usec + public readonly uint vs_wutime; + /// FS name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] vs_fname; + /// FS pack name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)] + public readonly byte[] vs_fpack; + /// log format version + public readonly int vs_logversion; + /// unused + public readonly int __unused5; + /// OLT extent and replica + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly int[] vs_oltext; + /// OLT extent size + public readonly int vs_oltsize; + /// size of inode map + public readonly int vs_iauimlen; + /// size of IAU in blocks + public readonly int vs_iausize; + /// size of inode in bytes + public readonly int vs_dinosize; + /// indir levels per inode + public readonly int vs_old_dniaddr; + /// checksum of V2 RO + public readonly int vs_checksum2; } } \ No newline at end of file diff --git a/Aaru.Filesystems/XFS.cs b/Aaru.Filesystems/XFS.cs index fb1f9f440..d49ed8f84 100644 --- a/Aaru.Filesystems/XFS.cs +++ b/Aaru.Filesystems/XFS.cs @@ -41,268 +41,267 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements detection of SGI's XFS +public sealed class XFS : IFilesystem { + const uint XFS_MAGIC = 0x58465342; + /// - /// Implements detection of SGI's XFS - public sealed class XFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "XFS Filesystem Plugin"; + /// + public Guid Id => new("1D8CD8B8-27E6-410F-9973-D16409225FBA"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint XFS_MAGIC = 0x58465342; + if(imagePlugin.Info.SectorSize < 512) + return false; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "XFS Filesystem Plugin"; - /// - public Guid Id => new("1D8CD8B8-27E6-410F-9973-D16409225FBA"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + // Misaligned + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - if(imagePlugin.Info.SectorSize < 512) + uint sbSize = (uint)((Marshal.SizeOf() + 0x400) / imagePlugin.Info.SectorSize); + + if((Marshal.SizeOf() + 0x400) % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) return false; - // Misaligned - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) - { - uint sbSize = (uint)((Marshal.SizeOf() + 0x400) / imagePlugin.Info.SectorSize); + if(sector.Length < Marshal.SizeOf()) + return false; - if((Marshal.SizeOf() + 0x400) % imagePlugin.Info.SectorSize != 0) + byte[] sbpiece = new byte[Marshal.SizeOf()]; + + foreach(int location in new[] + { + 0, 0x200, 0x400 + }) + { + Array.Copy(sector, location, sbpiece, 0, Marshal.SizeOf()); + + Superblock xfsSb = Marshal.ByteArrayToStructureBigEndian(sbpiece); + + AaruConsole.DebugWriteLine("XFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8})", + location, xfsSb.magicnum, XFS_MAGIC); + + if(xfsSb.magicnum == XFS_MAGIC) + return true; + } + } + else + foreach(int i in new[] + { + 0, 1, 2 + }) + { + ulong location = (ulong)i; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) sbSize++; - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); if(errno != ErrorNumber.NoError) - return false; + continue; if(sector.Length < Marshal.SizeOf()) return false; - byte[] sbpiece = new byte[Marshal.SizeOf()]; + Superblock xfsSb = Marshal.ByteArrayToStructureBigEndian(sector); - foreach(int location in new[] - { - 0, 0x200, 0x400 - }) - { - Array.Copy(sector, location, sbpiece, 0, Marshal.SizeOf()); + AaruConsole.DebugWriteLine("XFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8})", location, + xfsSb.magicnum, XFS_MAGIC); - Superblock xfsSb = Marshal.ByteArrayToStructureBigEndian(sbpiece); - - AaruConsole.DebugWriteLine("XFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8})", - location, xfsSb.magicnum, XFS_MAGIC); - - if(xfsSb.magicnum == XFS_MAGIC) - return true; - } + if(xfsSb.magicnum == XFS_MAGIC) + return true; } - else - foreach(int i in new[] - { - 0, 1, 2 - }) - { - ulong location = (ulong)i; - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + return false; + } - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); + if(imagePlugin.Info.SectorSize < 512) + return; - if(errno != ErrorNumber.NoError) - continue; + var xfsSb = new Superblock(); - if(sector.Length < Marshal.SizeOf()) - return false; - - Superblock xfsSb = Marshal.ByteArrayToStructureBigEndian(sector); - - AaruConsole.DebugWriteLine("XFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8})", location, - xfsSb.magicnum, XFS_MAGIC); - - if(xfsSb.magicnum == XFS_MAGIC) - return true; - } - - return false; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + // Misaligned + if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + uint sbSize = (uint)((Marshal.SizeOf() + 0x400) / imagePlugin.Info.SectorSize); - if(imagePlugin.Info.SectorSize < 512) + if((Marshal.SizeOf() + 0x400) % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError || + sector.Length < Marshal.SizeOf()) return; - var xfsSb = new Superblock(); + byte[] sbpiece = new byte[Marshal.SizeOf()]; - // Misaligned - if(imagePlugin.Info.XmlMediaType == XmlMediaType.OpticalDisc) + foreach(int location in new[] + { + 0, 0x200, 0x400 + }) { - uint sbSize = (uint)((Marshal.SizeOf() + 0x400) / imagePlugin.Info.SectorSize); + Array.Copy(sector, location, sbpiece, 0, Marshal.SizeOf()); - if((Marshal.SizeOf() + 0x400) % imagePlugin.Info.SectorSize != 0) + xfsSb = Marshal.ByteArrayToStructureBigEndian(sbpiece); + + AaruConsole.DebugWriteLine("XFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8})", + location, xfsSb.magicnum, XFS_MAGIC); + + if(xfsSb.magicnum == XFS_MAGIC) + break; + } + } + else + foreach(int i in new[] + { + 0, 1, 2 + }) + { + ulong location = (ulong)i; + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) sbSize++; - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); if(errno != ErrorNumber.NoError || sector.Length < Marshal.SizeOf()) return; - byte[] sbpiece = new byte[Marshal.SizeOf()]; + xfsSb = Marshal.ByteArrayToStructureBigEndian(sector); - foreach(int location in new[] - { - 0, 0x200, 0x400 - }) - { - Array.Copy(sector, location, sbpiece, 0, Marshal.SizeOf()); + AaruConsole.DebugWriteLine("XFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8})", location, + xfsSb.magicnum, XFS_MAGIC); - xfsSb = Marshal.ByteArrayToStructureBigEndian(sbpiece); - - AaruConsole.DebugWriteLine("XFS plugin", "magic at 0x{0:X3} = 0x{1:X8} (expected 0x{2:X8})", - location, xfsSb.magicnum, XFS_MAGIC); - - if(xfsSb.magicnum == XFS_MAGIC) - break; - } + if(xfsSb.magicnum == XFS_MAGIC) + break; } - else - foreach(int i in new[] - { - 0, 1, 2 - }) - { - ulong location = (ulong)i; - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; + if(xfsSb.magicnum != XFS_MAGIC) + return; - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start + location, sbSize, out byte[] sector); + var sb = new StringBuilder(); - if(errno != ErrorNumber.NoError || - sector.Length < Marshal.SizeOf()) - return; + sb.AppendLine("XFS filesystem"); + sb.AppendFormat("Filesystem version {0}", xfsSb.version & 0xF).AppendLine(); + sb.AppendFormat("{0} bytes per sector", xfsSb.sectsize).AppendLine(); + sb.AppendFormat("{0} bytes per block", xfsSb.blocksize).AppendLine(); + sb.AppendFormat("{0} bytes per inode", xfsSb.inodesize).AppendLine(); + sb.AppendFormat("{0} data blocks in volume, {1} free", xfsSb.dblocks, xfsSb.fdblocks).AppendLine(); + sb.AppendFormat("{0} blocks per allocation group", xfsSb.agblocks).AppendLine(); + sb.AppendFormat("{0} allocation groups in volume", xfsSb.agcount).AppendLine(); + sb.AppendFormat("{0} inodes in volume, {1} free", xfsSb.icount, xfsSb.ifree).AppendLine(); - xfsSb = Marshal.ByteArrayToStructureBigEndian(sector); + if(xfsSb.inprogress > 0) + sb.AppendLine("fsck in progress"); - AaruConsole.DebugWriteLine("XFS plugin", "magic at {0} = 0x{1:X8} (expected 0x{2:X8})", location, - xfsSb.magicnum, XFS_MAGIC); + sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(xfsSb.fname, Encoding)).AppendLine(); + sb.AppendFormat("Volume UUID: {0}", xfsSb.uuid).AppendLine(); - if(xfsSb.magicnum == XFS_MAGIC) - break; - } + information = sb.ToString(); - if(xfsSb.magicnum != XFS_MAGIC) - return; - - var sb = new StringBuilder(); - - sb.AppendLine("XFS filesystem"); - sb.AppendFormat("Filesystem version {0}", xfsSb.version & 0xF).AppendLine(); - sb.AppendFormat("{0} bytes per sector", xfsSb.sectsize).AppendLine(); - sb.AppendFormat("{0} bytes per block", xfsSb.blocksize).AppendLine(); - sb.AppendFormat("{0} bytes per inode", xfsSb.inodesize).AppendLine(); - sb.AppendFormat("{0} data blocks in volume, {1} free", xfsSb.dblocks, xfsSb.fdblocks).AppendLine(); - sb.AppendFormat("{0} blocks per allocation group", xfsSb.agblocks).AppendLine(); - sb.AppendFormat("{0} allocation groups in volume", xfsSb.agcount).AppendLine(); - sb.AppendFormat("{0} inodes in volume, {1} free", xfsSb.icount, xfsSb.ifree).AppendLine(); - - if(xfsSb.inprogress > 0) - sb.AppendLine("fsck in progress"); - - sb.AppendFormat("Volume name: {0}", StringHandlers.CToString(xfsSb.fname, Encoding)).AppendLine(); - sb.AppendFormat("Volume UUID: {0}", xfsSb.uuid).AppendLine(); - - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "XFS filesystem", - ClusterSize = xfsSb.blocksize, - Clusters = xfsSb.dblocks, - FreeClusters = xfsSb.fdblocks, - FreeClustersSpecified = true, - Files = xfsSb.icount - xfsSb.ifree, - FilesSpecified = true, - Dirty = xfsSb.inprogress > 0, - VolumeName = StringHandlers.CToString(xfsSb.fname, Encoding), - VolumeSerial = xfsSb.uuid.ToString() - }; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Superblock + XmlFsType = new FileSystemType { - public readonly uint magicnum; - public readonly uint blocksize; - public readonly ulong dblocks; - public readonly ulong rblocks; - public readonly ulong rextents; - public readonly Guid uuid; - public readonly ulong logstat; - public readonly ulong rootino; - public readonly ulong rbmino; - public readonly ulong rsumino; - public readonly uint rextsize; - public readonly uint agblocks; - public readonly uint agcount; - public readonly uint rbmblocks; - public readonly uint logblocks; - public readonly ushort version; - public readonly ushort sectsize; - public readonly ushort inodesize; - public readonly ushort inopblock; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] - public readonly byte[] fname; - public readonly byte blocklog; - public readonly byte sectlog; - public readonly byte inodelog; - public readonly byte inopblog; - public readonly byte agblklog; - public readonly byte rextslog; - public readonly byte inprogress; - public readonly byte imax_pct; - public readonly ulong icount; - public readonly ulong ifree; - public readonly ulong fdblocks; - public readonly ulong frextents; - public readonly ulong uquotino; - public readonly ulong gquotino; - public readonly ushort qflags; - public readonly byte flags; - public readonly byte shared_vn; - public readonly ulong inoalignmt; - public readonly ulong unit; - public readonly ulong width; - public readonly byte dirblklog; - public readonly byte logsectlog; - public readonly ushort logsectsize; - public readonly uint logsunit; - public readonly uint features2; - public readonly uint bad_features2; - public readonly uint features_compat; - public readonly uint features_ro_compat; - public readonly uint features_incompat; - public readonly uint features_log_incompat; + Type = "XFS filesystem", + ClusterSize = xfsSb.blocksize, + Clusters = xfsSb.dblocks, + FreeClusters = xfsSb.fdblocks, + FreeClustersSpecified = true, + Files = xfsSb.icount - xfsSb.ifree, + FilesSpecified = true, + Dirty = xfsSb.inprogress > 0, + VolumeName = StringHandlers.CToString(xfsSb.fname, Encoding), + VolumeSerial = xfsSb.uuid.ToString() + }; + } - // This field is little-endian while rest of superblock is big-endian - public readonly uint crc; - public readonly uint spino_align; - public readonly ulong pquotino; - public readonly ulong lsn; - public readonly Guid meta_uuid; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Superblock + { + public readonly uint magicnum; + public readonly uint blocksize; + public readonly ulong dblocks; + public readonly ulong rblocks; + public readonly ulong rextents; + public readonly Guid uuid; + public readonly ulong logstat; + public readonly ulong rootino; + public readonly ulong rbmino; + public readonly ulong rsumino; + public readonly uint rextsize; + public readonly uint agblocks; + public readonly uint agcount; + public readonly uint rbmblocks; + public readonly uint logblocks; + public readonly ushort version; + public readonly ushort sectsize; + public readonly ushort inodesize; + public readonly ushort inopblock; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 12)] + public readonly byte[] fname; + public readonly byte blocklog; + public readonly byte sectlog; + public readonly byte inodelog; + public readonly byte inopblog; + public readonly byte agblklog; + public readonly byte rextslog; + public readonly byte inprogress; + public readonly byte imax_pct; + public readonly ulong icount; + public readonly ulong ifree; + public readonly ulong fdblocks; + public readonly ulong frextents; + public readonly ulong uquotino; + public readonly ulong gquotino; + public readonly ushort qflags; + public readonly byte flags; + public readonly byte shared_vn; + public readonly ulong inoalignmt; + public readonly ulong unit; + public readonly ulong width; + public readonly byte dirblklog; + public readonly byte logsectlog; + public readonly ushort logsectsize; + public readonly uint logsunit; + public readonly uint features2; + public readonly uint bad_features2; + public readonly uint features_compat; + public readonly uint features_ro_compat; + public readonly uint features_incompat; + public readonly uint features_log_incompat; + + // This field is little-endian while rest of superblock is big-endian + public readonly uint crc; + public readonly uint spino_align; + public readonly ulong pquotino; + public readonly ulong lsn; + public readonly Guid meta_uuid; } } \ No newline at end of file diff --git a/Aaru.Filesystems/Xia.cs b/Aaru.Filesystems/Xia.cs index a02201c98..0ce95dfd4 100644 --- a/Aaru.Filesystems/Xia.cs +++ b/Aaru.Filesystems/Xia.cs @@ -41,180 +41,179 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from the Linux kernel +/// +/// Implements detection for the Xia filesystem +[SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "UnusedType.Local")] +public sealed class Xia : IFilesystem { - // Information from the Linux kernel + const uint XIAFS_SUPER_MAGIC = 0x012FD16D; + const uint XIAFS_ROOT_INO = 1; + const uint XIAFS_BAD_INO = 2; + const int XIAFS_MAX_LINK = 64000; + const int XIAFS_DIR_SIZE = 12; + const int XIAFS_NUM_BLOCK_POINTERS = 10; + const int XIAFS_NAME_LEN = 248; + /// - /// Implements detection for the Xia filesystem - [SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "UnusedType.Local")] - public sealed class Xia : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Xia filesystem"; + /// + public Guid Id => new("169E1DE5-24F2-4EF6-A04D-A4B2CA66DE9D"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const uint XIAFS_SUPER_MAGIC = 0x012FD16D; - const uint XIAFS_ROOT_INO = 1; - const uint XIAFS_BAD_INO = 2; - const int XIAFS_MAX_LINK = 64000; - const int XIAFS_DIR_SIZE = 12; - const int XIAFS_NUM_BLOCK_POINTERS = 10; - const int XIAFS_NAME_LEN = 248; + int sbSizeInBytes = Marshal.SizeOf(); + uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Xia filesystem"; - /// - public Guid Id => new("169E1DE5-24F2-4EF6-A04D-A4B2CA66DE9D"); - /// - public string Author => "Natalia Portillo"; + if(sbSizeInBytes % imagePlugin.Info.SectorSize > 0) + sbSizeInSectors++; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(sbSizeInSectors + partition.Start >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSizeInSectors, out byte[] sbSector); + + if(errno != ErrorNumber.NoError) + return false; + + SuperBlock supblk = Marshal.ByteArrayToStructureLittleEndian(sbSector); + + return supblk.s_magic == XIAFS_SUPER_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + + int sbSizeInBytes = Marshal.SizeOf(); + uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); + + if(sbSizeInBytes % imagePlugin.Info.SectorSize > 0) + sbSizeInSectors++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSizeInSectors, out byte[] sbSector); + + if(errno != ErrorNumber.NoError) + return; + + SuperBlock supblk = Marshal.ByteArrayToStructureLittleEndian(sbSector); + + sb.AppendFormat("{0} bytes per zone", supblk.s_zone_size).AppendLine(); + + sb.AppendFormat("{0} zones in volume ({1} bytes)", supblk.s_nzones, supblk.s_nzones * supblk.s_zone_size). + AppendLine(); + + sb.AppendFormat("{0} inodes", supblk.s_ninodes).AppendLine(); + + sb.AppendFormat("{0} data zones ({1} bytes)", supblk.s_ndatazones, + supblk.s_ndatazones * supblk.s_zone_size).AppendLine(); + + sb.AppendFormat("{0} imap zones ({1} bytes)", supblk.s_imap_zones, + supblk.s_imap_zones * supblk.s_zone_size).AppendLine(); + + sb.AppendFormat("{0} zmap zones ({1} bytes)", supblk.s_zmap_zones, + supblk.s_zmap_zones * supblk.s_zone_size).AppendLine(); + + sb.AppendFormat("First data zone: {0}", supblk.s_firstdatazone).AppendLine(); + + sb.AppendFormat("Maximum filesize is {0} bytes ({1} MiB)", supblk.s_max_size, supblk.s_max_size / 1048576). + AppendLine(); + + sb.AppendFormat("{0} zones reserved for kernel images ({1} bytes)", supblk.s_kernzones, + supblk.s_kernzones * supblk.s_zone_size).AppendLine(); + + sb.AppendFormat("First kernel zone: {0}", supblk.s_firstkernzone).AppendLine(); + + XmlFsType = new FileSystemType { - int sbSizeInBytes = Marshal.SizeOf(); - uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); + Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(supblk.s_boot_segment), + Clusters = supblk.s_nzones, + ClusterSize = supblk.s_zone_size, + Type = "Xia filesystem" + }; - if(sbSizeInBytes % imagePlugin.Info.SectorSize > 0) - sbSizeInSectors++; + information = sb.ToString(); + } - if(sbSizeInSectors + partition.Start >= partition.End) - return false; + /// Xia superblock + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct SuperBlock + { + /// 1st sector reserved for boot + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] + public readonly byte[] s_boot_segment; + /// the name says it + public readonly uint s_zone_size; + /// volume size, zone aligned + public readonly uint s_nzones; + /// # of inodes + public readonly uint s_ninodes; + /// # of data zones + public readonly uint s_ndatazones; + /// # of imap zones + public readonly uint s_imap_zones; + /// # of zmap zones + public readonly uint s_zmap_zones; + /// first data zone + public readonly uint s_firstdatazone; + /// z size = 1KB << z shift + public readonly uint s_zone_shift; + /// max size of a single file + public readonly uint s_max_size; + /// reserved + public readonly uint s_reserved0; + /// reserved + public readonly uint s_reserved1; + /// reserved + public readonly uint s_reserved2; + /// reserved + public readonly uint s_reserved3; + /// first kernel zone + public readonly uint s_firstkernzone; + /// kernel size in zones + public readonly uint s_kernzones; + /// magic number for xiafs + public readonly uint s_magic; + } - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSizeInSectors, out byte[] sbSector); + /// Xia directory entry + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DirectoryEntry + { + public readonly uint d_ino; + public readonly ushort d_rec_len; + public readonly byte d_name_len; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = XIAFS_NAME_LEN + 1)] + public readonly byte[] d_name; + } - if(errno != ErrorNumber.NoError) - return false; - - SuperBlock supblk = Marshal.ByteArrayToStructureLittleEndian(sbSector); - - return supblk.s_magic == XIAFS_SUPER_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - var sb = new StringBuilder(); - - int sbSizeInBytes = Marshal.SizeOf(); - uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); - - if(sbSizeInBytes % imagePlugin.Info.SectorSize > 0) - sbSizeInSectors++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSizeInSectors, out byte[] sbSector); - - if(errno != ErrorNumber.NoError) - return; - - SuperBlock supblk = Marshal.ByteArrayToStructureLittleEndian(sbSector); - - sb.AppendFormat("{0} bytes per zone", supblk.s_zone_size).AppendLine(); - - sb.AppendFormat("{0} zones in volume ({1} bytes)", supblk.s_nzones, supblk.s_nzones * supblk.s_zone_size). - AppendLine(); - - sb.AppendFormat("{0} inodes", supblk.s_ninodes).AppendLine(); - - sb.AppendFormat("{0} data zones ({1} bytes)", supblk.s_ndatazones, - supblk.s_ndatazones * supblk.s_zone_size).AppendLine(); - - sb.AppendFormat("{0} imap zones ({1} bytes)", supblk.s_imap_zones, - supblk.s_imap_zones * supblk.s_zone_size).AppendLine(); - - sb.AppendFormat("{0} zmap zones ({1} bytes)", supblk.s_zmap_zones, - supblk.s_zmap_zones * supblk.s_zone_size).AppendLine(); - - sb.AppendFormat("First data zone: {0}", supblk.s_firstdatazone).AppendLine(); - - sb.AppendFormat("Maximum filesize is {0} bytes ({1} MiB)", supblk.s_max_size, supblk.s_max_size / 1048576). - AppendLine(); - - sb.AppendFormat("{0} zones reserved for kernel images ({1} bytes)", supblk.s_kernzones, - supblk.s_kernzones * supblk.s_zone_size).AppendLine(); - - sb.AppendFormat("First kernel zone: {0}", supblk.s_firstkernzone).AppendLine(); - - XmlFsType = new FileSystemType - { - Bootable = !ArrayHelpers.ArrayIsNullOrEmpty(supblk.s_boot_segment), - Clusters = supblk.s_nzones, - ClusterSize = supblk.s_zone_size, - Type = "Xia filesystem" - }; - - information = sb.ToString(); - } - - /// Xia superblock - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct SuperBlock - { - /// 1st sector reserved for boot - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)] - public readonly byte[] s_boot_segment; - /// the name says it - public readonly uint s_zone_size; - /// volume size, zone aligned - public readonly uint s_nzones; - /// # of inodes - public readonly uint s_ninodes; - /// # of data zones - public readonly uint s_ndatazones; - /// # of imap zones - public readonly uint s_imap_zones; - /// # of zmap zones - public readonly uint s_zmap_zones; - /// first data zone - public readonly uint s_firstdatazone; - /// z size = 1KB << z shift - public readonly uint s_zone_shift; - /// max size of a single file - public readonly uint s_max_size; - /// reserved - public readonly uint s_reserved0; - /// reserved - public readonly uint s_reserved1; - /// reserved - public readonly uint s_reserved2; - /// reserved - public readonly uint s_reserved3; - /// first kernel zone - public readonly uint s_firstkernzone; - /// kernel size in zones - public readonly uint s_kernzones; - /// magic number for xiafs - public readonly uint s_magic; - } - - /// Xia directory entry - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DirectoryEntry - { - public readonly uint d_ino; - public readonly ushort d_rec_len; - public readonly byte d_name_len; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = XIAFS_NAME_LEN + 1)] - public readonly byte[] d_name; - } - - /// Xia inode - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Inode - { - public readonly ushort i_mode; - public readonly ushort i_nlinks; - public readonly ushort i_uid; - public readonly ushort i_gid; - public readonly uint i_size; - public readonly uint i_ctime; - public readonly uint i_atime; - public readonly uint i_mtime; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = XIAFS_NUM_BLOCK_POINTERS)] - public readonly uint[] i_zone; - } + /// Xia inode + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Inode + { + public readonly ushort i_mode; + public readonly ushort i_nlinks; + public readonly ushort i_uid; + public readonly ushort i_gid; + public readonly uint i_size; + public readonly uint i_ctime; + public readonly uint i_atime; + public readonly uint i_mtime; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = XIAFS_NUM_BLOCK_POINTERS)] + public readonly uint[] i_zone; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ZFS.cs b/Aaru.Filesystems/ZFS.cs index 57f399f47..adb76db0e 100644 --- a/Aaru.Filesystems/ZFS.cs +++ b/Aaru.Filesystems/ZFS.cs @@ -41,799 +41,798 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/* + * The ZFS on-disk structure is quite undocumented, so this has been checked using several test images and reading the comments and headers (but not the code) + * of ZFS-On-Linux. + * + * The most basic structure, the vdev label, is as follows: + * 8KiB of blank space + * 8KiB reserved for boot code, stored as a ZIO block with magic and checksum + * 112KiB of nvlist, usually encoded using XDR + * 128KiB of copies of the 1KiB uberblock + * + * Two vdev labels, L0 and L1 are stored at the start of the vdev. + * Another two, L2 and L3 are stored at the end. + * + * The nvlist is nothing more than a double linked list of name/value pairs where name is a string and value is an arbitrary type (and can be an array of it). + * On-disk they are stored sequentially (no pointers) and can be encoded in XDR (an old Sun serialization method that stores everything as 4 bytes chunks) or + * natively (that is as the host natively stores that values, for example on Intel an extended float would be 10 bytes (80 bit). + * It can also be encoded little or big endian. + * Because of this variations, ZFS stored a header indicating the used encoding and endianess before the encoded nvlist. + */ +/// +/// Implements detection for the Zettabyte File System (ZFS) +[SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "UnusedType.Local"), + SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "NotAccessedField.Local")] +public sealed class ZFS : IFilesystem { - /* - * The ZFS on-disk structure is quite undocumented, so this has been checked using several test images and reading the comments and headers (but not the code) - * of ZFS-On-Linux. - * - * The most basic structure, the vdev label, is as follows: - * 8KiB of blank space - * 8KiB reserved for boot code, stored as a ZIO block with magic and checksum - * 112KiB of nvlist, usually encoded using XDR - * 128KiB of copies of the 1KiB uberblock - * - * Two vdev labels, L0 and L1 are stored at the start of the vdev. - * Another two, L2 and L3 are stored at the end. - * - * The nvlist is nothing more than a double linked list of name/value pairs where name is a string and value is an arbitrary type (and can be an array of it). - * On-disk they are stored sequentially (no pointers) and can be encoded in XDR (an old Sun serialization method that stores everything as 4 bytes chunks) or - * natively (that is as the host natively stores that values, for example on Intel an extended float would be 10 bytes (80 bit). - * It can also be encoded little or big endian. - * Because of this variations, ZFS stored a header indicating the used encoding and endianess before the encoded nvlist. - */ + const ulong ZEC_MAGIC = 0x0210DA7AB10C7A11; + const ulong ZEC_CIGAM = 0x117A0CB17ADA1002; + + // These parameters define how the nvlist is stored + const byte NVS_LITTLE_ENDIAN = 1; + const byte NVS_BIG_ENDIAN = 0; + const byte NVS_NATIVE = 0; + const byte NVS_XDR = 1; + + const ulong UBERBLOCK_MAGIC = 0x00BAB10C; + + const uint ZFS_MAGIC = 0x58465342; + /// - /// Implements detection for the Zettabyte File System (ZFS) - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "UnusedType.Local"), - SuppressMessage("ReSharper", "UnusedMember.Local"), SuppressMessage("ReSharper", "NotAccessedField.Local")] - public sealed class ZFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "ZFS Filesystem Plugin"; + /// + public Guid Id => new("0750014F-A714-4692-A369-E23F6EC3659C"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const ulong ZEC_MAGIC = 0x0210DA7AB10C7A11; - const ulong ZEC_CIGAM = 0x117A0CB17ADA1002; + if(imagePlugin.Info.SectorSize < 512) + return false; - // These parameters define how the nvlist is stored - const byte NVS_LITTLE_ENDIAN = 1; - const byte NVS_BIG_ENDIAN = 0; - const byte NVS_NATIVE = 0; - const byte NVS_XDR = 1; + byte[] sector; + ulong magic; + ErrorNumber errno; - const ulong UBERBLOCK_MAGIC = 0x00BAB10C; - - const uint ZFS_MAGIC = 0x58465342; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "ZFS Filesystem Plugin"; - /// - public Guid Id => new("0750014F-A714-4692-A369-E23F6EC3659C"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(partition.Start + 31 < partition.End) { - if(imagePlugin.Info.SectorSize < 512) - return false; - - byte[] sector; - ulong magic; - ErrorNumber errno; - - if(partition.Start + 31 < partition.End) - { - errno = imagePlugin.ReadSector(partition.Start + 31, out sector); - - if(errno != ErrorNumber.NoError) - return false; - - magic = BitConverter.ToUInt64(sector, 0x1D8); - - if(magic == ZEC_MAGIC || - magic == ZEC_CIGAM) - return true; - } - - if(partition.Start + 16 >= partition.End) - return false; - - errno = imagePlugin.ReadSector(partition.Start + 16, out sector); + errno = imagePlugin.ReadSector(partition.Start + 31, out sector); if(errno != ErrorNumber.NoError) return false; magic = BitConverter.ToUInt64(sector, 0x1D8); - return magic == ZEC_MAGIC || magic == ZEC_CIGAM; + if(magic == ZEC_MAGIC || + magic == ZEC_CIGAM) + return true; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + if(partition.Start + 16 >= partition.End) + return false; + + errno = imagePlugin.ReadSector(partition.Start + 16, out sector); + + if(errno != ErrorNumber.NoError) + return false; + + magic = BitConverter.ToUInt64(sector, 0x1D8); + + return magic == ZEC_MAGIC || magic == ZEC_CIGAM; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + // ZFS is always UTF-8 + Encoding = Encoding.UTF8; + information = ""; + ErrorNumber errno; + + if(imagePlugin.Info.SectorSize < 512) + return; + + byte[] sector; + ulong magic; + + ulong nvlistOff = 32; + uint nvlistLen = 114688 / imagePlugin.Info.SectorSize; + + if(partition.Start + 31 < partition.End) { - // ZFS is always UTF-8 - Encoding = Encoding.UTF8; - information = ""; - ErrorNumber errno; - - if(imagePlugin.Info.SectorSize < 512) - return; - - byte[] sector; - ulong magic; - - ulong nvlistOff = 32; - uint nvlistLen = 114688 / imagePlugin.Info.SectorSize; - - if(partition.Start + 31 < partition.End) - { - errno = imagePlugin.ReadSector(partition.Start + 31, out sector); - - if(errno != ErrorNumber.NoError) - return; - - magic = BitConverter.ToUInt64(sector, 0x1D8); - - if(magic == ZEC_MAGIC || - magic == ZEC_CIGAM) - nvlistOff = 32; - } - - if(partition.Start + 16 < partition.End) - { - errno = imagePlugin.ReadSector(partition.Start + 16, out sector); - - if(errno != ErrorNumber.NoError) - return; - - magic = BitConverter.ToUInt64(sector, 0x1D8); - - if(magic == ZEC_MAGIC || - magic == ZEC_CIGAM) - nvlistOff = 17; - } - - var sb = new StringBuilder(); - sb.AppendLine("ZFS filesystem"); - - errno = imagePlugin.ReadSectors(partition.Start + nvlistOff, nvlistLen, out byte[] nvlist); + errno = imagePlugin.ReadSector(partition.Start + 31, out sector); if(errno != ErrorNumber.NoError) return; - sb.AppendLine(!DecodeNvList(nvlist, out Dictionary decodedNvList) - ? "Could not decode nvlist" : PrintNvList(decodedNvList)); + magic = BitConverter.ToUInt64(sector, 0x1D8); - information = sb.ToString(); - - XmlFsType = new FileSystemType - { - Type = "ZFS filesystem" - }; - - if(decodedNvList.TryGetValue("name", out NVS_Item tmpObj)) - XmlFsType.VolumeName = (string)tmpObj.value; - - if(decodedNvList.TryGetValue("guid", out tmpObj)) - XmlFsType.VolumeSerial = $"{(ulong)tmpObj.value}"; - - if(decodedNvList.TryGetValue("pool_guid", out tmpObj)) - XmlFsType.VolumeSetIdentifier = $"{(ulong)tmpObj.value}"; + if(magic == ZEC_MAGIC || + magic == ZEC_CIGAM) + nvlistOff = 32; } - static bool DecodeNvList(byte[] nvlist, out Dictionary decodedNvList) + if(partition.Start + 16 < partition.End) { - byte[] tmp = new byte[nvlist.Length - 4]; - Array.Copy(nvlist, 4, tmp, 0, nvlist.Length - 4); - bool xdr = nvlist[0] == 1; - bool littleEndian = nvlist[1] == 1; + errno = imagePlugin.ReadSector(partition.Start + 16, out sector); - return DecodeNvList(tmp, out decodedNvList, xdr, littleEndian); + if(errno != ErrorNumber.NoError) + return; + + magic = BitConverter.ToUInt64(sector, 0x1D8); + + if(magic == ZEC_MAGIC || + magic == ZEC_CIGAM) + nvlistOff = 17; } - // TODO: Decode native nvlist - static bool DecodeNvList(byte[] nvlist, out Dictionary decodedNvList, bool xdr, - bool littleEndian) + var sb = new StringBuilder(); + sb.AppendLine("ZFS filesystem"); + + errno = imagePlugin.ReadSectors(partition.Start + nvlistOff, nvlistLen, out byte[] nvlist); + + if(errno != ErrorNumber.NoError) + return; + + sb.AppendLine(!DecodeNvList(nvlist, out Dictionary decodedNvList) + ? "Could not decode nvlist" : PrintNvList(decodedNvList)); + + information = sb.ToString(); + + XmlFsType = new FileSystemType { - decodedNvList = new Dictionary(); + Type = "ZFS filesystem" + }; - if(nvlist == null || - nvlist.Length < 16) - return false; + if(decodedNvList.TryGetValue("name", out NVS_Item tmpObj)) + XmlFsType.VolumeName = (string)tmpObj.value; - if(!xdr) - return false; + if(decodedNvList.TryGetValue("guid", out tmpObj)) + XmlFsType.VolumeSerial = $"{(ulong)tmpObj.value}"; - int offset = 8; + if(decodedNvList.TryGetValue("pool_guid", out tmpObj)) + XmlFsType.VolumeSetIdentifier = $"{(ulong)tmpObj.value}"; + } - while(offset < nvlist.Length) + static bool DecodeNvList(byte[] nvlist, out Dictionary decodedNvList) + { + byte[] tmp = new byte[nvlist.Length - 4]; + Array.Copy(nvlist, 4, tmp, 0, nvlist.Length - 4); + bool xdr = nvlist[0] == 1; + bool littleEndian = nvlist[1] == 1; + + return DecodeNvList(tmp, out decodedNvList, xdr, littleEndian); + } + + // TODO: Decode native nvlist + static bool DecodeNvList(byte[] nvlist, out Dictionary decodedNvList, bool xdr, + bool littleEndian) + { + decodedNvList = new Dictionary(); + + if(nvlist == null || + nvlist.Length < 16) + return false; + + if(!xdr) + return false; + + int offset = 8; + + while(offset < nvlist.Length) + { + var item = new NVS_Item(); + int currOff = offset; + + item.encodedSize = BigEndianBitConverter.ToUInt32(nvlist, offset); + + // Finished + if(item.encodedSize == 0) + break; + + offset += 4; + item.decodedSize = BigEndianBitConverter.ToUInt32(nvlist, offset); + offset += 4; + uint nameLength = BigEndianBitConverter.ToUInt32(nvlist, offset); + offset += 4; + + if(nameLength % 4 > 0) + nameLength += 4 - (nameLength % 4); + + byte[] nameBytes = new byte[nameLength]; + Array.Copy(nvlist, offset, nameBytes, 0, nameLength); + item.name = StringHandlers.CToString(nameBytes); + offset += (int)nameLength; + item.dataType = (NVS_DataTypes)BigEndianBitConverter.ToUInt32(nvlist, offset); + offset += 4; + item.elements = BigEndianBitConverter.ToUInt32(nvlist, offset); + offset += 4; + + if(item.elements == 0) { - var item = new NVS_Item(); - int currOff = offset; + decodedNvList.Add(item.name, item); - item.encodedSize = BigEndianBitConverter.ToUInt32(nvlist, offset); + continue; + } - // Finished - if(item.encodedSize == 0) - break; + switch(item.dataType) + { + case NVS_DataTypes.DATA_TYPE_BOOLEAN: + case NVS_DataTypes.DATA_TYPE_BOOLEAN_ARRAY: + case NVS_DataTypes.DATA_TYPE_BOOLEAN_VALUE: + if(item.elements > 1) + { + bool[] boolArray = new bool[item.elements]; - offset += 4; - item.decodedSize = BigEndianBitConverter.ToUInt32(nvlist, offset); - offset += 4; - uint nameLength = BigEndianBitConverter.ToUInt32(nvlist, offset); - offset += 4; - - if(nameLength % 4 > 0) - nameLength += 4 - (nameLength % 4); - - byte[] nameBytes = new byte[nameLength]; - Array.Copy(nvlist, offset, nameBytes, 0, nameLength); - item.name = StringHandlers.CToString(nameBytes); - offset += (int)nameLength; - item.dataType = (NVS_DataTypes)BigEndianBitConverter.ToUInt32(nvlist, offset); - offset += 4; - item.elements = BigEndianBitConverter.ToUInt32(nvlist, offset); - offset += 4; - - if(item.elements == 0) - { - decodedNvList.Add(item.name, item); - - continue; - } - - switch(item.dataType) - { - case NVS_DataTypes.DATA_TYPE_BOOLEAN: - case NVS_DataTypes.DATA_TYPE_BOOLEAN_ARRAY: - case NVS_DataTypes.DATA_TYPE_BOOLEAN_VALUE: - if(item.elements > 1) - { - bool[] boolArray = new bool[item.elements]; - - for(int i = 0; i < item.elements; i++) - { - uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset); - boolArray[i] = temp > 0; - offset += 4; - } - - item.value = boolArray; - } - else + for(int i = 0; i < item.elements; i++) { uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset); - item.value = temp > 0; - offset += 4; + boolArray[i] = temp > 0; + offset += 4; } - break; - case NVS_DataTypes.DATA_TYPE_BYTE: - case NVS_DataTypes.DATA_TYPE_BYTE_ARRAY: - case NVS_DataTypes.DATA_TYPE_UINT8: - case NVS_DataTypes.DATA_TYPE_UINT8_ARRAY: - if(item.elements > 1) - { - byte[] byteArray = new byte[item.elements]; - Array.Copy(nvlist, offset, byteArray, 0, item.elements); - offset += (int)item.elements; + item.value = boolArray; + } + else + { + uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset); + item.value = temp > 0; + offset += 4; + } - if(item.elements % 4 > 0) - offset += 4 - (int)(item.elements % 4); + break; + case NVS_DataTypes.DATA_TYPE_BYTE: + case NVS_DataTypes.DATA_TYPE_BYTE_ARRAY: + case NVS_DataTypes.DATA_TYPE_UINT8: + case NVS_DataTypes.DATA_TYPE_UINT8_ARRAY: + if(item.elements > 1) + { + byte[] byteArray = new byte[item.elements]; + Array.Copy(nvlist, offset, byteArray, 0, item.elements); + offset += (int)item.elements; - item.value = byteArray; - } - else + if(item.elements % 4 > 0) + offset += 4 - (int)(item.elements % 4); + + item.value = byteArray; + } + else + { + item.value = nvlist[offset]; + offset += 4; + } + + break; + case NVS_DataTypes.DATA_TYPE_DOUBLE: + if(item.elements > 1) + { + double[] doubleArray = new double[item.elements]; + + for(int i = 0; i < item.elements; i++) { - item.value = nvlist[offset]; - offset += 4; + double temp = BigEndianBitConverter.ToDouble(nvlist, offset); + doubleArray[i] = temp; + offset += 8; } - break; - case NVS_DataTypes.DATA_TYPE_DOUBLE: - if(item.elements > 1) + item.value = doubleArray; + } + else + { + item.value = BigEndianBitConverter.ToDouble(nvlist, offset); + offset += 8; + } + + break; + case NVS_DataTypes.DATA_TYPE_HRTIME: + if(item.elements > 1) + { + DateTime[] hrtimeArray = new DateTime[item.elements]; + + for(int i = 0; i < item.elements; i++) { - double[] doubleArray = new double[item.elements]; - - for(int i = 0; i < item.elements; i++) - { - double temp = BigEndianBitConverter.ToDouble(nvlist, offset); - doubleArray[i] = temp; - offset += 8; - } - - item.value = doubleArray; - } - else - { - item.value = BigEndianBitConverter.ToDouble(nvlist, offset); - offset += 8; - } - - break; - case NVS_DataTypes.DATA_TYPE_HRTIME: - if(item.elements > 1) - { - DateTime[] hrtimeArray = new DateTime[item.elements]; - - for(int i = 0; i < item.elements; i++) - { - DateTime temp = - DateHandlers.UnixHrTimeToDateTime(BigEndianBitConverter.ToUInt64(nvlist, offset)); - - hrtimeArray[i] = temp; - offset += 8; - } - - item.value = hrtimeArray; - } - else - { - item.value = + DateTime temp = DateHandlers.UnixHrTimeToDateTime(BigEndianBitConverter.ToUInt64(nvlist, offset)); - offset += 8; + hrtimeArray[i] = temp; + offset += 8; } - break; - case NVS_DataTypes.DATA_TYPE_INT16: - case NVS_DataTypes.DATA_TYPE_INT16_ARRAY: - if(item.elements > 1) + item.value = hrtimeArray; + } + else + { + item.value = + DateHandlers.UnixHrTimeToDateTime(BigEndianBitConverter.ToUInt64(nvlist, offset)); + + offset += 8; + } + + break; + case NVS_DataTypes.DATA_TYPE_INT16: + case NVS_DataTypes.DATA_TYPE_INT16_ARRAY: + if(item.elements > 1) + { + short[] shortArray = new short[item.elements]; + + for(int i = 0; i < item.elements; i++) { - short[] shortArray = new short[item.elements]; - - for(int i = 0; i < item.elements; i++) - { - short temp = BigEndianBitConverter.ToInt16(nvlist, offset); - shortArray[i] = temp; - offset += 4; - } - - item.value = shortArray; + short temp = BigEndianBitConverter.ToInt16(nvlist, offset); + shortArray[i] = temp; + offset += 4; } - else + + item.value = shortArray; + } + else + { + item.value = BigEndianBitConverter.ToInt16(nvlist, offset); + offset += 4; + } + + break; + case NVS_DataTypes.DATA_TYPE_INT32: + case NVS_DataTypes.DATA_TYPE_INT32_ARRAY: + if(item.elements > 1) + { + int[] intArray = new int[item.elements]; + + for(int i = 0; i < item.elements; i++) { - item.value = BigEndianBitConverter.ToInt16(nvlist, offset); - offset += 4; + int temp = BigEndianBitConverter.ToInt32(nvlist, offset); + intArray[i] = temp; + offset += 4; } - break; - case NVS_DataTypes.DATA_TYPE_INT32: - case NVS_DataTypes.DATA_TYPE_INT32_ARRAY: - if(item.elements > 1) + item.value = intArray; + } + else + { + item.value = BigEndianBitConverter.ToInt32(nvlist, offset); + offset += 4; + } + + break; + case NVS_DataTypes.DATA_TYPE_INT64: + case NVS_DataTypes.DATA_TYPE_INT64_ARRAY: + if(item.elements > 1) + { + long[] longArray = new long[item.elements]; + + for(int i = 0; i < item.elements; i++) { - int[] intArray = new int[item.elements]; - - for(int i = 0; i < item.elements; i++) - { - int temp = BigEndianBitConverter.ToInt32(nvlist, offset); - intArray[i] = temp; - offset += 4; - } - - item.value = intArray; + long temp = BigEndianBitConverter.ToInt64(nvlist, offset); + longArray[i] = temp; + offset += 8; } - else + + item.value = longArray; + } + else + { + item.value = BigEndianBitConverter.ToInt64(nvlist, offset); + offset += 8; + } + + break; + case NVS_DataTypes.DATA_TYPE_INT8: + case NVS_DataTypes.DATA_TYPE_INT8_ARRAY: + if(item.elements > 1) + { + sbyte[] sbyteArray = new sbyte[item.elements]; + + for(int i = 0; i < item.elements; i++) { - item.value = BigEndianBitConverter.ToInt32(nvlist, offset); - offset += 4; + sbyte temp = (sbyte)nvlist[offset]; + sbyteArray[i] = temp; + offset++; } - break; - case NVS_DataTypes.DATA_TYPE_INT64: - case NVS_DataTypes.DATA_TYPE_INT64_ARRAY: - if(item.elements > 1) - { - long[] longArray = new long[item.elements]; + item.value = sbyteArray; - for(int i = 0; i < item.elements; i++) - { - long temp = BigEndianBitConverter.ToInt64(nvlist, offset); - longArray[i] = temp; - offset += 8; - } + if(sbyteArray.Length % 4 > 0) + offset += 4 - (sbyteArray.Length % 4); + } + else + { + item.value = BigEndianBitConverter.ToInt64(nvlist, offset); + offset += 4; + } - item.value = longArray; - } - else - { - item.value = BigEndianBitConverter.ToInt64(nvlist, offset); - offset += 8; - } + break; + case NVS_DataTypes.DATA_TYPE_STRING: + case NVS_DataTypes.DATA_TYPE_STRING_ARRAY: + if(item.elements > 1) + { + string[] stringArray = new string[item.elements]; - break; - case NVS_DataTypes.DATA_TYPE_INT8: - case NVS_DataTypes.DATA_TYPE_INT8_ARRAY: - if(item.elements > 1) - { - sbyte[] sbyteArray = new sbyte[item.elements]; - - for(int i = 0; i < item.elements; i++) - { - sbyte temp = (sbyte)nvlist[offset]; - sbyteArray[i] = temp; - offset++; - } - - item.value = sbyteArray; - - if(sbyteArray.Length % 4 > 0) - offset += 4 - (sbyteArray.Length % 4); - } - else - { - item.value = BigEndianBitConverter.ToInt64(nvlist, offset); - offset += 4; - } - - break; - case NVS_DataTypes.DATA_TYPE_STRING: - case NVS_DataTypes.DATA_TYPE_STRING_ARRAY: - if(item.elements > 1) - { - string[] stringArray = new string[item.elements]; - - for(int i = 0; i < item.elements; i++) - { - uint strLength = BigEndianBitConverter.ToUInt32(nvlist, offset); - offset += 4; - byte[] strBytes = new byte[strLength]; - Array.Copy(nvlist, offset, strBytes, 0, strLength); - stringArray[i] = StringHandlers.CToString(strBytes); - offset += (int)strLength; - - if(strLength % 4 > 0) - offset += 4 - (int)(strLength % 4); - } - - item.value = stringArray; - } - else + for(int i = 0; i < item.elements; i++) { uint strLength = BigEndianBitConverter.ToUInt32(nvlist, offset); offset += 4; byte[] strBytes = new byte[strLength]; Array.Copy(nvlist, offset, strBytes, 0, strLength); - item.value = StringHandlers.CToString(strBytes); - offset += (int)strLength; + stringArray[i] = StringHandlers.CToString(strBytes); + offset += (int)strLength; if(strLength % 4 > 0) offset += 4 - (int)(strLength % 4); } - break; - case NVS_DataTypes.DATA_TYPE_UINT16: - case NVS_DataTypes.DATA_TYPE_UINT16_ARRAY: - if(item.elements > 1) - { - ushort[] ushortArray = new ushort[item.elements]; + item.value = stringArray; + } + else + { + uint strLength = BigEndianBitConverter.ToUInt32(nvlist, offset); + offset += 4; + byte[] strBytes = new byte[strLength]; + Array.Copy(nvlist, offset, strBytes, 0, strLength); + item.value = StringHandlers.CToString(strBytes); + offset += (int)strLength; - for(int i = 0; i < item.elements; i++) - { - ushort temp = BigEndianBitConverter.ToUInt16(nvlist, offset); - ushortArray[i] = temp; - offset += 4; - } + if(strLength % 4 > 0) + offset += 4 - (int)(strLength % 4); + } - item.value = ushortArray; - } - else + break; + case NVS_DataTypes.DATA_TYPE_UINT16: + case NVS_DataTypes.DATA_TYPE_UINT16_ARRAY: + if(item.elements > 1) + { + ushort[] ushortArray = new ushort[item.elements]; + + for(int i = 0; i < item.elements; i++) { - item.value = BigEndianBitConverter.ToUInt16(nvlist, offset); - offset += 4; + ushort temp = BigEndianBitConverter.ToUInt16(nvlist, offset); + ushortArray[i] = temp; + offset += 4; } - break; - case NVS_DataTypes.DATA_TYPE_UINT32: - case NVS_DataTypes.DATA_TYPE_UINT32_ARRAY: - if(item.elements > 1) - { - uint[] uintArray = new uint[item.elements]; + item.value = ushortArray; + } + else + { + item.value = BigEndianBitConverter.ToUInt16(nvlist, offset); + offset += 4; + } - for(int i = 0; i < item.elements; i++) - { - uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset); - uintArray[i] = temp; - offset += 4; - } + break; + case NVS_DataTypes.DATA_TYPE_UINT32: + case NVS_DataTypes.DATA_TYPE_UINT32_ARRAY: + if(item.elements > 1) + { + uint[] uintArray = new uint[item.elements]; - item.value = uintArray; - } - else + for(int i = 0; i < item.elements; i++) { - item.value = BigEndianBitConverter.ToUInt32(nvlist, offset); - offset += 4; + uint temp = BigEndianBitConverter.ToUInt32(nvlist, offset); + uintArray[i] = temp; + offset += 4; } - break; - case NVS_DataTypes.DATA_TYPE_UINT64: - case NVS_DataTypes.DATA_TYPE_UINT64_ARRAY: - if(item.elements > 1) - { - ulong[] ulongArray = new ulong[item.elements]; + item.value = uintArray; + } + else + { + item.value = BigEndianBitConverter.ToUInt32(nvlist, offset); + offset += 4; + } - for(int i = 0; i < item.elements; i++) - { - ulong temp = BigEndianBitConverter.ToUInt64(nvlist, offset); - ulongArray[i] = temp; - offset += 8; - } + break; + case NVS_DataTypes.DATA_TYPE_UINT64: + case NVS_DataTypes.DATA_TYPE_UINT64_ARRAY: + if(item.elements > 1) + { + ulong[] ulongArray = new ulong[item.elements]; - item.value = ulongArray; - } - else + for(int i = 0; i < item.elements; i++) { - item.value = BigEndianBitConverter.ToUInt64(nvlist, offset); - offset += 8; + ulong temp = BigEndianBitConverter.ToUInt64(nvlist, offset); + ulongArray[i] = temp; + offset += 8; } - break; - case NVS_DataTypes.DATA_TYPE_NVLIST: - if(item.elements > 1) - goto default; + item.value = ulongArray; + } + else + { + item.value = BigEndianBitConverter.ToUInt64(nvlist, offset); + offset += 8; + } - byte[] subListBytes = new byte[item.encodedSize - (offset - currOff)]; - Array.Copy(nvlist, offset, subListBytes, 0, subListBytes.Length); + break; + case NVS_DataTypes.DATA_TYPE_NVLIST: + if(item.elements > 1) + goto default; - if(DecodeNvList(subListBytes, out Dictionary subList, true, littleEndian)) - item.value = subList; - else - goto default; + byte[] subListBytes = new byte[item.encodedSize - (offset - currOff)]; + Array.Copy(nvlist, offset, subListBytes, 0, subListBytes.Length); - offset = (int)(currOff + item.encodedSize); + if(DecodeNvList(subListBytes, out Dictionary subList, true, littleEndian)) + item.value = subList; + else + goto default; - break; - default: - byte[] unknown = new byte[item.encodedSize - (offset - currOff)]; - Array.Copy(nvlist, offset, unknown, 0, unknown.Length); - item.value = unknown; - offset = (int)(currOff + item.encodedSize); + offset = (int)(currOff + item.encodedSize); - break; - } + break; + default: + byte[] unknown = new byte[item.encodedSize - (offset - currOff)]; + Array.Copy(nvlist, offset, unknown, 0, unknown.Length); + item.value = unknown; + offset = (int)(currOff + item.encodedSize); - decodedNvList.Add(item.name, item); + break; } - return decodedNvList.Count > 0; + decodedNvList.Add(item.name, item); } - static string PrintNvList(Dictionary decodedNvList) - { - var sb = new StringBuilder(); + return decodedNvList.Count > 0; + } - foreach(NVS_Item item in decodedNvList.Values) + static string PrintNvList(Dictionary decodedNvList) + { + var sb = new StringBuilder(); + + foreach(NVS_Item item in decodedNvList.Values) + { + if(item.elements == 0) { - if(item.elements == 0) - { - sb.AppendFormat("{0} is not set", item.name).AppendLine(); + sb.AppendFormat("{0} is not set", item.name).AppendLine(); - continue; - } - - switch(item.dataType) - { - case NVS_DataTypes.DATA_TYPE_BOOLEAN: - case NVS_DataTypes.DATA_TYPE_BOOLEAN_ARRAY: - case NVS_DataTypes.DATA_TYPE_BOOLEAN_VALUE: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((bool[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (bool)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_BYTE: - case NVS_DataTypes.DATA_TYPE_BYTE_ARRAY: - case NVS_DataTypes.DATA_TYPE_UINT8: - case NVS_DataTypes.DATA_TYPE_UINT8_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((byte[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (byte)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_DOUBLE: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((double[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (double)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_HRTIME: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((DateTime[])item.value)[i]). - AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (DateTime)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_INT16: - case NVS_DataTypes.DATA_TYPE_INT16_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((short[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (short)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_INT32: - case NVS_DataTypes.DATA_TYPE_INT32_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((int[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (int)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_INT64: - case NVS_DataTypes.DATA_TYPE_INT64_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((long[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (long)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_INT8: - case NVS_DataTypes.DATA_TYPE_INT8_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((sbyte[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (sbyte)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_STRING: - case NVS_DataTypes.DATA_TYPE_STRING_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((string[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (string)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_UINT16: - case NVS_DataTypes.DATA_TYPE_UINT16_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((ushort[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (ushort)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_UINT32: - case NVS_DataTypes.DATA_TYPE_UINT32_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((uint[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (uint)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_UINT64: - case NVS_DataTypes.DATA_TYPE_UINT64_ARRAY: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((ulong[])item.value)[i]).AppendLine(); - else - sb.AppendFormat("{0} = {1}", item.name, (ulong)item.value).AppendLine(); - - break; - case NVS_DataTypes.DATA_TYPE_NVLIST: - if(item.elements == 1) - sb.AppendFormat("{0} =\n{1}", item.name, - PrintNvList((Dictionary)item.value)).AppendLine(); - else - sb.AppendFormat("{0} = {1} elements nvlist[], unable to print", item.name, item.elements). - AppendLine(); - - break; - default: - if(item.elements > 1) - for(int i = 0; i < item.elements; i++) - sb.AppendFormat("{0}[{1}] = Unknown data type {2}", item.name, i, item.dataType). - AppendLine(); - else - sb.AppendFormat("{0} = Unknown data type {1}", item.name, item.dataType).AppendLine(); - - break; - } + continue; } - return sb.ToString(); + switch(item.dataType) + { + case NVS_DataTypes.DATA_TYPE_BOOLEAN: + case NVS_DataTypes.DATA_TYPE_BOOLEAN_ARRAY: + case NVS_DataTypes.DATA_TYPE_BOOLEAN_VALUE: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((bool[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (bool)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_BYTE: + case NVS_DataTypes.DATA_TYPE_BYTE_ARRAY: + case NVS_DataTypes.DATA_TYPE_UINT8: + case NVS_DataTypes.DATA_TYPE_UINT8_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((byte[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (byte)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_DOUBLE: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((double[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (double)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_HRTIME: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((DateTime[])item.value)[i]). + AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (DateTime)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_INT16: + case NVS_DataTypes.DATA_TYPE_INT16_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((short[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (short)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_INT32: + case NVS_DataTypes.DATA_TYPE_INT32_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((int[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (int)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_INT64: + case NVS_DataTypes.DATA_TYPE_INT64_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((long[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (long)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_INT8: + case NVS_DataTypes.DATA_TYPE_INT8_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((sbyte[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (sbyte)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_STRING: + case NVS_DataTypes.DATA_TYPE_STRING_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((string[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (string)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_UINT16: + case NVS_DataTypes.DATA_TYPE_UINT16_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((ushort[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (ushort)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_UINT32: + case NVS_DataTypes.DATA_TYPE_UINT32_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((uint[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (uint)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_UINT64: + case NVS_DataTypes.DATA_TYPE_UINT64_ARRAY: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = {2}", item.name, i, ((ulong[])item.value)[i]).AppendLine(); + else + sb.AppendFormat("{0} = {1}", item.name, (ulong)item.value).AppendLine(); + + break; + case NVS_DataTypes.DATA_TYPE_NVLIST: + if(item.elements == 1) + sb.AppendFormat("{0} =\n{1}", item.name, + PrintNvList((Dictionary)item.value)).AppendLine(); + else + sb.AppendFormat("{0} = {1} elements nvlist[], unable to print", item.name, item.elements). + AppendLine(); + + break; + default: + if(item.elements > 1) + for(int i = 0; i < item.elements; i++) + sb.AppendFormat("{0}[{1}] = Unknown data type {2}", item.name, i, item.dataType). + AppendLine(); + else + sb.AppendFormat("{0} = Unknown data type {1}", item.name, item.dataType).AppendLine(); + + break; + } } - struct ZIO_Checksum - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public ulong[] word; - } + return sb.ToString(); + } - /// - /// There is an empty ZIO at sector 16 or sector 31, with magic and checksum, to detect it is really ZFS I - /// suppose. - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ZIO_Empty - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 472)] - public readonly byte[] empty; - public readonly ulong magic; - public readonly ZIO_Checksum checksum; - } + struct ZIO_Checksum + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public ulong[] word; + } - /// This structure indicates which encoding method and endianness is used to encode the nvlist - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct NVS_Method - { - public readonly byte encoding; - public readonly byte endian; - public readonly byte reserved1; - public readonly byte reserved2; - } + /// + /// There is an empty ZIO at sector 16 or sector 31, with magic and checksum, to detect it is really ZFS I + /// suppose. + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ZIO_Empty + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 472)] + public readonly byte[] empty; + public readonly ulong magic; + public readonly ZIO_Checksum checksum; + } - /// This structure gives information about the encoded nvlist - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct NVS_XDR_Header - { - public readonly NVS_Method encodingAndEndian; - public readonly uint version; - public readonly uint flags; - } + /// This structure indicates which encoding method and endianness is used to encode the nvlist + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct NVS_Method + { + public readonly byte encoding; + public readonly byte endian; + public readonly byte reserved1; + public readonly byte reserved2; + } - enum NVS_DataTypes : uint - { - DATA_TYPE_UNKNOWN = 0, DATA_TYPE_BOOLEAN, DATA_TYPE_BYTE, - DATA_TYPE_INT16, DATA_TYPE_UINT16, DATA_TYPE_INT32, - DATA_TYPE_UINT32, DATA_TYPE_INT64, DATA_TYPE_UINT64, - DATA_TYPE_STRING, DATA_TYPE_BYTE_ARRAY, DATA_TYPE_INT16_ARRAY, - DATA_TYPE_UINT16_ARRAY, DATA_TYPE_INT32_ARRAY, DATA_TYPE_UINT32_ARRAY, - DATA_TYPE_INT64_ARRAY, DATA_TYPE_UINT64_ARRAY, DATA_TYPE_STRING_ARRAY, - DATA_TYPE_HRTIME, DATA_TYPE_NVLIST, DATA_TYPE_NVLIST_ARRAY, - DATA_TYPE_BOOLEAN_VALUE, DATA_TYPE_INT8, DATA_TYPE_UINT8, - DATA_TYPE_BOOLEAN_ARRAY, DATA_TYPE_INT8_ARRAY, DATA_TYPE_UINT8_ARRAY, - DATA_TYPE_DOUBLE - } + /// This structure gives information about the encoded nvlist + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct NVS_XDR_Header + { + public readonly NVS_Method encodingAndEndian; + public readonly uint version; + public readonly uint flags; + } - /// This represent an encoded nvpair (an item of an nvlist) - struct NVS_Item - { - /// Size in bytes when encoded in XDR - public uint encodedSize; - /// Size in bytes when decoded - public uint decodedSize; - /// On disk, it is null-padded for alignment to 4 bytes and prepended by a 4 byte length indicator - public string name; - /// Data type - public NVS_DataTypes dataType; - /// How many elements are here - public uint elements; - /// On disk size is relative to and always aligned to 4 bytes - public object value; - } + enum NVS_DataTypes : uint + { + DATA_TYPE_UNKNOWN = 0, DATA_TYPE_BOOLEAN, DATA_TYPE_BYTE, + DATA_TYPE_INT16, DATA_TYPE_UINT16, DATA_TYPE_INT32, + DATA_TYPE_UINT32, DATA_TYPE_INT64, DATA_TYPE_UINT64, + DATA_TYPE_STRING, DATA_TYPE_BYTE_ARRAY, DATA_TYPE_INT16_ARRAY, + DATA_TYPE_UINT16_ARRAY, DATA_TYPE_INT32_ARRAY, DATA_TYPE_UINT32_ARRAY, + DATA_TYPE_INT64_ARRAY, DATA_TYPE_UINT64_ARRAY, DATA_TYPE_STRING_ARRAY, + DATA_TYPE_HRTIME, DATA_TYPE_NVLIST, DATA_TYPE_NVLIST_ARRAY, + DATA_TYPE_BOOLEAN_VALUE, DATA_TYPE_INT8, DATA_TYPE_UINT8, + DATA_TYPE_BOOLEAN_ARRAY, DATA_TYPE_INT8_ARRAY, DATA_TYPE_UINT8_ARRAY, + DATA_TYPE_DOUBLE + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct DVA - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly ulong[] word; - } + /// This represent an encoded nvpair (an item of an nvlist) + struct NVS_Item + { + /// Size in bytes when encoded in XDR + public uint encodedSize; + /// Size in bytes when decoded + public uint decodedSize; + /// On disk, it is null-padded for alignment to 4 bytes and prepended by a 4 byte length indicator + public string name; + /// Data type + public NVS_DataTypes dataType; + /// How many elements are here + public uint elements; + /// On disk size is relative to and always aligned to 4 bytes + public object value; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct SPA_BlockPointer - { - /// Data virtual address - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly DVA[] dataVirtualAddress; - /// Block properties - public readonly ulong properties; - /// Reserved for future expansion - public readonly ulong[] padding; - /// TXG when block was allocated - public readonly ulong birthTxg; - /// Transaction group at birth - public readonly ulong birth; - /// Fill count - public readonly ulong fill; - public readonly ZIO_Checksum checksum; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DVA + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly ulong[] word; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ZFS_Uberblock - { - public readonly ulong magic; - public readonly ulong spaVersion; - public readonly ulong lastTxg; - public readonly ulong guidSum; - public readonly ulong timestamp; - public readonly SPA_BlockPointer mosPtr; - public readonly ulong softwareVersion; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct SPA_BlockPointer + { + /// Data virtual address + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly DVA[] dataVirtualAddress; + /// Block properties + public readonly ulong properties; + /// Reserved for future expansion + public readonly ulong[] padding; + /// TXG when block was allocated + public readonly ulong birthTxg; + /// Transaction group at birth + public readonly ulong birth; + /// Fill count + public readonly ulong fill; + public readonly ZIO_Checksum checksum; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ZFS_Uberblock + { + public readonly ulong magic; + public readonly ulong spaVersion; + public readonly ulong lastTxg; + public readonly ulong guidSum; + public readonly ulong timestamp; + public readonly SPA_BlockPointer mosPtr; + public readonly ulong softwareVersion; } } \ No newline at end of file diff --git a/Aaru.Filesystems/dump.cs b/Aaru.Filesystems/dump.cs index c5e832c5e..cfd4543de 100644 --- a/Aaru.Filesystems/dump.cs +++ b/Aaru.Filesystems/dump.cs @@ -43,394 +43,393 @@ using Schemas; using Marshal = Aaru.Helpers.Marshal; using ufs_daddr_t = System.Int32; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +/// +/// Implements identification of a dump(8) image (virtual filesystem on a file) +[SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class dump : IFilesystem { + /// Magic number for old dump + const ushort OFS_MAGIC = 60011; + /// Magic number for new dump + const uint NFS_MAGIC = 60012; + /// Magic number for AIX dump + const uint XIX_MAGIC = 60013; + /// Magic number for UFS2 dump + const uint UFS2_MAGIC = 0x19540119; + /// Magic number for old dump + const uint OFS_CIGAM = 0x6BEA0000; + /// Magic number for new dump + const uint NFS_CIGAM = 0x6CEA0000; + /// Magic number for AIX dump + const uint XIX_CIGAM = 0x6DEA0000; + /// Magic number for UFS2 dump + const uint UFS2_CIGAM = 0x19015419; + + const int TP_BSIZE = 1024; + + /// Dump tape header + const short TS_TAPE = 1; + /// Beginning of file record + const short TS_INODE = 2; + /// Map of inodes on tape + const short TS_BITS = 3; + /// Continuation of file record + const short TS_ADDR = 4; + /// Map of inodes deleted since last dump + const short TS_END = 5; + /// Inode bitmap + const short TS_CLRI = 6; + const short TS_ACL = 7; + const short TS_PCL = 8; + + const int TP_NINDIR = TP_BSIZE / 2; + const int LBLSIZE = 16; + const int NAMELEN = 64; + + const int NDADDR = 12; + const int NIADDR = 3; + /// - /// Implements identification of a dump(8) image (virtual filesystem on a file) - [SuppressMessage("ReSharper", "InconsistentNaming"), SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class dump : IFilesystem + public Encoding Encoding { get; private set; } + /// + public string Name => "dump(8) Plugin"; + /// + public Guid Id => new("E53B4D28-C858-4800-B092-DDAE80D361B9"); + /// + public FileSystemType XmlFsType { get; private set; } + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - /// Magic number for old dump - const ushort OFS_MAGIC = 60011; - /// Magic number for new dump - const uint NFS_MAGIC = 60012; - /// Magic number for AIX dump - const uint XIX_MAGIC = 60013; - /// Magic number for UFS2 dump - const uint UFS2_MAGIC = 0x19540119; - /// Magic number for old dump - const uint OFS_CIGAM = 0x6BEA0000; - /// Magic number for new dump - const uint NFS_CIGAM = 0x6CEA0000; - /// Magic number for AIX dump - const uint XIX_CIGAM = 0x6DEA0000; - /// Magic number for UFS2 dump - const uint UFS2_CIGAM = 0x19015419; + if(imagePlugin.Info.SectorSize < 512) + return false; - const int TP_BSIZE = 1024; + // It should be start of a tape or floppy or file + if(partition.Start != 0) + return false; - /// Dump tape header - const short TS_TAPE = 1; - /// Beginning of file record - const short TS_INODE = 2; - /// Map of inodes on tape - const short TS_BITS = 3; - /// Continuation of file record - const short TS_ADDR = 4; - /// Map of inodes deleted since last dump - const short TS_END = 5; - /// Inode bitmap - const short TS_CLRI = 6; - const short TS_ACL = 7; - const short TS_PCL = 8; + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - const int TP_NINDIR = TP_BSIZE / 2; - const int LBLSIZE = 16; - const int NAMELEN = 64; + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; - const int NDADDR = 12; - const int NIADDR = 3; + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "dump(8) Plugin"; - /// - public Guid Id => new("E53B4D28-C858-4800-B092-DDAE80D361B9"); - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Author => "Natalia Portillo"; + if(errno != ErrorNumber.NoError) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + if(sector.Length < Marshal.SizeOf()) + return false; + + spcl16 oldHdr = Marshal.ByteArrayToStructureLittleEndian(sector); + spcl_aix aixHdr = Marshal.ByteArrayToStructureLittleEndian(sector); + s_spcl newHdr = Marshal.ByteArrayToStructureLittleEndian(sector); + + AaruConsole.DebugWriteLine("dump(8) plugin", "old magic = 0x{0:X8}", oldHdr.c_magic); + AaruConsole.DebugWriteLine("dump(8) plugin", "aix magic = 0x{0:X8}", aixHdr.c_magic); + AaruConsole.DebugWriteLine("dump(8) plugin", "new magic = 0x{0:X8}", newHdr.c_magic); + + return oldHdr.c_magic == OFS_MAGIC || aixHdr.c_magic == XIX_MAGIC || aixHdr.c_magic == XIX_CIGAM || + newHdr.c_magic == OFS_MAGIC || newHdr.c_magic == NFS_MAGIC || newHdr.c_magic == OFS_CIGAM || + newHdr.c_magic == NFS_CIGAM || newHdr.c_magic == UFS2_MAGIC || newHdr.c_magic == UFS2_CIGAM; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + if(imagePlugin.Info.SectorSize < 512) + return; + + if(partition.Start != 0) + return; + + uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); + + if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) + sbSize++; + + ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); + + if(errno != ErrorNumber.NoError) + return; + + if(sector.Length < Marshal.SizeOf()) + return; + + spcl16 oldHdr = Marshal.ByteArrayToStructureLittleEndian(sector); + spcl_aix aixHdr = Marshal.ByteArrayToStructureLittleEndian(sector); + s_spcl newHdr = Marshal.ByteArrayToStructureLittleEndian(sector); + + bool useOld = false; + bool useAix = false; + + if(newHdr.c_magic == OFS_MAGIC || + newHdr.c_magic == NFS_MAGIC || + newHdr.c_magic == OFS_CIGAM || + newHdr.c_magic == NFS_CIGAM || + newHdr.c_magic == UFS2_MAGIC || + newHdr.c_magic == UFS2_CIGAM) { - if(imagePlugin.Info.SectorSize < 512) - return false; - - // It should be start of a tape or floppy or file - if(partition.Start != 0) - return false; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return false; - - if(sector.Length < Marshal.SizeOf()) - return false; - - spcl16 oldHdr = Marshal.ByteArrayToStructureLittleEndian(sector); - spcl_aix aixHdr = Marshal.ByteArrayToStructureLittleEndian(sector); - s_spcl newHdr = Marshal.ByteArrayToStructureLittleEndian(sector); - - AaruConsole.DebugWriteLine("dump(8) plugin", "old magic = 0x{0:X8}", oldHdr.c_magic); - AaruConsole.DebugWriteLine("dump(8) plugin", "aix magic = 0x{0:X8}", aixHdr.c_magic); - AaruConsole.DebugWriteLine("dump(8) plugin", "new magic = 0x{0:X8}", newHdr.c_magic); - - return oldHdr.c_magic == OFS_MAGIC || aixHdr.c_magic == XIX_MAGIC || aixHdr.c_magic == XIX_CIGAM || - newHdr.c_magic == OFS_MAGIC || newHdr.c_magic == NFS_MAGIC || newHdr.c_magic == OFS_CIGAM || - newHdr.c_magic == NFS_CIGAM || newHdr.c_magic == UFS2_MAGIC || newHdr.c_magic == UFS2_CIGAM; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - if(imagePlugin.Info.SectorSize < 512) - return; - - if(partition.Start != 0) - return; - - uint sbSize = (uint)(Marshal.SizeOf() / imagePlugin.Info.SectorSize); - - if(Marshal.SizeOf() % imagePlugin.Info.SectorSize != 0) - sbSize++; - - ErrorNumber errno = imagePlugin.ReadSectors(partition.Start, sbSize, out byte[] sector); - - if(errno != ErrorNumber.NoError) - return; - - if(sector.Length < Marshal.SizeOf()) - return; - - spcl16 oldHdr = Marshal.ByteArrayToStructureLittleEndian(sector); - spcl_aix aixHdr = Marshal.ByteArrayToStructureLittleEndian(sector); - s_spcl newHdr = Marshal.ByteArrayToStructureLittleEndian(sector); - - bool useOld = false; - bool useAix = false; - - if(newHdr.c_magic == OFS_MAGIC || - newHdr.c_magic == NFS_MAGIC || - newHdr.c_magic == OFS_CIGAM || - newHdr.c_magic == NFS_CIGAM || - newHdr.c_magic == UFS2_MAGIC || + if(newHdr.c_magic == OFS_CIGAM || + newHdr.c_magic == NFS_CIGAM || newHdr.c_magic == UFS2_CIGAM) - { - if(newHdr.c_magic == OFS_CIGAM || - newHdr.c_magic == NFS_CIGAM || - newHdr.c_magic == UFS2_CIGAM) - newHdr = Marshal.ByteArrayToStructureBigEndian(sector); - } - else if(aixHdr.c_magic == XIX_MAGIC || - aixHdr.c_magic == XIX_CIGAM) - { - useAix = true; - - if(aixHdr.c_magic == XIX_CIGAM) - aixHdr = Marshal.ByteArrayToStructureBigEndian(sector); - } - else if(oldHdr.c_magic == OFS_MAGIC) - { - useOld = true; - - // Swap PDP-11 endian - oldHdr.c_date = (int)Swapping.PDPFromLittleEndian((uint)oldHdr.c_date); - oldHdr.c_ddate = (int)Swapping.PDPFromLittleEndian((uint)oldHdr.c_ddate); - } - else - { - information = "Could not read dump(8) header block"; - - return; - } - - var sb = new StringBuilder(); - - XmlFsType = new FileSystemType - { - ClusterSize = 1024, - Clusters = partition.Size / 1024 - }; - - if(useOld) - { - XmlFsType.Type = "Old 16-bit dump(8)"; - sb.AppendLine(XmlFsType.Type); - - if(oldHdr.c_date > 0) - { - XmlFsType.CreationDate = DateHandlers.UnixToDateTime(oldHdr.c_date); - XmlFsType.CreationDateSpecified = true; - sb.AppendFormat("Dump created on {0}", XmlFsType.CreationDate).AppendLine(); - } - - if(oldHdr.c_ddate > 0) - { - XmlFsType.BackupDate = DateHandlers.UnixToDateTime(oldHdr.c_ddate); - XmlFsType.BackupDateSpecified = true; - sb.AppendFormat("Previous dump created on {0}", XmlFsType.BackupDate).AppendLine(); - } - - sb.AppendFormat("Dump volume number: {0}", oldHdr.c_volume).AppendLine(); - } - else if(useAix) - { - XmlFsType.Type = "AIX dump(8)"; - sb.AppendLine(XmlFsType.Type); - - if(aixHdr.c_date > 0) - { - XmlFsType.CreationDate = DateHandlers.UnixToDateTime(aixHdr.c_date); - XmlFsType.CreationDateSpecified = true; - sb.AppendFormat("Dump created on {0}", XmlFsType.CreationDate).AppendLine(); - } - - if(aixHdr.c_ddate > 0) - { - XmlFsType.BackupDate = DateHandlers.UnixToDateTime(aixHdr.c_ddate); - XmlFsType.BackupDateSpecified = true; - sb.AppendFormat("Previous dump created on {0}", XmlFsType.BackupDate).AppendLine(); - } - - sb.AppendFormat("Dump volume number: {0}", aixHdr.c_volume).AppendLine(); - } - else - { - XmlFsType.Type = "dump(8)"; - sb.AppendLine(XmlFsType.Type); - - if(newHdr.c_ndate > 0) - { - XmlFsType.CreationDate = DateHandlers.UnixToDateTime(newHdr.c_ndate); - XmlFsType.CreationDateSpecified = true; - sb.AppendFormat("Dump created on {0}", XmlFsType.CreationDate).AppendLine(); - } - else if(newHdr.c_date > 0) - { - XmlFsType.CreationDate = DateHandlers.UnixToDateTime(newHdr.c_date); - XmlFsType.CreationDateSpecified = true; - sb.AppendFormat("Dump created on {0}", XmlFsType.CreationDate).AppendLine(); - } - - if(newHdr.c_nddate > 0) - { - XmlFsType.BackupDate = DateHandlers.UnixToDateTime(newHdr.c_nddate); - XmlFsType.BackupDateSpecified = true; - sb.AppendFormat("Previous dump created on {0}", XmlFsType.BackupDate).AppendLine(); - } - else if(newHdr.c_ddate > 0) - { - XmlFsType.BackupDate = DateHandlers.UnixToDateTime(newHdr.c_ddate); - XmlFsType.BackupDateSpecified = true; - sb.AppendFormat("Previous dump created on {0}", XmlFsType.BackupDate).AppendLine(); - } - - sb.AppendFormat("Dump volume number: {0}", newHdr.c_volume).AppendLine(); - sb.AppendFormat("Dump level: {0}", newHdr.c_level).AppendLine(); - string dumpname = StringHandlers.CToString(newHdr.c_label); - - if(!string.IsNullOrEmpty(dumpname)) - { - XmlFsType.VolumeName = dumpname; - sb.AppendFormat("Dump label: {0}", dumpname).AppendLine(); - } - - string str = StringHandlers.CToString(newHdr.c_filesys); - - if(!string.IsNullOrEmpty(str)) - sb.AppendFormat("Dumped filesystem name: {0}", str).AppendLine(); - - str = StringHandlers.CToString(newHdr.c_dev); - - if(!string.IsNullOrEmpty(str)) - sb.AppendFormat("Dumped device: {0}", str).AppendLine(); - - str = StringHandlers.CToString(newHdr.c_host); - - if(!string.IsNullOrEmpty(str)) - sb.AppendFormat("Dump hostname: {0}", str).AppendLine(); - } - - information = sb.ToString(); + newHdr = Marshal.ByteArrayToStructureBigEndian(sector); } - - // Old 16-bit format record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct spcl16 + else if(aixHdr.c_magic == XIX_MAGIC || + aixHdr.c_magic == XIX_CIGAM) { - /// Record type - public readonly short c_type; - /// Dump date - public int c_date; - /// Previous dump date - public int c_ddate; - /// Dump volume number - public readonly short c_volume; - /// Logical block of this record - public readonly int c_tapea; - /// Inode number - public readonly ushort c_inumber; - /// Magic number - public readonly ushort c_magic; - /// Record checksum - public readonly int c_checksum; + useAix = true; - // Unneeded for now - /* - struct dinode c_dinode; - int c_count; - char c_addr[BSIZE]; - */ + if(aixHdr.c_magic == XIX_CIGAM) + aixHdr = Marshal.ByteArrayToStructureBigEndian(sector); } - - // 32-bit AIX format record - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct spcl_aix + else if(oldHdr.c_magic == OFS_MAGIC) { - /// Record type - public readonly int c_type; - /// Dump date - public readonly int c_date; - /// Previous dump date - public readonly int c_ddate; - /// Dump volume number - public readonly int c_volume; - /// Logical block of this record - public readonly int c_tapea; - public readonly uint c_inumber; - public readonly uint c_magic; - public readonly int c_checksum; + useOld = true; - // Unneeded for now - /* - public bsd_dinode bsd_c_dinode; - public int c_count; - public char c_addr[TP_NINDIR]; - public int xix_flag; - public dinode xix_dinode; - */ + // Swap PDP-11 endian + oldHdr.c_date = (int)Swapping.PDPFromLittleEndian((uint)oldHdr.c_date); + oldHdr.c_ddate = (int)Swapping.PDPFromLittleEndian((uint)oldHdr.c_ddate); } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct s_spcl + else { - public readonly int c_type; /* record type (see below) */ - public readonly int c_date; /* date of this dump */ - public readonly int c_ddate; /* date of previous dump */ - public readonly int c_volume; /* dump volume number */ - public readonly int c_tapea; /* logical block of this record */ - public readonly uint c_inumber; /* number of inode */ - public readonly int c_magic; /* magic number (see above) */ - public readonly int c_checksum; /* record checksum */ - public readonly dinode c_dinode; /* ownership and mode of inode */ - public readonly int c_count; /* number of valid c_addr entries */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = TP_NINDIR)] - public readonly byte[] c_addr; /* 1 => data; 0 => hole in inode */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = LBLSIZE)] - public readonly byte[] c_label; /* dump label */ - public readonly int c_level; /* level of this dump */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] - public readonly byte[] c_filesys; /* name of dumpped file system */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] - public readonly byte[] c_dev; /* name of dumpped device */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] - public readonly byte[] c_host; /* name of dumpped host */ - public readonly int c_flags; /* additional information */ - public readonly int c_firstrec; /* first record on volume */ - public readonly long c_ndate; /* date of this dump */ - public readonly long c_nddate; /* date of previous dump */ - public readonly long c_ntapea; /* logical block of this record */ - public readonly long c_nfirstrec; /* first record on volume */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly int[] c_spare; /* reserved for future uses */ + information = "Could not read dump(8) header block"; + + return; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct dinode + var sb = new StringBuilder(); + + XmlFsType = new FileSystemType { - public readonly ushort di_mode; /* 0: IFMT, permissions; see below. */ - public readonly short di_nlink; /* 2: File link count. */ - public readonly int inumber; /* 4: Lfs: inode number. */ - public readonly ulong di_size; /* 8: File byte count. */ - public readonly int di_atime; /* 16: Last access time. */ - public readonly int di_atimensec; /* 20: Last access time. */ - public readonly int di_mtime; /* 24: Last modified time. */ - public readonly int di_mtimensec; /* 28: Last modified time. */ - public readonly int di_ctime; /* 32: Last inode change time. */ - public readonly int di_ctimensec; /* 36: Last inode change time. */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NDADDR)] - public readonly int[] di_db; /* 40: Direct disk blocks. */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = NIADDR)] - public readonly int[] di_ib; /* 88: Indirect disk blocks. */ - public readonly uint di_flags; /* 100: Status flags (chflags). */ - public readonly uint di_blocks; /* 104: Blocks actually held. */ - public readonly int di_gen; /* 108: Generation number. */ - public readonly uint di_uid; /* 112: File owner. */ - public readonly uint di_gid; /* 116: File group. */ - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly int[] di_spare; /* 120: Reserved; currently unused */ + ClusterSize = 1024, + Clusters = partition.Size / 1024 + }; + + if(useOld) + { + XmlFsType.Type = "Old 16-bit dump(8)"; + sb.AppendLine(XmlFsType.Type); + + if(oldHdr.c_date > 0) + { + XmlFsType.CreationDate = DateHandlers.UnixToDateTime(oldHdr.c_date); + XmlFsType.CreationDateSpecified = true; + sb.AppendFormat("Dump created on {0}", XmlFsType.CreationDate).AppendLine(); + } + + if(oldHdr.c_ddate > 0) + { + XmlFsType.BackupDate = DateHandlers.UnixToDateTime(oldHdr.c_ddate); + XmlFsType.BackupDateSpecified = true; + sb.AppendFormat("Previous dump created on {0}", XmlFsType.BackupDate).AppendLine(); + } + + sb.AppendFormat("Dump volume number: {0}", oldHdr.c_volume).AppendLine(); } + else if(useAix) + { + XmlFsType.Type = "AIX dump(8)"; + sb.AppendLine(XmlFsType.Type); + + if(aixHdr.c_date > 0) + { + XmlFsType.CreationDate = DateHandlers.UnixToDateTime(aixHdr.c_date); + XmlFsType.CreationDateSpecified = true; + sb.AppendFormat("Dump created on {0}", XmlFsType.CreationDate).AppendLine(); + } + + if(aixHdr.c_ddate > 0) + { + XmlFsType.BackupDate = DateHandlers.UnixToDateTime(aixHdr.c_ddate); + XmlFsType.BackupDateSpecified = true; + sb.AppendFormat("Previous dump created on {0}", XmlFsType.BackupDate).AppendLine(); + } + + sb.AppendFormat("Dump volume number: {0}", aixHdr.c_volume).AppendLine(); + } + else + { + XmlFsType.Type = "dump(8)"; + sb.AppendLine(XmlFsType.Type); + + if(newHdr.c_ndate > 0) + { + XmlFsType.CreationDate = DateHandlers.UnixToDateTime(newHdr.c_ndate); + XmlFsType.CreationDateSpecified = true; + sb.AppendFormat("Dump created on {0}", XmlFsType.CreationDate).AppendLine(); + } + else if(newHdr.c_date > 0) + { + XmlFsType.CreationDate = DateHandlers.UnixToDateTime(newHdr.c_date); + XmlFsType.CreationDateSpecified = true; + sb.AppendFormat("Dump created on {0}", XmlFsType.CreationDate).AppendLine(); + } + + if(newHdr.c_nddate > 0) + { + XmlFsType.BackupDate = DateHandlers.UnixToDateTime(newHdr.c_nddate); + XmlFsType.BackupDateSpecified = true; + sb.AppendFormat("Previous dump created on {0}", XmlFsType.BackupDate).AppendLine(); + } + else if(newHdr.c_ddate > 0) + { + XmlFsType.BackupDate = DateHandlers.UnixToDateTime(newHdr.c_ddate); + XmlFsType.BackupDateSpecified = true; + sb.AppendFormat("Previous dump created on {0}", XmlFsType.BackupDate).AppendLine(); + } + + sb.AppendFormat("Dump volume number: {0}", newHdr.c_volume).AppendLine(); + sb.AppendFormat("Dump level: {0}", newHdr.c_level).AppendLine(); + string dumpname = StringHandlers.CToString(newHdr.c_label); + + if(!string.IsNullOrEmpty(dumpname)) + { + XmlFsType.VolumeName = dumpname; + sb.AppendFormat("Dump label: {0}", dumpname).AppendLine(); + } + + string str = StringHandlers.CToString(newHdr.c_filesys); + + if(!string.IsNullOrEmpty(str)) + sb.AppendFormat("Dumped filesystem name: {0}", str).AppendLine(); + + str = StringHandlers.CToString(newHdr.c_dev); + + if(!string.IsNullOrEmpty(str)) + sb.AppendFormat("Dumped device: {0}", str).AppendLine(); + + str = StringHandlers.CToString(newHdr.c_host); + + if(!string.IsNullOrEmpty(str)) + sb.AppendFormat("Dump hostname: {0}", str).AppendLine(); + } + + information = sb.ToString(); + } + + // Old 16-bit format record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct spcl16 + { + /// Record type + public readonly short c_type; + /// Dump date + public int c_date; + /// Previous dump date + public int c_ddate; + /// Dump volume number + public readonly short c_volume; + /// Logical block of this record + public readonly int c_tapea; + /// Inode number + public readonly ushort c_inumber; + /// Magic number + public readonly ushort c_magic; + /// Record checksum + public readonly int c_checksum; + + // Unneeded for now + /* + struct dinode c_dinode; + int c_count; + char c_addr[BSIZE]; + */ + } + + // 32-bit AIX format record + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct spcl_aix + { + /// Record type + public readonly int c_type; + /// Dump date + public readonly int c_date; + /// Previous dump date + public readonly int c_ddate; + /// Dump volume number + public readonly int c_volume; + /// Logical block of this record + public readonly int c_tapea; + public readonly uint c_inumber; + public readonly uint c_magic; + public readonly int c_checksum; + + // Unneeded for now + /* + public bsd_dinode bsd_c_dinode; + public int c_count; + public char c_addr[TP_NINDIR]; + public int xix_flag; + public dinode xix_dinode; + */ + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct s_spcl + { + public readonly int c_type; /* record type (see below) */ + public readonly int c_date; /* date of this dump */ + public readonly int c_ddate; /* date of previous dump */ + public readonly int c_volume; /* dump volume number */ + public readonly int c_tapea; /* logical block of this record */ + public readonly uint c_inumber; /* number of inode */ + public readonly int c_magic; /* magic number (see above) */ + public readonly int c_checksum; /* record checksum */ + public readonly dinode c_dinode; /* ownership and mode of inode */ + public readonly int c_count; /* number of valid c_addr entries */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = TP_NINDIR)] + public readonly byte[] c_addr; /* 1 => data; 0 => hole in inode */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = LBLSIZE)] + public readonly byte[] c_label; /* dump label */ + public readonly int c_level; /* level of this dump */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] + public readonly byte[] c_filesys; /* name of dumpped file system */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] + public readonly byte[] c_dev; /* name of dumpped device */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NAMELEN)] + public readonly byte[] c_host; /* name of dumpped host */ + public readonly int c_flags; /* additional information */ + public readonly int c_firstrec; /* first record on volume */ + public readonly long c_ndate; /* date of this dump */ + public readonly long c_nddate; /* date of previous dump */ + public readonly long c_ntapea; /* logical block of this record */ + public readonly long c_nfirstrec; /* first record on volume */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly int[] c_spare; /* reserved for future uses */ + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct dinode + { + public readonly ushort di_mode; /* 0: IFMT, permissions; see below. */ + public readonly short di_nlink; /* 2: File link count. */ + public readonly int inumber; /* 4: Lfs: inode number. */ + public readonly ulong di_size; /* 8: File byte count. */ + public readonly int di_atime; /* 16: Last access time. */ + public readonly int di_atimensec; /* 20: Last access time. */ + public readonly int di_mtime; /* 24: Last modified time. */ + public readonly int di_mtimensec; /* 28: Last modified time. */ + public readonly int di_ctime; /* 32: Last inode change time. */ + public readonly int di_ctimensec; /* 36: Last inode change time. */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NDADDR)] + public readonly int[] di_db; /* 40: Direct disk blocks. */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = NIADDR)] + public readonly int[] di_ib; /* 88: Indirect disk blocks. */ + public readonly uint di_flags; /* 100: Status flags (chflags). */ + public readonly uint di_blocks; /* 104: Blocks actually held. */ + public readonly int di_gen; /* 108: Generation number. */ + public readonly uint di_uid; /* 112: File owner. */ + public readonly uint di_gid; /* 116: File group. */ + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly int[] di_spare; /* 120: Reserved; currently unused */ } } \ No newline at end of file diff --git a/Aaru.Filesystems/exFAT.cs b/Aaru.Filesystems/exFAT.cs index 9f12fefd0..b45347b7e 100644 --- a/Aaru.Filesystems/exFAT.cs +++ b/Aaru.Filesystems/exFAT.cs @@ -41,216 +41,215 @@ using Aaru.CommonTypes.Interfaces; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from https://www.sans.org/reading-room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system-33274 +/// +/// Implements detection of the exFAT filesystem +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class exFAT : IFilesystem { - // Information from https://www.sans.org/reading-room/whitepapers/forensics/reverse-engineering-microsoft-exfat-file-system-33274 - /// - /// Implements detection of the exFAT filesystem - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class exFAT : IFilesystem + readonly Guid _oemFlashParameterGuid = new("0A0C7E46-3399-4021-90C8-FA6D389C4BA2"); + + readonly byte[] _signature = { - readonly Guid _oemFlashParameterGuid = new("0A0C7E46-3399-4021-90C8-FA6D389C4BA2"); + 0x45, 0x58, 0x46, 0x41, 0x54, 0x20, 0x20, 0x20 + }; - readonly byte[] _signature = + /// + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Microsoft Extended File Allocation Table"; + /// + public Guid Id => new("8271D088-1533-4CB3-AC28-D802B68BB95C"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) + { + if(12 + partition.Start >= partition.End) + return false; + + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] vbrSector); + + if(errno != ErrorNumber.NoError) + return false; + + if(vbrSector.Length < 512) + return false; + + VolumeBootRecord vbr = Marshal.ByteArrayToStructureLittleEndian(vbrSector); + + return _signature.SequenceEqual(vbr.signature); + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + XmlFsType = new FileSystemType(); + + ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] vbrSector); + + if(errno != ErrorNumber.NoError) + return; + + VolumeBootRecord vbr = Marshal.ByteArrayToStructureLittleEndian(vbrSector); + + errno = imagePlugin.ReadSector(9 + partition.Start, out byte[] parametersSector); + + if(errno != ErrorNumber.NoError) + return; + + OemParameterTable parametersTable = + Marshal.ByteArrayToStructureLittleEndian(parametersSector); + + errno = imagePlugin.ReadSector(11 + partition.Start, out byte[] chkSector); + + if(errno != ErrorNumber.NoError) + return; + + ChecksumSector chksector = Marshal.ByteArrayToStructureLittleEndian(chkSector); + + sb.AppendLine("Microsoft exFAT"); + sb.AppendFormat("Partition offset: {0}", vbr.offset).AppendLine(); + + sb.AppendFormat("Volume has {0} sectors of {1} bytes each for a total of {2} bytes", vbr.sectors, + 1 << vbr.sectorShift, vbr.sectors * (ulong)(1 << vbr.sectorShift)).AppendLine(); + + sb.AppendFormat("Volume uses clusters of {0} sectors ({1} bytes) each", 1 << vbr.clusterShift, + (1 << vbr.sectorShift) * (1 << vbr.clusterShift)).AppendLine(); + + sb.AppendFormat("First FAT starts at sector {0} and runs for {1} sectors", vbr.fatOffset, vbr.fatLength). + AppendLine(); + + sb.AppendFormat("Volume uses {0} FATs", vbr.fats).AppendLine(); + + sb.AppendFormat("Cluster heap starts at sector {0}, contains {1} clusters and is {2}% used", + vbr.clusterHeapOffset, vbr.clusterHeapLength, vbr.heapUsage).AppendLine(); + + sb.AppendFormat("Root directory starts at cluster {0}", vbr.rootDirectoryCluster).AppendLine(); + + sb.AppendFormat("Filesystem revision is {0}.{1:D2}", (vbr.revision & 0xFF00) >> 8, vbr.revision & 0xFF). + AppendLine(); + + sb.AppendFormat("Volume serial number: {0:X8}", vbr.volumeSerial).AppendLine(); + sb.AppendFormat("BIOS drive is {0:X2}h", vbr.drive).AppendLine(); + + if(vbr.flags.HasFlag(VolumeFlags.SecondFatActive)) + sb.AppendLine("2nd FAT is in use"); + + if(vbr.flags.HasFlag(VolumeFlags.VolumeDirty)) + sb.AppendLine("Volume is dirty"); + + if(vbr.flags.HasFlag(VolumeFlags.MediaFailure)) + sb.AppendLine("Underlying media presented errors"); + + int count = 1; + + foreach(OemParameter parameter in parametersTable.parameters) { - 0x45, 0x58, 0x46, 0x41, 0x54, 0x20, 0x20, 0x20 - }; - - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Microsoft Extended File Allocation Table"; - /// - public Guid Id => new("8271D088-1533-4CB3-AC28-D802B68BB95C"); - /// - public string Author => "Natalia Portillo"; - - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) - { - if(12 + partition.Start >= partition.End) - return false; - - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] vbrSector); - - if(errno != ErrorNumber.NoError) - return false; - - if(vbrSector.Length < 512) - return false; - - VolumeBootRecord vbr = Marshal.ByteArrayToStructureLittleEndian(vbrSector); - - return _signature.SequenceEqual(vbr.signature); - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) - { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; - - var sb = new StringBuilder(); - XmlFsType = new FileSystemType(); - - ErrorNumber errno = imagePlugin.ReadSector(0 + partition.Start, out byte[] vbrSector); - - if(errno != ErrorNumber.NoError) - return; - - VolumeBootRecord vbr = Marshal.ByteArrayToStructureLittleEndian(vbrSector); - - errno = imagePlugin.ReadSector(9 + partition.Start, out byte[] parametersSector); - - if(errno != ErrorNumber.NoError) - return; - - OemParameterTable parametersTable = - Marshal.ByteArrayToStructureLittleEndian(parametersSector); - - errno = imagePlugin.ReadSector(11 + partition.Start, out byte[] chkSector); - - if(errno != ErrorNumber.NoError) - return; - - ChecksumSector chksector = Marshal.ByteArrayToStructureLittleEndian(chkSector); - - sb.AppendLine("Microsoft exFAT"); - sb.AppendFormat("Partition offset: {0}", vbr.offset).AppendLine(); - - sb.AppendFormat("Volume has {0} sectors of {1} bytes each for a total of {2} bytes", vbr.sectors, - 1 << vbr.sectorShift, vbr.sectors * (ulong)(1 << vbr.sectorShift)).AppendLine(); - - sb.AppendFormat("Volume uses clusters of {0} sectors ({1} bytes) each", 1 << vbr.clusterShift, - (1 << vbr.sectorShift) * (1 << vbr.clusterShift)).AppendLine(); - - sb.AppendFormat("First FAT starts at sector {0} and runs for {1} sectors", vbr.fatOffset, vbr.fatLength). - AppendLine(); - - sb.AppendFormat("Volume uses {0} FATs", vbr.fats).AppendLine(); - - sb.AppendFormat("Cluster heap starts at sector {0}, contains {1} clusters and is {2}% used", - vbr.clusterHeapOffset, vbr.clusterHeapLength, vbr.heapUsage).AppendLine(); - - sb.AppendFormat("Root directory starts at cluster {0}", vbr.rootDirectoryCluster).AppendLine(); - - sb.AppendFormat("Filesystem revision is {0}.{1:D2}", (vbr.revision & 0xFF00) >> 8, vbr.revision & 0xFF). - AppendLine(); - - sb.AppendFormat("Volume serial number: {0:X8}", vbr.volumeSerial).AppendLine(); - sb.AppendFormat("BIOS drive is {0:X2}h", vbr.drive).AppendLine(); - - if(vbr.flags.HasFlag(VolumeFlags.SecondFatActive)) - sb.AppendLine("2nd FAT is in use"); - - if(vbr.flags.HasFlag(VolumeFlags.VolumeDirty)) - sb.AppendLine("Volume is dirty"); - - if(vbr.flags.HasFlag(VolumeFlags.MediaFailure)) - sb.AppendLine("Underlying media presented errors"); - - int count = 1; - - foreach(OemParameter parameter in parametersTable.parameters) + if(parameter.OemParameterType == _oemFlashParameterGuid) { - if(parameter.OemParameterType == _oemFlashParameterGuid) - { - sb.AppendFormat("OEM Parameters {0}:", count).AppendLine(); - sb.AppendFormat("\t{0} bytes in erase block", parameter.eraseBlockSize).AppendLine(); - sb.AppendFormat("\t{0} bytes per page", parameter.pageSize).AppendLine(); - sb.AppendFormat("\t{0} spare blocks", parameter.spareBlocks).AppendLine(); - sb.AppendFormat("\t{0} nanoseconds random access time", parameter.randomAccessTime).AppendLine(); - sb.AppendFormat("\t{0} nanoseconds program time", parameter.programTime).AppendLine(); - sb.AppendFormat("\t{0} nanoseconds read cycle time", parameter.readCycleTime).AppendLine(); - sb.AppendFormat("\t{0} nanoseconds write cycle time", parameter.writeCycleTime).AppendLine(); - } - else if(parameter.OemParameterType != Guid.Empty) - sb.AppendFormat("Found unknown parameter type {0}", parameter.OemParameterType).AppendLine(); - - count++; + sb.AppendFormat("OEM Parameters {0}:", count).AppendLine(); + sb.AppendFormat("\t{0} bytes in erase block", parameter.eraseBlockSize).AppendLine(); + sb.AppendFormat("\t{0} bytes per page", parameter.pageSize).AppendLine(); + sb.AppendFormat("\t{0} spare blocks", parameter.spareBlocks).AppendLine(); + sb.AppendFormat("\t{0} nanoseconds random access time", parameter.randomAccessTime).AppendLine(); + sb.AppendFormat("\t{0} nanoseconds program time", parameter.programTime).AppendLine(); + sb.AppendFormat("\t{0} nanoseconds read cycle time", parameter.readCycleTime).AppendLine(); + sb.AppendFormat("\t{0} nanoseconds write cycle time", parameter.writeCycleTime).AppendLine(); } + else if(parameter.OemParameterType != Guid.Empty) + sb.AppendFormat("Found unknown parameter type {0}", parameter.OemParameterType).AppendLine(); - sb.AppendFormat("Checksum 0x{0:X8}", chksector.checksum[0]).AppendLine(); - - XmlFsType.ClusterSize = (uint)((1 << vbr.sectorShift) * (1 << vbr.clusterShift)); - XmlFsType.Clusters = vbr.clusterHeapLength; - XmlFsType.Dirty = vbr.flags.HasFlag(VolumeFlags.VolumeDirty); - XmlFsType.Type = "exFAT"; - XmlFsType.VolumeSerial = $"{vbr.volumeSerial:X8}"; - - information = sb.ToString(); + count++; } - [Flags] - enum VolumeFlags : ushort - { - SecondFatActive = 1, VolumeDirty = 2, MediaFailure = 4, - ClearToZero = 8 - } + sb.AppendFormat("Checksum 0x{0:X8}", chksector.checksum[0]).AppendLine(); - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct VolumeBootRecord - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] - public readonly byte[] jump; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] - public readonly byte[] signature; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 53)] - public readonly byte[] zero; - public readonly ulong offset; - public readonly ulong sectors; - public readonly uint fatOffset; - public readonly uint fatLength; - public readonly uint clusterHeapOffset; - public readonly uint clusterHeapLength; - public readonly uint rootDirectoryCluster; - public readonly uint volumeSerial; - public readonly ushort revision; - public readonly VolumeFlags flags; - public readonly byte sectorShift; - public readonly byte clusterShift; - public readonly byte fats; - public readonly byte drive; - public readonly byte heapUsage; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 53)] - public readonly byte[] reserved; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 53)] - public readonly byte[] bootCode; - public readonly ushort bootSignature; - } + XmlFsType.ClusterSize = (uint)((1 << vbr.sectorShift) * (1 << vbr.clusterShift)); + XmlFsType.Clusters = vbr.clusterHeapLength; + XmlFsType.Dirty = vbr.flags.HasFlag(VolumeFlags.VolumeDirty); + XmlFsType.Type = "exFAT"; + XmlFsType.VolumeSerial = $"{vbr.volumeSerial:X8}"; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct OemParameter - { - public readonly Guid OemParameterType; - public readonly uint eraseBlockSize; - public readonly uint pageSize; - public readonly uint spareBlocks; - public readonly uint randomAccessTime; - public readonly uint programTime; - public readonly uint readCycleTime; - public readonly uint writeCycleTime; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] reserved; - } + information = sb.ToString(); + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct OemParameterTable - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] - public readonly OemParameter[] parameters; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] padding; - } + [Flags] + enum VolumeFlags : ushort + { + SecondFatActive = 1, VolumeDirty = 2, MediaFailure = 4, + ClearToZero = 8 + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ChecksumSector - { - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] - public readonly uint[] checksum; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct VolumeBootRecord + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3)] + public readonly byte[] jump; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)] + public readonly byte[] signature; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 53)] + public readonly byte[] zero; + public readonly ulong offset; + public readonly ulong sectors; + public readonly uint fatOffset; + public readonly uint fatLength; + public readonly uint clusterHeapOffset; + public readonly uint clusterHeapLength; + public readonly uint rootDirectoryCluster; + public readonly uint volumeSerial; + public readonly ushort revision; + public readonly VolumeFlags flags; + public readonly byte sectorShift; + public readonly byte clusterShift; + public readonly byte fats; + public readonly byte drive; + public readonly byte heapUsage; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 53)] + public readonly byte[] reserved; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 53)] + public readonly byte[] bootCode; + public readonly ushort bootSignature; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct OemParameter + { + public readonly Guid OemParameterType; + public readonly uint eraseBlockSize; + public readonly uint pageSize; + public readonly uint spareBlocks; + public readonly uint randomAccessTime; + public readonly uint programTime; + public readonly uint readCycleTime; + public readonly uint writeCycleTime; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] reserved; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct OemParameterTable + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] + public readonly OemParameter[] parameters; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] padding; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ChecksumSector + { + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 128)] + public readonly uint[] checksum; } } \ No newline at end of file diff --git a/Aaru.Filesystems/ext2FS.cs b/Aaru.Filesystems/ext2FS.cs index 6e4696af0..56f9a093e 100644 --- a/Aaru.Filesystems/ext2FS.cs +++ b/Aaru.Filesystems/ext2FS.cs @@ -41,961 +41,960 @@ using Aaru.Helpers; using Schemas; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from the Linux kernel +/// +/// Implements detection of the Linux extended filesystem v2, v3 and v4 +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class ext2FS : IFilesystem { - // Information from the Linux kernel + const int SB_POS = 0x400; + + /// Same magic for ext2, ext3 and ext4 + const ushort EXT2_MAGIC = 0xEF53; + + const ushort EXT2_MAGIC_OLD = 0xEF51; + + // ext? filesystem states + /// Cleanly-unmounted volume + const ushort EXT2_VALID_FS = 0x0001; + /// Dirty volume + const ushort EXT2_ERROR_FS = 0x0002; + /// Recovering orphan files + const ushort EXT3_ORPHAN_FS = 0x0004; + + // ext? default mount flags + /// Enable debugging messages + const uint EXT2_DEFM_DEBUG = 0x000001; + /// Emulates BSD behaviour on new file creation + const uint EXT2_DEFM_BSDGROUPS = 0x000002; + /// Enable user xattrs + const uint EXT2_DEFM_XATTR_USER = 0x000004; + /// Enable POSIX ACLs + const uint EXT2_DEFM_ACL = 0x000008; + /// Use 16bit UIDs + const uint EXT2_DEFM_UID16 = 0x000010; + /// Journal data mode + const uint EXT3_DEFM_JMODE_DATA = 0x000040; + /// Journal ordered mode + const uint EXT3_DEFM_JMODE_ORDERED = 0x000080; + /// Journal writeback mode + const uint EXT3_DEFM_JMODE_WBACK = 0x000100; + + // Behaviour on errors + /// Continue execution + const ushort EXT2_ERRORS_CONTINUE = 1; + /// Remount fs read-only + const ushort EXT2_ERRORS_RO = 2; + /// Panic + const ushort EXT2_ERRORS_PANIC = 3; + + // OS codes + const uint EXT2_OS_LINUX = 0; + const uint EXT2_OS_HURD = 1; + const uint EXT2_OS_MASIX = 2; + const uint EXT2_OS_FREEBSD = 3; + const uint EXT2_OS_LITES = 4; + + // Revision levels + /// The good old (original) format + const uint EXT2_GOOD_OLD_REV = 0; + /// V2 format w/ dynamic inode sizes + const uint EXT2_DYNAMIC_REV = 1; + + // Compatible features + /// Pre-allocate directories + const uint EXT2_FEATURE_COMPAT_DIR_PREALLOC = 0x00000001; + /// imagic inodes ? + const uint EXT2_FEATURE_COMPAT_IMAGIC_INODES = 0x00000002; + /// Has journal (it's ext3) + const uint EXT3_FEATURE_COMPAT_HAS_JOURNAL = 0x00000004; + /// EA blocks + const uint EXT2_FEATURE_COMPAT_EXT_ATTR = 0x00000008; + /// Online filesystem resize reservations + const uint EXT2_FEATURE_COMPAT_RESIZE_INO = 0x00000010; + /// Can use hashed indexes on directories + const uint EXT2_FEATURE_COMPAT_DIR_INDEX = 0x00000020; + + // Read-only compatible features + /// Reduced number of superblocks + const uint EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER = 0x00000001; + /// Can have files bigger than 2GiB + const uint EXT2_FEATURE_RO_COMPAT_LARGE_FILE = 0x00000002; + /// Use B-Tree for directories + const uint EXT2_FEATURE_RO_COMPAT_BTREE_DIR = 0x00000004; + /// Can have files bigger than 2TiB *ext4* + const uint EXT4_FEATURE_RO_COMPAT_HUGE_FILE = 0x00000008; + /// Group descriptor checksums and sparse inode table *ext4* + const uint EXT4_FEATURE_RO_COMPAT_GDT_CSUM = 0x00000010; + /// More than 32000 directory entries *ext4* + const uint EXT4_FEATURE_RO_COMPAT_DIR_NLINK = 0x00000020; + /// Nanosecond timestamps and creation time *ext4* + const uint EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE = 0x00000040; + + // Incompatible features + /// Uses compression + const uint EXT2_FEATURE_INCOMPAT_COMPRESSION = 0x00000001; + /// Filetype in directory entries + const uint EXT2_FEATURE_INCOMPAT_FILETYPE = 0x00000002; + /// Journal needs recovery *ext3* + const uint EXT3_FEATURE_INCOMPAT_RECOVER = 0x00000004; + /// Has journal on another device *ext3* + const uint EXT3_FEATURE_INCOMPAT_JOURNAL_DEV = 0x00000008; + /// Reduced block group backups + const uint EXT2_FEATURE_INCOMPAT_META_BG = 0x00000010; + /// Volume use extents *ext4* + const uint EXT4_FEATURE_INCOMPAT_EXTENTS = 0x00000040; + /// Supports volumes bigger than 2^32 blocks *ext4* + const uint EXT4_FEATURE_INCOMPAT_64BIT = 0x00000080; + /// Multi-mount protection *ext4* + const uint EXT4_FEATURE_INCOMPAT_MMP = 0x00000100; + /// Flexible block group metadata location *ext4* + const uint EXT4_FEATURE_INCOMPAT_FLEX_BG = 0x00000200; + /// EA in inode *ext4* + const uint EXT4_FEATURE_INCOMPAT_EA_INODE = 0x00000400; + /// Data can reside in directory entry *ext4* + const uint EXT4_FEATURE_INCOMPAT_DIRDATA = 0x00001000; + + // Miscellaneous filesystem flags + /// Signed dirhash in use + const uint EXT2_FLAGS_SIGNED_HASH = 0x00000001; + /// Unsigned dirhash in use + const uint EXT2_FLAGS_UNSIGNED_HASH = 0x00000002; + /// Testing development code + const uint EXT2_FLAGS_TEST_FILESYS = 0x00000004; + /// - /// Implements detection of the Linux extended filesystem v2, v3 and v4 - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class ext2FS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public Encoding Encoding { get; private set; } + /// + public string Name => "Linux extended Filesystem 2, 3 and 4"; + /// + public Guid Id => new("6AA91B88-150B-4A7B-AD56-F84FB2DF4184"); + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const int SB_POS = 0x400; + ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; + uint sbOff = SB_POS % imagePlugin.Info.SectorSize; - /// Same magic for ext2, ext3 and ext4 - const ushort EXT2_MAGIC = 0xEF53; + if(sbSectorOff + partition.Start >= partition.End) + return false; - const ushort EXT2_MAGIC_OLD = 0xEF51; + int sbSizeInBytes = Marshal.SizeOf(); + uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); - // ext? filesystem states - /// Cleanly-unmounted volume - const ushort EXT2_VALID_FS = 0x0001; - /// Dirty volume - const ushort EXT2_ERROR_FS = 0x0002; - /// Recovering orphan files - const ushort EXT3_ORPHAN_FS = 0x0004; + if(sbSizeInBytes % imagePlugin.Info.SectorSize > 0) + sbSizeInSectors++; - // ext? default mount flags - /// Enable debugging messages - const uint EXT2_DEFM_DEBUG = 0x000001; - /// Emulates BSD behaviour on new file creation - const uint EXT2_DEFM_BSDGROUPS = 0x000002; - /// Enable user xattrs - const uint EXT2_DEFM_XATTR_USER = 0x000004; - /// Enable POSIX ACLs - const uint EXT2_DEFM_ACL = 0x000008; - /// Use 16bit UIDs - const uint EXT2_DEFM_UID16 = 0x000010; - /// Journal data mode - const uint EXT3_DEFM_JMODE_DATA = 0x000040; - /// Journal ordered mode - const uint EXT3_DEFM_JMODE_ORDERED = 0x000080; - /// Journal writeback mode - const uint EXT3_DEFM_JMODE_WBACK = 0x000100; + ErrorNumber errno = + imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSizeInSectors, out byte[] sbSector); - // Behaviour on errors - /// Continue execution - const ushort EXT2_ERRORS_CONTINUE = 1; - /// Remount fs read-only - const ushort EXT2_ERRORS_RO = 2; - /// Panic - const ushort EXT2_ERRORS_PANIC = 3; + if(errno != ErrorNumber.NoError) + return false; - // OS codes - const uint EXT2_OS_LINUX = 0; - const uint EXT2_OS_HURD = 1; - const uint EXT2_OS_MASIX = 2; - const uint EXT2_OS_FREEBSD = 3; - const uint EXT2_OS_LITES = 4; + byte[] sb = new byte[sbSizeInBytes]; - // Revision levels - /// The good old (original) format - const uint EXT2_GOOD_OLD_REV = 0; - /// V2 format w/ dynamic inode sizes - const uint EXT2_DYNAMIC_REV = 1; + if(sbOff + sbSizeInBytes > sbSector.Length) + return false; - // Compatible features - /// Pre-allocate directories - const uint EXT2_FEATURE_COMPAT_DIR_PREALLOC = 0x00000001; - /// imagic inodes ? - const uint EXT2_FEATURE_COMPAT_IMAGIC_INODES = 0x00000002; - /// Has journal (it's ext3) - const uint EXT3_FEATURE_COMPAT_HAS_JOURNAL = 0x00000004; - /// EA blocks - const uint EXT2_FEATURE_COMPAT_EXT_ATTR = 0x00000008; - /// Online filesystem resize reservations - const uint EXT2_FEATURE_COMPAT_RESIZE_INO = 0x00000010; - /// Can use hashed indexes on directories - const uint EXT2_FEATURE_COMPAT_DIR_INDEX = 0x00000020; + Array.Copy(sbSector, sbOff, sb, 0, sbSizeInBytes); - // Read-only compatible features - /// Reduced number of superblocks - const uint EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER = 0x00000001; - /// Can have files bigger than 2GiB - const uint EXT2_FEATURE_RO_COMPAT_LARGE_FILE = 0x00000002; - /// Use B-Tree for directories - const uint EXT2_FEATURE_RO_COMPAT_BTREE_DIR = 0x00000004; - /// Can have files bigger than 2TiB *ext4* - const uint EXT4_FEATURE_RO_COMPAT_HUGE_FILE = 0x00000008; - /// Group descriptor checksums and sparse inode table *ext4* - const uint EXT4_FEATURE_RO_COMPAT_GDT_CSUM = 0x00000010; - /// More than 32000 directory entries *ext4* - const uint EXT4_FEATURE_RO_COMPAT_DIR_NLINK = 0x00000020; - /// Nanosecond timestamps and creation time *ext4* - const uint EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE = 0x00000040; + ushort magic = BitConverter.ToUInt16(sb, 0x038); - // Incompatible features - /// Uses compression - const uint EXT2_FEATURE_INCOMPAT_COMPRESSION = 0x00000001; - /// Filetype in directory entries - const uint EXT2_FEATURE_INCOMPAT_FILETYPE = 0x00000002; - /// Journal needs recovery *ext3* - const uint EXT3_FEATURE_INCOMPAT_RECOVER = 0x00000004; - /// Has journal on another device *ext3* - const uint EXT3_FEATURE_INCOMPAT_JOURNAL_DEV = 0x00000008; - /// Reduced block group backups - const uint EXT2_FEATURE_INCOMPAT_META_BG = 0x00000010; - /// Volume use extents *ext4* - const uint EXT4_FEATURE_INCOMPAT_EXTENTS = 0x00000040; - /// Supports volumes bigger than 2^32 blocks *ext4* - const uint EXT4_FEATURE_INCOMPAT_64BIT = 0x00000080; - /// Multi-mount protection *ext4* - const uint EXT4_FEATURE_INCOMPAT_MMP = 0x00000100; - /// Flexible block group metadata location *ext4* - const uint EXT4_FEATURE_INCOMPAT_FLEX_BG = 0x00000200; - /// EA in inode *ext4* - const uint EXT4_FEATURE_INCOMPAT_EA_INODE = 0x00000400; - /// Data can reside in directory entry *ext4* - const uint EXT4_FEATURE_INCOMPAT_DIRDATA = 0x00001000; + return magic == EXT2_MAGIC || magic == EXT2_MAGIC_OLD; + } - // Miscellaneous filesystem flags - /// Signed dirhash in use - const uint EXT2_FLAGS_SIGNED_HASH = 0x00000001; - /// Unsigned dirhash in use - const uint EXT2_FLAGS_UNSIGNED_HASH = 0x00000002; - /// Testing development code - const uint EXT2_FLAGS_TEST_FILESYS = 0x00000004; + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public Encoding Encoding { get; private set; } - /// - public string Name => "Linux extended Filesystem 2, 3 and 4"; - /// - public Guid Id => new("6AA91B88-150B-4A7B-AD56-F84FB2DF4184"); - /// - public string Author => "Natalia Portillo"; + var sb = new StringBuilder(); - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + bool newExt2 = false; + bool ext3 = false; + bool ext4 = false; + + int sbSizeInBytes = Marshal.SizeOf(); + uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); + + if(sbSizeInBytes % imagePlugin.Info.SectorSize > 0) + sbSizeInSectors++; + + ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; + uint sbOff = SB_POS % imagePlugin.Info.SectorSize; + + ErrorNumber errno = + imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSizeInSectors, out byte[] sbSector); + + if(errno != ErrorNumber.NoError) + return; + + byte[] sblock = new byte[sbSizeInBytes]; + Array.Copy(sbSector, sbOff, sblock, 0, sbSizeInBytes); + SuperBlock supblk = Marshal.ByteArrayToStructureLittleEndian(sblock); + + XmlFsType = new FileSystemType(); + + switch(supblk.magic) { - ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; - uint sbOff = SB_POS % imagePlugin.Info.SectorSize; + case EXT2_MAGIC_OLD: + sb.AppendLine("ext2 (old) filesystem"); + XmlFsType.Type = "ext2"; - if(sbSectorOff + partition.Start >= partition.End) - return false; + break; + case EXT2_MAGIC: + ext3 |= (supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL || + (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) == EXT3_FEATURE_INCOMPAT_RECOVER || + (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == + EXT3_FEATURE_INCOMPAT_JOURNAL_DEV; - int sbSizeInBytes = Marshal.SizeOf(); - uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); + if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) == EXT4_FEATURE_RO_COMPAT_HUGE_FILE || + (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) == EXT4_FEATURE_RO_COMPAT_GDT_CSUM || + (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) == EXT4_FEATURE_RO_COMPAT_DIR_NLINK || + (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) == + EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE || + (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT || + (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_MMP) == EXT4_FEATURE_INCOMPAT_MMP || + (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) == EXT4_FEATURE_INCOMPAT_FLEX_BG || + (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EA_INODE) == EXT4_FEATURE_INCOMPAT_EA_INODE || + (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) == EXT4_FEATURE_INCOMPAT_DIRDATA) + { + ext3 = false; + ext4 = true; + } - if(sbSizeInBytes % imagePlugin.Info.SectorSize > 0) - sbSizeInSectors++; + newExt2 |= !ext3 && !ext4; - ErrorNumber errno = - imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSizeInSectors, out byte[] sbSector); + if(newExt2) + { + sb.AppendLine("ext2 filesystem"); + XmlFsType.Type = "ext2"; + } - if(errno != ErrorNumber.NoError) - return false; + if(ext3) + { + sb.AppendLine("ext3 filesystem"); + XmlFsType.Type = "ext3"; + } - byte[] sb = new byte[sbSizeInBytes]; + if(ext4) + { + sb.AppendLine("ext4 filesystem"); + XmlFsType.Type = "ext4"; + } - if(sbOff + sbSizeInBytes > sbSector.Length) - return false; + break; + default: + information = "Not a ext2/3/4 filesystem" + Environment.NewLine; - Array.Copy(sbSector, sbOff, sb, 0, sbSizeInBytes); - - ushort magic = BitConverter.ToUInt16(sb, 0x038); - - return magic == EXT2_MAGIC || magic == EXT2_MAGIC_OLD; + return; } - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + string extOs; + + switch(supblk.creator_os) { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + case EXT2_OS_FREEBSD: + extOs = "FreeBSD"; - var sb = new StringBuilder(); + break; + case EXT2_OS_HURD: + extOs = "Hurd"; - bool newExt2 = false; - bool ext3 = false; - bool ext4 = false; + break; + case EXT2_OS_LINUX: + extOs = "Linux"; - int sbSizeInBytes = Marshal.SizeOf(); - uint sbSizeInSectors = (uint)(sbSizeInBytes / imagePlugin.Info.SectorSize); + break; + case EXT2_OS_LITES: + extOs = "Lites"; - if(sbSizeInBytes % imagePlugin.Info.SectorSize > 0) - sbSizeInSectors++; + break; + case EXT2_OS_MASIX: + extOs = "MasIX"; - ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; - uint sbOff = SB_POS % imagePlugin.Info.SectorSize; + break; + default: + extOs = $"Unknown OS ({supblk.creator_os})"; - ErrorNumber errno = - imagePlugin.ReadSectors(sbSectorOff + partition.Start, sbSizeInSectors, out byte[] sbSector); + break; + } - if(errno != ErrorNumber.NoError) - return; + XmlFsType.SystemIdentifier = extOs; - byte[] sblock = new byte[sbSizeInBytes]; - Array.Copy(sbSector, sbOff, sblock, 0, sbSizeInBytes); - SuperBlock supblk = Marshal.ByteArrayToStructureLittleEndian(sblock); + if(supblk.mkfs_t > 0) + { + sb.AppendFormat("Volume was created on {0} for {1}", DateHandlers.UnixUnsignedToDateTime(supblk.mkfs_t), + extOs).AppendLine(); - XmlFsType = new FileSystemType(); + XmlFsType.CreationDate = DateHandlers.UnixUnsignedToDateTime(supblk.mkfs_t); + XmlFsType.CreationDateSpecified = true; + } + else + sb.AppendFormat("Volume was created for {0}", extOs).AppendLine(); - switch(supblk.magic) - { - case EXT2_MAGIC_OLD: - sb.AppendLine("ext2 (old) filesystem"); - XmlFsType.Type = "ext2"; + byte[] tempBytes = new byte[8]; + ulong blocks, reserved, free; - break; - case EXT2_MAGIC: - ext3 |= (supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL || - (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) == EXT3_FEATURE_INCOMPAT_RECOVER || - (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == - EXT3_FEATURE_INCOMPAT_JOURNAL_DEV; + if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT) + { + byte[] tempLo = BitConverter.GetBytes(supblk.blocks); + byte[] tempHi = BitConverter.GetBytes(supblk.blocks_hi); + tempBytes[0] = tempLo[0]; + tempBytes[1] = tempLo[1]; + tempBytes[2] = tempLo[2]; + tempBytes[3] = tempLo[3]; + tempBytes[4] = tempHi[0]; + tempBytes[5] = tempHi[1]; + tempBytes[6] = tempHi[2]; + tempBytes[7] = tempHi[3]; + blocks = BitConverter.ToUInt64(tempBytes, 0); - if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) == EXT4_FEATURE_RO_COMPAT_HUGE_FILE || - (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) == EXT4_FEATURE_RO_COMPAT_GDT_CSUM || - (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) == EXT4_FEATURE_RO_COMPAT_DIR_NLINK || - (supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) == - EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE || - (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT || - (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_MMP) == EXT4_FEATURE_INCOMPAT_MMP || - (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) == EXT4_FEATURE_INCOMPAT_FLEX_BG || - (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EA_INODE) == EXT4_FEATURE_INCOMPAT_EA_INODE || - (supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) == EXT4_FEATURE_INCOMPAT_DIRDATA) - { - ext3 = false; - ext4 = true; - } + tempLo = BitConverter.GetBytes(supblk.reserved_blocks); + tempHi = BitConverter.GetBytes(supblk.reserved_blocks_hi); + tempBytes[0] = tempLo[0]; + tempBytes[1] = tempLo[1]; + tempBytes[2] = tempLo[2]; + tempBytes[3] = tempLo[3]; + tempBytes[4] = tempHi[0]; + tempBytes[5] = tempHi[1]; + tempBytes[6] = tempHi[2]; + tempBytes[7] = tempHi[3]; + reserved = BitConverter.ToUInt64(tempBytes, 0); - newExt2 |= !ext3 && !ext4; + tempLo = BitConverter.GetBytes(supblk.free_blocks); + tempHi = BitConverter.GetBytes(supblk.free_blocks_hi); + tempBytes[0] = tempLo[0]; + tempBytes[1] = tempLo[1]; + tempBytes[2] = tempLo[2]; + tempBytes[3] = tempLo[3]; + tempBytes[4] = tempHi[0]; + tempBytes[5] = tempHi[1]; + tempBytes[6] = tempHi[2]; + tempBytes[7] = tempHi[3]; + free = BitConverter.ToUInt64(tempBytes, 0); + } + else + { + blocks = supblk.blocks; + reserved = supblk.reserved_blocks; + free = supblk.free_blocks; + } - if(newExt2) - { - sb.AppendLine("ext2 filesystem"); - XmlFsType.Type = "ext2"; - } + if(supblk.block_size == 0) // Then it is 1024 bytes + supblk.block_size = 1024; - if(ext3) - { - sb.AppendLine("ext3 filesystem"); - XmlFsType.Type = "ext3"; - } + sb.AppendFormat("Volume has {0} blocks of {1} bytes, for a total of {2} bytes", blocks, + 1024 << (int)supblk.block_size, blocks * (ulong)(1024 << (int)supblk.block_size)). + AppendLine(); - if(ext4) - { - sb.AppendLine("ext4 filesystem"); - XmlFsType.Type = "ext4"; - } + XmlFsType.Clusters = blocks; + XmlFsType.ClusterSize = (uint)(1024 << (int)supblk.block_size); - break; - default: - information = "Not a ext2/3/4 filesystem" + Environment.NewLine; + if(supblk.mount_t > 0 || + supblk.mount_c > 0) + { + if(supblk.mount_t > 0) + sb.AppendFormat("Last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.mount_t)). + AppendLine(); - return; - } - - string extOs; - - switch(supblk.creator_os) - { - case EXT2_OS_FREEBSD: - extOs = "FreeBSD"; - - break; - case EXT2_OS_HURD: - extOs = "Hurd"; - - break; - case EXT2_OS_LINUX: - extOs = "Linux"; - - break; - case EXT2_OS_LITES: - extOs = "Lites"; - - break; - case EXT2_OS_MASIX: - extOs = "MasIX"; - - break; - default: - extOs = $"Unknown OS ({supblk.creator_os})"; - - break; - } - - XmlFsType.SystemIdentifier = extOs; - - if(supblk.mkfs_t > 0) - { - sb.AppendFormat("Volume was created on {0} for {1}", DateHandlers.UnixUnsignedToDateTime(supblk.mkfs_t), - extOs).AppendLine(); - - XmlFsType.CreationDate = DateHandlers.UnixUnsignedToDateTime(supblk.mkfs_t); - XmlFsType.CreationDateSpecified = true; - } + if(supblk.max_mount_c != -1) + sb.AppendFormat("Volume has been mounted {0} times of a maximum of {1} mounts before checking", + supblk.mount_c, supblk.max_mount_c).AppendLine(); else - sb.AppendFormat("Volume was created for {0}", extOs).AppendLine(); + sb.AppendFormat("Volume has been mounted {0} times with no maximum no. of mounts before checking", + supblk.mount_c).AppendLine(); - byte[] tempBytes = new byte[8]; - ulong blocks, reserved, free; + if(!string.IsNullOrEmpty(StringHandlers.CToString(supblk.last_mount_dir, Encoding))) + sb.AppendFormat("Last mounted on: \"{0}\"", + StringHandlers.CToString(supblk.last_mount_dir, Encoding)).AppendLine(); - if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT) - { - byte[] tempLo = BitConverter.GetBytes(supblk.blocks); - byte[] tempHi = BitConverter.GetBytes(supblk.blocks_hi); - tempBytes[0] = tempLo[0]; - tempBytes[1] = tempLo[1]; - tempBytes[2] = tempLo[2]; - tempBytes[3] = tempLo[3]; - tempBytes[4] = tempHi[0]; - tempBytes[5] = tempHi[1]; - tempBytes[6] = tempHi[2]; - tempBytes[7] = tempHi[3]; - blocks = BitConverter.ToUInt64(tempBytes, 0); + if(!string.IsNullOrEmpty(StringHandlers.CToString(supblk.mount_options, Encoding))) + sb.AppendFormat("Last used mount options were: {0}", + StringHandlers.CToString(supblk.mount_options, Encoding)).AppendLine(); + } + else + { + sb.AppendLine("Volume has never been mounted"); - tempLo = BitConverter.GetBytes(supblk.reserved_blocks); - tempHi = BitConverter.GetBytes(supblk.reserved_blocks_hi); - tempBytes[0] = tempLo[0]; - tempBytes[1] = tempLo[1]; - tempBytes[2] = tempLo[2]; - tempBytes[3] = tempLo[3]; - tempBytes[4] = tempHi[0]; - tempBytes[5] = tempHi[1]; - tempBytes[6] = tempHi[2]; - tempBytes[7] = tempHi[3]; - reserved = BitConverter.ToUInt64(tempBytes, 0); - - tempLo = BitConverter.GetBytes(supblk.free_blocks); - tempHi = BitConverter.GetBytes(supblk.free_blocks_hi); - tempBytes[0] = tempLo[0]; - tempBytes[1] = tempLo[1]; - tempBytes[2] = tempLo[2]; - tempBytes[3] = tempLo[3]; - tempBytes[4] = tempHi[0]; - tempBytes[5] = tempHi[1]; - tempBytes[6] = tempHi[2]; - tempBytes[7] = tempHi[3]; - free = BitConverter.ToUInt64(tempBytes, 0); - } + if(supblk.max_mount_c != -1) + sb.AppendFormat("Volume can be mounted {0} times before checking", supblk.max_mount_c).AppendLine(); else - { - blocks = supblk.blocks; - reserved = supblk.reserved_blocks; - free = supblk.free_blocks; - } + sb.AppendLine("Volume has no maximum no. of mounts before checking"); + } - if(supblk.block_size == 0) // Then it is 1024 bytes - supblk.block_size = 1024; + if(supblk.check_t > 0) + if(supblk.check_inv > 0) + sb.AppendFormat("Last checked on {0} (should check every {1} seconds)", + DateHandlers.UnixUnsignedToDateTime(supblk.check_t), supblk.check_inv).AppendLine(); + else + sb.AppendFormat("Last checked on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.check_t)). + AppendLine(); + else + { + if(supblk.check_inv > 0) + sb.AppendFormat("Volume has never been checked (should check every {0})", supblk.check_inv). + AppendLine(); + else + sb.AppendLine("Volume has never been checked"); + } - sb.AppendFormat("Volume has {0} blocks of {1} bytes, for a total of {2} bytes", blocks, - 1024 << (int)supblk.block_size, blocks * (ulong)(1024 << (int)supblk.block_size)). + if(supblk.write_t > 0) + { + sb.AppendFormat("Last written on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.write_t)). AppendLine(); - XmlFsType.Clusters = blocks; - XmlFsType.ClusterSize = (uint)(1024 << (int)supblk.block_size); - - if(supblk.mount_t > 0 || - supblk.mount_c > 0) - { - if(supblk.mount_t > 0) - sb.AppendFormat("Last mounted on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.mount_t)). - AppendLine(); - - if(supblk.max_mount_c != -1) - sb.AppendFormat("Volume has been mounted {0} times of a maximum of {1} mounts before checking", - supblk.mount_c, supblk.max_mount_c).AppendLine(); - else - sb.AppendFormat("Volume has been mounted {0} times with no maximum no. of mounts before checking", - supblk.mount_c).AppendLine(); - - if(!string.IsNullOrEmpty(StringHandlers.CToString(supblk.last_mount_dir, Encoding))) - sb.AppendFormat("Last mounted on: \"{0}\"", - StringHandlers.CToString(supblk.last_mount_dir, Encoding)).AppendLine(); - - if(!string.IsNullOrEmpty(StringHandlers.CToString(supblk.mount_options, Encoding))) - sb.AppendFormat("Last used mount options were: {0}", - StringHandlers.CToString(supblk.mount_options, Encoding)).AppendLine(); - } - else - { - sb.AppendLine("Volume has never been mounted"); - - if(supblk.max_mount_c != -1) - sb.AppendFormat("Volume can be mounted {0} times before checking", supblk.max_mount_c).AppendLine(); - else - sb.AppendLine("Volume has no maximum no. of mounts before checking"); - } - - if(supblk.check_t > 0) - if(supblk.check_inv > 0) - sb.AppendFormat("Last checked on {0} (should check every {1} seconds)", - DateHandlers.UnixUnsignedToDateTime(supblk.check_t), supblk.check_inv).AppendLine(); - else - sb.AppendFormat("Last checked on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.check_t)). - AppendLine(); - else - { - if(supblk.check_inv > 0) - sb.AppendFormat("Volume has never been checked (should check every {0})", supblk.check_inv). - AppendLine(); - else - sb.AppendLine("Volume has never been checked"); - } - - if(supblk.write_t > 0) - { - sb.AppendFormat("Last written on {0}", DateHandlers.UnixUnsignedToDateTime(supblk.write_t)). - AppendLine(); - - XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(supblk.write_t); - XmlFsType.ModificationDateSpecified = true; - } - else - sb.AppendLine("Volume has never been written"); - - XmlFsType.Dirty = true; - - switch(supblk.state) - { - case EXT2_VALID_FS: - sb.AppendLine("Volume is clean"); - XmlFsType.Dirty = false; - - break; - case EXT2_ERROR_FS: - sb.AppendLine("Volume is dirty"); - - break; - case EXT3_ORPHAN_FS: - sb.AppendLine("Volume is recovering orphan files"); - - break; - default: - sb.AppendFormat("Volume is in an unknown state ({0})", supblk.state).AppendLine(); - - break; - } - - if(!string.IsNullOrEmpty(StringHandlers.CToString(supblk.volume_name, Encoding))) - { - sb.AppendFormat("Volume name: \"{0}\"", StringHandlers.CToString(supblk.volume_name, Encoding)). - AppendLine(); - - XmlFsType.VolumeName = StringHandlers.CToString(supblk.volume_name, Encoding); - } - - switch(supblk.err_behaviour) - { - case EXT2_ERRORS_CONTINUE: - sb.AppendLine("On errors, filesystem should continue"); - - break; - case EXT2_ERRORS_RO: - sb.AppendLine("On errors, filesystem should remount read-only"); - - break; - case EXT2_ERRORS_PANIC: - sb.AppendLine("On errors, filesystem should panic"); - - break; - default: - sb.AppendFormat("On errors filesystem will do an unknown thing ({0})", supblk.err_behaviour). - AppendLine(); - - break; - } - - if(supblk.revision > 0) - sb.AppendFormat("Filesystem revision: {0}.{1}", supblk.revision, supblk.minor_revision).AppendLine(); - - if(supblk.uuid != Guid.Empty) - { - sb.AppendFormat("Volume UUID: {0}", supblk.uuid).AppendLine(); - XmlFsType.VolumeSerial = supblk.uuid.ToString(); - } - - if(supblk.kbytes_written > 0) - sb.AppendFormat("{0} KiB has been written on volume", supblk.kbytes_written).AppendLine(); - - sb.AppendFormat("{0} reserved and {1} free blocks", reserved, free).AppendLine(); - XmlFsType.FreeClusters = free; - XmlFsType.FreeClustersSpecified = true; - - sb.AppendFormat("{0} inodes with {1} free inodes ({2}%)", supblk.inodes, supblk.free_inodes, - supblk.free_inodes * 100 / supblk.inodes).AppendLine(); - - if(supblk.first_inode > 0) - sb.AppendFormat("First inode is {0}", supblk.first_inode).AppendLine(); - - if(supblk.frag_size > 0) - sb.AppendFormat("{0} bytes per fragment", supblk.frag_size).AppendLine(); - - if(supblk.blocks_per_grp > 0 && - supblk.flags_per_grp > 0 && - supblk.inodes_per_grp > 0) - sb.AppendFormat("{0} blocks, {1} flags and {2} inodes per group", supblk.blocks_per_grp, - supblk.flags_per_grp, supblk.inodes_per_grp).AppendLine(); - - if(supblk.first_block > 0) - sb.AppendFormat("{0} is first data block", supblk.first_block).AppendLine(); - - sb.AppendFormat("Default UID: {0}, GID: {1}", supblk.default_uid, supblk.default_gid).AppendLine(); - - if(supblk.block_group_no > 0) - sb.AppendFormat("Block group number is {0}", supblk.block_group_no).AppendLine(); - - if(supblk.desc_grp_size > 0) - sb.AppendFormat("Group descriptor size is {0} bytes", supblk.desc_grp_size).AppendLine(); - - if(supblk.first_meta_bg > 0) - sb.AppendFormat("First metablock group is {0}", supblk.first_meta_bg).AppendLine(); - - if(supblk.raid_stride > 0) - sb.AppendFormat("RAID stride: {0}", supblk.raid_stride).AppendLine(); - - if(supblk.raid_stripe_width > 0) - sb.AppendFormat("{0} blocks on all data disks", supblk.raid_stripe_width).AppendLine(); - - if(supblk.mmp_interval > 0 && - supblk.mmp_block > 0) - sb.AppendFormat("{0} seconds for multi-mount protection wait, on block {1}", supblk.mmp_interval, - supblk.mmp_block).AppendLine(); - - if(supblk.flex_bg_grp_size > 0) - sb.AppendFormat("{0} Flexible block group size", supblk.flex_bg_grp_size).AppendLine(); - - if(supblk.hash_seed_1 > 0 && - supblk.hash_seed_2 > 0 && - supblk.hash_seed_3 > 0 && - supblk.hash_seed_4 > 0) - sb.AppendFormat("Hash seed: {0:X8}{1:X8}{2:X8}{3:X8}, version {4}", supblk.hash_seed_1, - supblk.hash_seed_2, supblk.hash_seed_3, supblk.hash_seed_4, supblk.hash_version). - AppendLine(); - - if((supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL || - (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) - { - sb.AppendLine("Volume is journaled"); - - if(supblk.journal_uuid != Guid.Empty) - sb.AppendFormat("Journal UUID: {0}", supblk.journal_uuid).AppendLine(); - - sb.AppendFormat("Journal has inode {0}", supblk.journal_inode).AppendLine(); - - if((supblk.ftr_compat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV && - supblk.journal_dev > 0) - sb.AppendFormat("Journal is on device {0}", supblk.journal_dev).AppendLine(); - - if(supblk.jnl_backup_type > 0) - sb.AppendFormat("Journal backup type: {0}", supblk.jnl_backup_type).AppendLine(); - - if(supblk.last_orphan > 0) - sb.AppendFormat("Last orphaned inode is {0}", supblk.last_orphan).AppendLine(); - else - sb.AppendLine("There are no orphaned inodes"); - } - - if(ext4) - { - if(supblk.snapshot_id > 0) - sb. - AppendFormat("Active snapshot has ID {0}, on inode {1}, with {2} blocks reserved, list starting on block {3}", - supblk.snapshot_id, supblk.snapshot_inum, supblk.snapshot_blocks, - supblk.snapshot_list).AppendLine(); - - if(supblk.error_count > 0) - { - sb.AppendFormat("{0} errors registered", supblk.error_count).AppendLine(); - - sb.AppendFormat("First error occurred on {0}, last on {1}", - DateHandlers.UnixUnsignedToDateTime(supblk.first_error_t), - DateHandlers.UnixUnsignedToDateTime(supblk.last_error_t)).AppendLine(); - - sb.AppendFormat("First error inode is {0}, last is {1}", supblk.first_error_inode, - supblk.last_error_inode).AppendLine(); - - sb.AppendFormat("First error block is {0}, last is {1}", supblk.first_error_block, - supblk.last_error_block).AppendLine(); - - sb.AppendFormat("First error function is \"{0}\", last is \"{1}\"", supblk.first_error_func, - supblk.last_error_func).AppendLine(); - } - } - - sb.AppendFormat("Flags…:").AppendLine(); - - if((supblk.flags & EXT2_FLAGS_SIGNED_HASH) == EXT2_FLAGS_SIGNED_HASH) - sb.AppendLine("Signed directory hash is in use"); - - if((supblk.flags & EXT2_FLAGS_UNSIGNED_HASH) == EXT2_FLAGS_UNSIGNED_HASH) - sb.AppendLine("Unsigned directory hash is in use"); - - if((supblk.flags & EXT2_FLAGS_TEST_FILESYS) == EXT2_FLAGS_TEST_FILESYS) - sb.AppendLine("Volume is testing development code"); - - if((supblk.flags & 0xFFFFFFF8) != 0) - sb.AppendFormat("Unknown set flags: {0:X8}", supblk.flags); - - sb.AppendLine(); - - sb.AppendFormat("Default mount options…:").AppendLine(); - - if((supblk.default_mnt_opts & EXT2_DEFM_DEBUG) == EXT2_DEFM_DEBUG) - sb.AppendLine("(debug): Enable debugging code"); - - if((supblk.default_mnt_opts & EXT2_DEFM_BSDGROUPS) == EXT2_DEFM_BSDGROUPS) - sb.AppendLine("(bsdgroups): Emulate BSD behaviour when creating new files"); - - if((supblk.default_mnt_opts & EXT2_DEFM_XATTR_USER) == EXT2_DEFM_XATTR_USER) - sb.AppendLine("(user_xattr): Enable user-specified extended attributes"); - - if((supblk.default_mnt_opts & EXT2_DEFM_ACL) == EXT2_DEFM_ACL) - sb.AppendLine("(acl): Enable POSIX ACLs"); - - if((supblk.default_mnt_opts & EXT2_DEFM_UID16) == EXT2_DEFM_UID16) - sb.AppendLine("(uid16): Disable 32bit UIDs and GIDs"); - - if((supblk.default_mnt_opts & EXT3_DEFM_JMODE_DATA) == EXT3_DEFM_JMODE_DATA) - sb.AppendLine("(journal_data): Journal data and metadata"); - - if((supblk.default_mnt_opts & EXT3_DEFM_JMODE_ORDERED) == EXT3_DEFM_JMODE_ORDERED) - sb.AppendLine("(journal_data_ordered): Write data before journaling metadata"); - - if((supblk.default_mnt_opts & EXT3_DEFM_JMODE_WBACK) == EXT3_DEFM_JMODE_WBACK) - sb.AppendLine("(journal_data_writeback): Write journal before data"); - - if((supblk.default_mnt_opts & 0xFFFFFE20) != 0) - sb.AppendFormat("Unknown set default mount options: {0:X8}", supblk.default_mnt_opts); - - sb.AppendLine(); - - sb.AppendFormat("Compatible features…:").AppendLine(); - - if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_DIR_PREALLOC) == EXT2_FEATURE_COMPAT_DIR_PREALLOC) - sb.AppendLine("Pre-allocate directories"); - - if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES) == EXT2_FEATURE_COMPAT_IMAGIC_INODES) - sb.AppendLine("imagic inodes ?"); - - if((supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL) - sb.AppendLine("Has journal (ext3)"); - - if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) == EXT2_FEATURE_COMPAT_EXT_ATTR) - sb.AppendLine("Has extended attribute blocks"); - - if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) == EXT2_FEATURE_COMPAT_RESIZE_INO) - sb.AppendLine("Has online filesystem resize reservations"); - - if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) == EXT2_FEATURE_COMPAT_DIR_INDEX) - sb.AppendLine("Can use hashed indexes on directories"); - - if((supblk.ftr_compat & 0xFFFFFFC0) != 0) - sb.AppendFormat("Unknown compatible features: {0:X8}", supblk.ftr_compat); - - sb.AppendLine(); - - sb.AppendFormat("Compatible features if read-only…:").AppendLine(); - - if((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) == EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) - sb.AppendLine("Reduced number of superblocks"); - - if((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE) == EXT2_FEATURE_RO_COMPAT_LARGE_FILE) - sb.AppendLine("Can have files bigger than 2GiB"); - - if((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_BTREE_DIR) == EXT2_FEATURE_RO_COMPAT_BTREE_DIR) - sb.AppendLine("Uses B-Tree for directories"); - - if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) == EXT4_FEATURE_RO_COMPAT_HUGE_FILE) - sb.AppendLine("Can have files bigger than 2TiB (ext4)"); - - if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) == EXT4_FEATURE_RO_COMPAT_GDT_CSUM) - sb.AppendLine("Group descriptor checksums and sparse inode table (ext4)"); - - if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) == EXT4_FEATURE_RO_COMPAT_DIR_NLINK) - sb.AppendLine("More than 32000 directory entries (ext4)"); - - if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) == EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) - sb.AppendLine("Supports nanosecond timestamps and creation time (ext4)"); - - if((supblk.ftr_ro_compat & 0xFFFFFF80) != 0) - sb.AppendFormat("Unknown read-only compatible features: {0:X8}", supblk.ftr_ro_compat); - - sb.AppendLine(); - - sb.AppendFormat("Incompatible features…:").AppendLine(); - - if((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) == EXT2_FEATURE_INCOMPAT_COMPRESSION) - sb.AppendLine("Uses compression"); - - if((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) == EXT2_FEATURE_INCOMPAT_FILETYPE) - sb.AppendLine("Filetype in directory entries"); - - if((supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) == EXT3_FEATURE_INCOMPAT_RECOVER) - sb.AppendLine("Journal needs recovery (ext3)"); - - if((supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) - sb.AppendLine("Has journal on another device (ext3)"); - - if((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_META_BG) == EXT2_FEATURE_INCOMPAT_META_BG) - sb.AppendLine("Reduced block group backups"); - - if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EXTENTS) == EXT4_FEATURE_INCOMPAT_EXTENTS) - sb.AppendLine("Volume use extents (ext4)"); - - if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT) - sb.AppendLine("Supports volumes bigger than 2^32 blocks (ext4)"); - - if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_MMP) == EXT4_FEATURE_INCOMPAT_MMP) - sb.AppendLine("Multi-mount protection (ext4)"); - - if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) == EXT4_FEATURE_INCOMPAT_FLEX_BG) - sb.AppendLine("Flexible block group metadata location (ext4)"); - - if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EA_INODE) == EXT4_FEATURE_INCOMPAT_EA_INODE) - sb.AppendLine("Extended attributes can reside in inode (ext4)"); - - if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) == EXT4_FEATURE_INCOMPAT_DIRDATA) - sb.AppendLine("Data can reside in directory entry (ext4)"); - - if((supblk.ftr_incompat & 0xFFFFF020) != 0) - sb.AppendFormat("Unknown incompatible features: {0:X8}", supblk.ftr_incompat); - - information = sb.ToString(); + XmlFsType.ModificationDate = DateHandlers.UnixUnsignedToDateTime(supblk.write_t); + XmlFsType.ModificationDateSpecified = true; } + else + sb.AppendLine("Volume has never been written"); - /// ext2/3/4 superblock - [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] - struct SuperBlock + XmlFsType.Dirty = true; + + switch(supblk.state) { - /// 0x000, inodes on volume - public readonly uint inodes; - /// 0x004, blocks on volume - public readonly uint blocks; - /// 0x008, reserved blocks - public readonly uint reserved_blocks; - /// 0x00C, free blocks count - public readonly uint free_blocks; - /// 0x010, free inodes count - public readonly uint free_inodes; - /// 0x014, first data block - public readonly uint first_block; - /// 0x018, block size - public uint block_size; - /// 0x01C, fragment size - public readonly int frag_size; - /// 0x020, blocks per group - public readonly uint blocks_per_grp; - /// 0x024, fragments per group - public readonly uint flags_per_grp; - /// 0x028, inodes per group - public readonly uint inodes_per_grp; - /// 0x02C, last mount time - public readonly uint mount_t; - /// 0x030, last write time - public readonly uint write_t; - /// 0x034, mounts count - public readonly ushort mount_c; - /// 0x036, max mounts - public readonly short max_mount_c; - /// 0x038, (little endian) - public readonly ushort magic; - /// 0x03A, filesystem state - public readonly ushort state; - /// 0x03C, behaviour on errors - public readonly ushort err_behaviour; - /// 0x03E, From 0.5b onward - public readonly ushort minor_revision; - /// 0x040, last check time - public readonly uint check_t; - /// 0x044, max time between checks - public readonly uint check_inv; + case EXT2_VALID_FS: + sb.AppendLine("Volume is clean"); + XmlFsType.Dirty = false; - // From 0.5a onward - /// 0x048, Creation OS - public readonly uint creator_os; - /// 0x04C, Revison level - public readonly uint revision; - /// 0x050, Default UID for reserved blocks - public readonly ushort default_uid; - /// 0x052, Default GID for reserved blocks - public readonly ushort default_gid; + break; + case EXT2_ERROR_FS: + sb.AppendLine("Volume is dirty"); - // From 0.5b onward - /// 0x054, First unreserved inode - public readonly uint first_inode; - /// 0x058, inode size - public readonly ushort inode_size; - /// 0x05A, Block group number of THIS superblock - public readonly ushort block_group_no; - /// 0x05C, Compatible features set - public readonly uint ftr_compat; - /// 0x060, Incompatible features set - public readonly uint ftr_incompat; + break; + case EXT3_ORPHAN_FS: + sb.AppendLine("Volume is recovering orphan files"); - // Found on Linux 2.0.40 - /// 0x064, Read-only compatible features set - public readonly uint ftr_ro_compat; + break; + default: + sb.AppendFormat("Volume is in an unknown state ({0})", supblk.state).AppendLine(); - // Found on Linux 2.1.132 - /// 0x068, 16 bytes, UUID - public readonly Guid uuid; - /// 0x078, 16 bytes, volume name - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] volume_name; - /// 0x088, 64 bytes, where last mounted - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] last_mount_dir; - /// 0x0C8, Usage bitmap algorithm, for compression - public readonly uint algo_usage_bmp; - /// 0x0CC, Block to try to preallocate - public readonly byte prealloc_blks; - /// 0x0CD, Blocks to try to preallocate for directories - public readonly byte prealloc_dir_blks; - /// 0x0CE, Per-group desc for online growth - public readonly ushort rsrvd_gdt_blocks; - - // Found on Linux 2.4 - // ext3 - /// 0x0D0, 16 bytes, UUID of journal superblock - public readonly Guid journal_uuid; - /// 0x0E0, inode no. of journal file - public readonly uint journal_inode; - /// 0x0E4, device no. of journal file - public readonly uint journal_dev; - /// 0x0E8, Start of list of inodes to delete - public readonly uint last_orphan; - /// 0x0EC, First byte of 128bit HTREE hash seed - public readonly uint hash_seed_1; - /// 0x0F0, Second byte of 128bit HTREE hash seed - public readonly uint hash_seed_2; - /// 0x0F4, Third byte of 128bit HTREE hash seed - public readonly uint hash_seed_3; - /// 0x0F8, Fourth byte of 128bit HTREE hash seed - public readonly uint hash_seed_4; - /// 0x0FC, Hash version - public readonly byte hash_version; - /// 0x0FD, Journal backup type - public readonly byte jnl_backup_type; - /// 0x0FE, Size of group descriptor - public readonly ushort desc_grp_size; - /// 0x100, Default mount options - public readonly uint default_mnt_opts; - /// 0x104, First metablock block group - public readonly uint first_meta_bg; - - // Introduced with ext4, some can be ext3 - /// 0x108, Filesystem creation time - public readonly uint mkfs_t; - - /// Backup of the journal inode - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] - public readonly uint[] jnl_blocks; - - // Following 3 fields are valid if EXT4_FEATURE_COMPAT_64BIT is set - /// 0x14C, High 32bits of blocks no. - public readonly uint blocks_hi; - /// 0x150, High 32bits of reserved blocks no. - public readonly uint reserved_blocks_hi; - /// 0x154, High 32bits of free blocks no. - public readonly uint free_blocks_hi; - /// 0x158, inodes minimal size in bytes - public readonly ushort min_inode_size; - /// 0x15A, Bytes reserved by new inodes - public readonly ushort rsv_inode_size; - /// 0x15C, Flags - public readonly uint flags; - /// 0x160, RAID stride - public readonly ushort raid_stride; - /// 0x162, Waiting seconds in MMP check - public readonly ushort mmp_interval; - /// 0x164, Block for multi-mount protection - public readonly ulong mmp_block; - /// 0x16C, Blocks on all data disks (N*stride) - public readonly uint raid_stripe_width; - /// 0x170, FLEX_BG group size - public readonly byte flex_bg_grp_size; - /// 0x171 Metadata checksum algorithm - public readonly byte checksum_type; - /// 0x172 Versioning level for encryption - public readonly byte encryption_level; - /// 0x173 Padding - public readonly ushort padding; - - // Following are introduced with ext4 - /// 0x174, Kibibytes written in volume lifetime - public readonly ulong kbytes_written; - /// 0x17C, Active snapshot inode number - public readonly uint snapshot_inum; - /// 0x180, Active snapshot sequential ID - public readonly uint snapshot_id; - /// 0x184, Reserved blocks for active snapshot's future use - public readonly ulong snapshot_blocks; - /// 0x18C, inode number of the on-disk start of the snapshot list - public readonly uint snapshot_list; - - // Optional ext4 error-handling features - /// 0x190, total registered filesystem errors - public readonly uint error_count; - /// 0x194, time on first error - public readonly uint first_error_t; - /// 0x198, inode involved in first error - public readonly uint first_error_inode; - /// 0x19C, block involved of first error - public readonly ulong first_error_block; - /// 0x1A0, 32 bytes, function where the error happened - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] first_error_func; - /// 0x1B0, line number where error happened - public readonly uint first_error_line; - /// 0x1B4, time of most recent error - public readonly uint last_error_t; - /// 0x1B8, inode involved in last error - public readonly uint last_error_inode; - /// 0x1BC, line number where error happened - public readonly uint last_error_line; - /// 0x1C0, block involved of last error - public readonly ulong last_error_block; - /// 0x1C8, 32 bytes, function where the error happened - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] last_error_func; - - // End of optional error-handling features - - // 0x1D8, 64 bytes, last used mount options - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] mount_options; - - /// Inode for user quota - public readonly uint usr_quota_inum; - /// Inode for group quota - public readonly uint grp_quota_inum; - /// Overhead clusters in volume - public readonly uint overhead_clusters; - /// Groups with sparse_super2 SBs - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] - public readonly uint[] backup_bgs; - /// Encryption algorithms in use - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] - public readonly byte[] encrypt_algos; - /// Salt used for string2key algorithm - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] encrypt_pw_salt; - /// Inode number of lost+found - public readonly uint lpf_inum; - /// Inode number for tracking project quota - public readonly uint prj_quota_inum; - /// crc32c(uuid) if csum_seed is set - public readonly uint checksum_seed; - /// Reserved - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 98)] - public readonly byte[] reserved; - /// crc32c(superblock) - public readonly uint checksum; + break; } + + if(!string.IsNullOrEmpty(StringHandlers.CToString(supblk.volume_name, Encoding))) + { + sb.AppendFormat("Volume name: \"{0}\"", StringHandlers.CToString(supblk.volume_name, Encoding)). + AppendLine(); + + XmlFsType.VolumeName = StringHandlers.CToString(supblk.volume_name, Encoding); + } + + switch(supblk.err_behaviour) + { + case EXT2_ERRORS_CONTINUE: + sb.AppendLine("On errors, filesystem should continue"); + + break; + case EXT2_ERRORS_RO: + sb.AppendLine("On errors, filesystem should remount read-only"); + + break; + case EXT2_ERRORS_PANIC: + sb.AppendLine("On errors, filesystem should panic"); + + break; + default: + sb.AppendFormat("On errors filesystem will do an unknown thing ({0})", supblk.err_behaviour). + AppendLine(); + + break; + } + + if(supblk.revision > 0) + sb.AppendFormat("Filesystem revision: {0}.{1}", supblk.revision, supblk.minor_revision).AppendLine(); + + if(supblk.uuid != Guid.Empty) + { + sb.AppendFormat("Volume UUID: {0}", supblk.uuid).AppendLine(); + XmlFsType.VolumeSerial = supblk.uuid.ToString(); + } + + if(supblk.kbytes_written > 0) + sb.AppendFormat("{0} KiB has been written on volume", supblk.kbytes_written).AppendLine(); + + sb.AppendFormat("{0} reserved and {1} free blocks", reserved, free).AppendLine(); + XmlFsType.FreeClusters = free; + XmlFsType.FreeClustersSpecified = true; + + sb.AppendFormat("{0} inodes with {1} free inodes ({2}%)", supblk.inodes, supblk.free_inodes, + supblk.free_inodes * 100 / supblk.inodes).AppendLine(); + + if(supblk.first_inode > 0) + sb.AppendFormat("First inode is {0}", supblk.first_inode).AppendLine(); + + if(supblk.frag_size > 0) + sb.AppendFormat("{0} bytes per fragment", supblk.frag_size).AppendLine(); + + if(supblk.blocks_per_grp > 0 && + supblk.flags_per_grp > 0 && + supblk.inodes_per_grp > 0) + sb.AppendFormat("{0} blocks, {1} flags and {2} inodes per group", supblk.blocks_per_grp, + supblk.flags_per_grp, supblk.inodes_per_grp).AppendLine(); + + if(supblk.first_block > 0) + sb.AppendFormat("{0} is first data block", supblk.first_block).AppendLine(); + + sb.AppendFormat("Default UID: {0}, GID: {1}", supblk.default_uid, supblk.default_gid).AppendLine(); + + if(supblk.block_group_no > 0) + sb.AppendFormat("Block group number is {0}", supblk.block_group_no).AppendLine(); + + if(supblk.desc_grp_size > 0) + sb.AppendFormat("Group descriptor size is {0} bytes", supblk.desc_grp_size).AppendLine(); + + if(supblk.first_meta_bg > 0) + sb.AppendFormat("First metablock group is {0}", supblk.first_meta_bg).AppendLine(); + + if(supblk.raid_stride > 0) + sb.AppendFormat("RAID stride: {0}", supblk.raid_stride).AppendLine(); + + if(supblk.raid_stripe_width > 0) + sb.AppendFormat("{0} blocks on all data disks", supblk.raid_stripe_width).AppendLine(); + + if(supblk.mmp_interval > 0 && + supblk.mmp_block > 0) + sb.AppendFormat("{0} seconds for multi-mount protection wait, on block {1}", supblk.mmp_interval, + supblk.mmp_block).AppendLine(); + + if(supblk.flex_bg_grp_size > 0) + sb.AppendFormat("{0} Flexible block group size", supblk.flex_bg_grp_size).AppendLine(); + + if(supblk.hash_seed_1 > 0 && + supblk.hash_seed_2 > 0 && + supblk.hash_seed_3 > 0 && + supblk.hash_seed_4 > 0) + sb.AppendFormat("Hash seed: {0:X8}{1:X8}{2:X8}{3:X8}, version {4}", supblk.hash_seed_1, + supblk.hash_seed_2, supblk.hash_seed_3, supblk.hash_seed_4, supblk.hash_version). + AppendLine(); + + if((supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL || + (supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + { + sb.AppendLine("Volume is journaled"); + + if(supblk.journal_uuid != Guid.Empty) + sb.AppendFormat("Journal UUID: {0}", supblk.journal_uuid).AppendLine(); + + sb.AppendFormat("Journal has inode {0}", supblk.journal_inode).AppendLine(); + + if((supblk.ftr_compat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV && + supblk.journal_dev > 0) + sb.AppendFormat("Journal is on device {0}", supblk.journal_dev).AppendLine(); + + if(supblk.jnl_backup_type > 0) + sb.AppendFormat("Journal backup type: {0}", supblk.jnl_backup_type).AppendLine(); + + if(supblk.last_orphan > 0) + sb.AppendFormat("Last orphaned inode is {0}", supblk.last_orphan).AppendLine(); + else + sb.AppendLine("There are no orphaned inodes"); + } + + if(ext4) + { + if(supblk.snapshot_id > 0) + sb. + AppendFormat("Active snapshot has ID {0}, on inode {1}, with {2} blocks reserved, list starting on block {3}", + supblk.snapshot_id, supblk.snapshot_inum, supblk.snapshot_blocks, + supblk.snapshot_list).AppendLine(); + + if(supblk.error_count > 0) + { + sb.AppendFormat("{0} errors registered", supblk.error_count).AppendLine(); + + sb.AppendFormat("First error occurred on {0}, last on {1}", + DateHandlers.UnixUnsignedToDateTime(supblk.first_error_t), + DateHandlers.UnixUnsignedToDateTime(supblk.last_error_t)).AppendLine(); + + sb.AppendFormat("First error inode is {0}, last is {1}", supblk.first_error_inode, + supblk.last_error_inode).AppendLine(); + + sb.AppendFormat("First error block is {0}, last is {1}", supblk.first_error_block, + supblk.last_error_block).AppendLine(); + + sb.AppendFormat("First error function is \"{0}\", last is \"{1}\"", supblk.first_error_func, + supblk.last_error_func).AppendLine(); + } + } + + sb.AppendFormat("Flags…:").AppendLine(); + + if((supblk.flags & EXT2_FLAGS_SIGNED_HASH) == EXT2_FLAGS_SIGNED_HASH) + sb.AppendLine("Signed directory hash is in use"); + + if((supblk.flags & EXT2_FLAGS_UNSIGNED_HASH) == EXT2_FLAGS_UNSIGNED_HASH) + sb.AppendLine("Unsigned directory hash is in use"); + + if((supblk.flags & EXT2_FLAGS_TEST_FILESYS) == EXT2_FLAGS_TEST_FILESYS) + sb.AppendLine("Volume is testing development code"); + + if((supblk.flags & 0xFFFFFFF8) != 0) + sb.AppendFormat("Unknown set flags: {0:X8}", supblk.flags); + + sb.AppendLine(); + + sb.AppendFormat("Default mount options…:").AppendLine(); + + if((supblk.default_mnt_opts & EXT2_DEFM_DEBUG) == EXT2_DEFM_DEBUG) + sb.AppendLine("(debug): Enable debugging code"); + + if((supblk.default_mnt_opts & EXT2_DEFM_BSDGROUPS) == EXT2_DEFM_BSDGROUPS) + sb.AppendLine("(bsdgroups): Emulate BSD behaviour when creating new files"); + + if((supblk.default_mnt_opts & EXT2_DEFM_XATTR_USER) == EXT2_DEFM_XATTR_USER) + sb.AppendLine("(user_xattr): Enable user-specified extended attributes"); + + if((supblk.default_mnt_opts & EXT2_DEFM_ACL) == EXT2_DEFM_ACL) + sb.AppendLine("(acl): Enable POSIX ACLs"); + + if((supblk.default_mnt_opts & EXT2_DEFM_UID16) == EXT2_DEFM_UID16) + sb.AppendLine("(uid16): Disable 32bit UIDs and GIDs"); + + if((supblk.default_mnt_opts & EXT3_DEFM_JMODE_DATA) == EXT3_DEFM_JMODE_DATA) + sb.AppendLine("(journal_data): Journal data and metadata"); + + if((supblk.default_mnt_opts & EXT3_DEFM_JMODE_ORDERED) == EXT3_DEFM_JMODE_ORDERED) + sb.AppendLine("(journal_data_ordered): Write data before journaling metadata"); + + if((supblk.default_mnt_opts & EXT3_DEFM_JMODE_WBACK) == EXT3_DEFM_JMODE_WBACK) + sb.AppendLine("(journal_data_writeback): Write journal before data"); + + if((supblk.default_mnt_opts & 0xFFFFFE20) != 0) + sb.AppendFormat("Unknown set default mount options: {0:X8}", supblk.default_mnt_opts); + + sb.AppendLine(); + + sb.AppendFormat("Compatible features…:").AppendLine(); + + if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_DIR_PREALLOC) == EXT2_FEATURE_COMPAT_DIR_PREALLOC) + sb.AppendLine("Pre-allocate directories"); + + if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_IMAGIC_INODES) == EXT2_FEATURE_COMPAT_IMAGIC_INODES) + sb.AppendLine("imagic inodes ?"); + + if((supblk.ftr_compat & EXT3_FEATURE_COMPAT_HAS_JOURNAL) == EXT3_FEATURE_COMPAT_HAS_JOURNAL) + sb.AppendLine("Has journal (ext3)"); + + if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_EXT_ATTR) == EXT2_FEATURE_COMPAT_EXT_ATTR) + sb.AppendLine("Has extended attribute blocks"); + + if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_RESIZE_INO) == EXT2_FEATURE_COMPAT_RESIZE_INO) + sb.AppendLine("Has online filesystem resize reservations"); + + if((supblk.ftr_compat & EXT2_FEATURE_COMPAT_DIR_INDEX) == EXT2_FEATURE_COMPAT_DIR_INDEX) + sb.AppendLine("Can use hashed indexes on directories"); + + if((supblk.ftr_compat & 0xFFFFFFC0) != 0) + sb.AppendFormat("Unknown compatible features: {0:X8}", supblk.ftr_compat); + + sb.AppendLine(); + + sb.AppendFormat("Compatible features if read-only…:").AppendLine(); + + if((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) == EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER) + sb.AppendLine("Reduced number of superblocks"); + + if((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_LARGE_FILE) == EXT2_FEATURE_RO_COMPAT_LARGE_FILE) + sb.AppendLine("Can have files bigger than 2GiB"); + + if((supblk.ftr_ro_compat & EXT2_FEATURE_RO_COMPAT_BTREE_DIR) == EXT2_FEATURE_RO_COMPAT_BTREE_DIR) + sb.AppendLine("Uses B-Tree for directories"); + + if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_HUGE_FILE) == EXT4_FEATURE_RO_COMPAT_HUGE_FILE) + sb.AppendLine("Can have files bigger than 2TiB (ext4)"); + + if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_GDT_CSUM) == EXT4_FEATURE_RO_COMPAT_GDT_CSUM) + sb.AppendLine("Group descriptor checksums and sparse inode table (ext4)"); + + if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_DIR_NLINK) == EXT4_FEATURE_RO_COMPAT_DIR_NLINK) + sb.AppendLine("More than 32000 directory entries (ext4)"); + + if((supblk.ftr_ro_compat & EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) == EXT4_FEATURE_RO_COMPAT_EXTRA_ISIZE) + sb.AppendLine("Supports nanosecond timestamps and creation time (ext4)"); + + if((supblk.ftr_ro_compat & 0xFFFFFF80) != 0) + sb.AppendFormat("Unknown read-only compatible features: {0:X8}", supblk.ftr_ro_compat); + + sb.AppendLine(); + + sb.AppendFormat("Incompatible features…:").AppendLine(); + + if((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_COMPRESSION) == EXT2_FEATURE_INCOMPAT_COMPRESSION) + sb.AppendLine("Uses compression"); + + if((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_FILETYPE) == EXT2_FEATURE_INCOMPAT_FILETYPE) + sb.AppendLine("Filetype in directory entries"); + + if((supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_RECOVER) == EXT3_FEATURE_INCOMPAT_RECOVER) + sb.AppendLine("Journal needs recovery (ext3)"); + + if((supblk.ftr_incompat & EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) == EXT3_FEATURE_INCOMPAT_JOURNAL_DEV) + sb.AppendLine("Has journal on another device (ext3)"); + + if((supblk.ftr_incompat & EXT2_FEATURE_INCOMPAT_META_BG) == EXT2_FEATURE_INCOMPAT_META_BG) + sb.AppendLine("Reduced block group backups"); + + if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EXTENTS) == EXT4_FEATURE_INCOMPAT_EXTENTS) + sb.AppendLine("Volume use extents (ext4)"); + + if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_64BIT) == EXT4_FEATURE_INCOMPAT_64BIT) + sb.AppendLine("Supports volumes bigger than 2^32 blocks (ext4)"); + + if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_MMP) == EXT4_FEATURE_INCOMPAT_MMP) + sb.AppendLine("Multi-mount protection (ext4)"); + + if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_FLEX_BG) == EXT4_FEATURE_INCOMPAT_FLEX_BG) + sb.AppendLine("Flexible block group metadata location (ext4)"); + + if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_EA_INODE) == EXT4_FEATURE_INCOMPAT_EA_INODE) + sb.AppendLine("Extended attributes can reside in inode (ext4)"); + + if((supblk.ftr_incompat & EXT4_FEATURE_INCOMPAT_DIRDATA) == EXT4_FEATURE_INCOMPAT_DIRDATA) + sb.AppendLine("Data can reside in directory entry (ext4)"); + + if((supblk.ftr_incompat & 0xFFFFF020) != 0) + sb.AppendFormat("Unknown incompatible features: {0:X8}", supblk.ftr_incompat); + + information = sb.ToString(); + } + + /// ext2/3/4 superblock + [StructLayout(LayoutKind.Sequential, Pack = 1), SuppressMessage("ReSharper", "InconsistentNaming")] + struct SuperBlock + { + /// 0x000, inodes on volume + public readonly uint inodes; + /// 0x004, blocks on volume + public readonly uint blocks; + /// 0x008, reserved blocks + public readonly uint reserved_blocks; + /// 0x00C, free blocks count + public readonly uint free_blocks; + /// 0x010, free inodes count + public readonly uint free_inodes; + /// 0x014, first data block + public readonly uint first_block; + /// 0x018, block size + public uint block_size; + /// 0x01C, fragment size + public readonly int frag_size; + /// 0x020, blocks per group + public readonly uint blocks_per_grp; + /// 0x024, fragments per group + public readonly uint flags_per_grp; + /// 0x028, inodes per group + public readonly uint inodes_per_grp; + /// 0x02C, last mount time + public readonly uint mount_t; + /// 0x030, last write time + public readonly uint write_t; + /// 0x034, mounts count + public readonly ushort mount_c; + /// 0x036, max mounts + public readonly short max_mount_c; + /// 0x038, (little endian) + public readonly ushort magic; + /// 0x03A, filesystem state + public readonly ushort state; + /// 0x03C, behaviour on errors + public readonly ushort err_behaviour; + /// 0x03E, From 0.5b onward + public readonly ushort minor_revision; + /// 0x040, last check time + public readonly uint check_t; + /// 0x044, max time between checks + public readonly uint check_inv; + + // From 0.5a onward + /// 0x048, Creation OS + public readonly uint creator_os; + /// 0x04C, Revison level + public readonly uint revision; + /// 0x050, Default UID for reserved blocks + public readonly ushort default_uid; + /// 0x052, Default GID for reserved blocks + public readonly ushort default_gid; + + // From 0.5b onward + /// 0x054, First unreserved inode + public readonly uint first_inode; + /// 0x058, inode size + public readonly ushort inode_size; + /// 0x05A, Block group number of THIS superblock + public readonly ushort block_group_no; + /// 0x05C, Compatible features set + public readonly uint ftr_compat; + /// 0x060, Incompatible features set + public readonly uint ftr_incompat; + + // Found on Linux 2.0.40 + /// 0x064, Read-only compatible features set + public readonly uint ftr_ro_compat; + + // Found on Linux 2.1.132 + /// 0x068, 16 bytes, UUID + public readonly Guid uuid; + /// 0x078, 16 bytes, volume name + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] volume_name; + /// 0x088, 64 bytes, where last mounted + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] last_mount_dir; + /// 0x0C8, Usage bitmap algorithm, for compression + public readonly uint algo_usage_bmp; + /// 0x0CC, Block to try to preallocate + public readonly byte prealloc_blks; + /// 0x0CD, Blocks to try to preallocate for directories + public readonly byte prealloc_dir_blks; + /// 0x0CE, Per-group desc for online growth + public readonly ushort rsrvd_gdt_blocks; + + // Found on Linux 2.4 + // ext3 + /// 0x0D0, 16 bytes, UUID of journal superblock + public readonly Guid journal_uuid; + /// 0x0E0, inode no. of journal file + public readonly uint journal_inode; + /// 0x0E4, device no. of journal file + public readonly uint journal_dev; + /// 0x0E8, Start of list of inodes to delete + public readonly uint last_orphan; + /// 0x0EC, First byte of 128bit HTREE hash seed + public readonly uint hash_seed_1; + /// 0x0F0, Second byte of 128bit HTREE hash seed + public readonly uint hash_seed_2; + /// 0x0F4, Third byte of 128bit HTREE hash seed + public readonly uint hash_seed_3; + /// 0x0F8, Fourth byte of 128bit HTREE hash seed + public readonly uint hash_seed_4; + /// 0x0FC, Hash version + public readonly byte hash_version; + /// 0x0FD, Journal backup type + public readonly byte jnl_backup_type; + /// 0x0FE, Size of group descriptor + public readonly ushort desc_grp_size; + /// 0x100, Default mount options + public readonly uint default_mnt_opts; + /// 0x104, First metablock block group + public readonly uint first_meta_bg; + + // Introduced with ext4, some can be ext3 + /// 0x108, Filesystem creation time + public readonly uint mkfs_t; + + /// Backup of the journal inode + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 17)] + public readonly uint[] jnl_blocks; + + // Following 3 fields are valid if EXT4_FEATURE_COMPAT_64BIT is set + /// 0x14C, High 32bits of blocks no. + public readonly uint blocks_hi; + /// 0x150, High 32bits of reserved blocks no. + public readonly uint reserved_blocks_hi; + /// 0x154, High 32bits of free blocks no. + public readonly uint free_blocks_hi; + /// 0x158, inodes minimal size in bytes + public readonly ushort min_inode_size; + /// 0x15A, Bytes reserved by new inodes + public readonly ushort rsv_inode_size; + /// 0x15C, Flags + public readonly uint flags; + /// 0x160, RAID stride + public readonly ushort raid_stride; + /// 0x162, Waiting seconds in MMP check + public readonly ushort mmp_interval; + /// 0x164, Block for multi-mount protection + public readonly ulong mmp_block; + /// 0x16C, Blocks on all data disks (N*stride) + public readonly uint raid_stripe_width; + /// 0x170, FLEX_BG group size + public readonly byte flex_bg_grp_size; + /// 0x171 Metadata checksum algorithm + public readonly byte checksum_type; + /// 0x172 Versioning level for encryption + public readonly byte encryption_level; + /// 0x173 Padding + public readonly ushort padding; + + // Following are introduced with ext4 + /// 0x174, Kibibytes written in volume lifetime + public readonly ulong kbytes_written; + /// 0x17C, Active snapshot inode number + public readonly uint snapshot_inum; + /// 0x180, Active snapshot sequential ID + public readonly uint snapshot_id; + /// 0x184, Reserved blocks for active snapshot's future use + public readonly ulong snapshot_blocks; + /// 0x18C, inode number of the on-disk start of the snapshot list + public readonly uint snapshot_list; + + // Optional ext4 error-handling features + /// 0x190, total registered filesystem errors + public readonly uint error_count; + /// 0x194, time on first error + public readonly uint first_error_t; + /// 0x198, inode involved in first error + public readonly uint first_error_inode; + /// 0x19C, block involved of first error + public readonly ulong first_error_block; + /// 0x1A0, 32 bytes, function where the error happened + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] first_error_func; + /// 0x1B0, line number where error happened + public readonly uint first_error_line; + /// 0x1B4, time of most recent error + public readonly uint last_error_t; + /// 0x1B8, inode involved in last error + public readonly uint last_error_inode; + /// 0x1BC, line number where error happened + public readonly uint last_error_line; + /// 0x1C0, block involved of last error + public readonly ulong last_error_block; + /// 0x1C8, 32 bytes, function where the error happened + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] last_error_func; + + // End of optional error-handling features + + // 0x1D8, 64 bytes, last used mount options + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] mount_options; + + /// Inode for user quota + public readonly uint usr_quota_inum; + /// Inode for group quota + public readonly uint grp_quota_inum; + /// Overhead clusters in volume + public readonly uint overhead_clusters; + /// Groups with sparse_super2 SBs + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)] + public readonly uint[] backup_bgs; + /// Encryption algorithms in use + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)] + public readonly byte[] encrypt_algos; + /// Salt used for string2key algorithm + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] encrypt_pw_salt; + /// Inode number of lost+found + public readonly uint lpf_inum; + /// Inode number for tracking project quota + public readonly uint prj_quota_inum; + /// crc32c(uuid) if csum_seed is set + public readonly uint checksum_seed; + /// Reserved + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 98)] + public readonly byte[] reserved; + /// crc32c(superblock) + public readonly uint checksum; } } \ No newline at end of file diff --git a/Aaru.Filesystems/extFS.cs b/Aaru.Filesystems/extFS.cs index 596fabfa9..dbc202a35 100644 --- a/Aaru.Filesystems/extFS.cs +++ b/Aaru.Filesystems/extFS.cs @@ -38,156 +38,155 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Schemas; -namespace Aaru.Filesystems +namespace Aaru.Filesystems; + +// Information from the Linux kernel +/// +/// Implements detection of the Linux extended filesystem +public sealed class extFS : IFilesystem { - // Information from the Linux kernel + const int SB_POS = 0x400; + + /// ext superblock magic + const ushort EXT_MAGIC = 0x137D; + /// - /// Implements detection of the Linux extended filesystem - public sealed class extFS : IFilesystem + public FileSystemType XmlFsType { get; private set; } + /// + public string Name => "Linux extended Filesystem"; + /// + public Guid Id => new("076CB3A2-08C2-4D69-BC8A-FCAA2E502BE2"); + /// + public Encoding Encoding { get; private set; } + /// + public string Author => "Natalia Portillo"; + + /// + public bool Identify(IMediaImage imagePlugin, Partition partition) { - const int SB_POS = 0x400; + if(imagePlugin.Info.SectorSize < 512) + return false; - /// ext superblock magic - const ushort EXT_MAGIC = 0x137D; + ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; + uint sbOff = SB_POS % imagePlugin.Info.SectorSize; - /// - public FileSystemType XmlFsType { get; private set; } - /// - public string Name => "Linux extended Filesystem"; - /// - public Guid Id => new("076CB3A2-08C2-4D69-BC8A-FCAA2E502BE2"); - /// - public Encoding Encoding { get; private set; } - /// - public string Author => "Natalia Portillo"; + if(sbSectorOff + partition.Start >= partition.End) + return false; - /// - public bool Identify(IMediaImage imagePlugin, Partition partition) + ErrorNumber errno = imagePlugin.ReadSector(sbSectorOff + partition.Start, out byte[] sbSector); + + if(errno != ErrorNumber.NoError) + return false; + + byte[] sb = new byte[512]; + + if(sbOff + 512 > sbSector.Length) + return false; + + Array.Copy(sbSector, sbOff, sb, 0, 512); + + ushort magic = BitConverter.ToUInt16(sb, 0x038); + + return magic == EXT_MAGIC; + } + + /// + public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, + Encoding encoding) + { + Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); + information = ""; + + var sb = new StringBuilder(); + + if(imagePlugin.Info.SectorSize < 512) + return; + + ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; + uint sbOff = SB_POS % imagePlugin.Info.SectorSize; + + if(sbSectorOff + partition.Start >= partition.End) + return; + + ErrorNumber errno = imagePlugin.ReadSector(sbSectorOff + partition.Start, out byte[] sblock); + + if(errno != ErrorNumber.NoError) + return; + + byte[] sbSector = new byte[512]; + Array.Copy(sblock, sbOff, sbSector, 0, 512); + + var extSb = new SuperBlock { - if(imagePlugin.Info.SectorSize < 512) - return false; + inodes = BitConverter.ToUInt32(sbSector, 0x000), + zones = BitConverter.ToUInt32(sbSector, 0x004), + firstfreeblk = BitConverter.ToUInt32(sbSector, 0x008), + freecountblk = BitConverter.ToUInt32(sbSector, 0x00C), + firstfreeind = BitConverter.ToUInt32(sbSector, 0x010), + freecountind = BitConverter.ToUInt32(sbSector, 0x014), + firstdatazone = BitConverter.ToUInt32(sbSector, 0x018), + logzonesize = BitConverter.ToUInt32(sbSector, 0x01C), + maxsize = BitConverter.ToUInt32(sbSector, 0x020) + }; - ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; - uint sbOff = SB_POS % imagePlugin.Info.SectorSize; + sb.AppendLine("ext filesystem"); + sb.AppendFormat("{0} zones on volume", extSb.zones); + sb.AppendFormat("{0} free blocks ({1} bytes)", extSb.freecountblk, extSb.freecountblk * 1024); - if(sbSectorOff + partition.Start >= partition.End) - return false; + sb.AppendFormat("{0} inodes on volume, {1} free ({2}%)", extSb.inodes, extSb.freecountind, + extSb.freecountind * 100 / extSb.inodes); - ErrorNumber errno = imagePlugin.ReadSector(sbSectorOff + partition.Start, out byte[] sbSector); + sb.AppendFormat("First free inode is {0}", extSb.firstfreeind); + sb.AppendFormat("First free block is {0}", extSb.firstfreeblk); + sb.AppendFormat("First data zone is {0}", extSb.firstdatazone); + sb.AppendFormat("Log zone size: {0}", extSb.logzonesize); + sb.AppendFormat("Max zone size: {0}", extSb.maxsize); - if(errno != ErrorNumber.NoError) - return false; - - byte[] sb = new byte[512]; - - if(sbOff + 512 > sbSector.Length) - return false; - - Array.Copy(sbSector, sbOff, sb, 0, 512); - - ushort magic = BitConverter.ToUInt16(sb, 0x038); - - return magic == EXT_MAGIC; - } - - /// - public void GetInformation(IMediaImage imagePlugin, Partition partition, out string information, - Encoding encoding) + XmlFsType = new FileSystemType { - Encoding = encoding ?? Encoding.GetEncoding("iso-8859-15"); - information = ""; + Type = "ext", + FreeClusters = extSb.freecountblk, + FreeClustersSpecified = true, + ClusterSize = 1024, + Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / 1024 + }; - var sb = new StringBuilder(); + information = sb.ToString(); + } - if(imagePlugin.Info.SectorSize < 512) - return; - - ulong sbSectorOff = SB_POS / imagePlugin.Info.SectorSize; - uint sbOff = SB_POS % imagePlugin.Info.SectorSize; - - if(sbSectorOff + partition.Start >= partition.End) - return; - - ErrorNumber errno = imagePlugin.ReadSector(sbSectorOff + partition.Start, out byte[] sblock); - - if(errno != ErrorNumber.NoError) - return; - - byte[] sbSector = new byte[512]; - Array.Copy(sblock, sbOff, sbSector, 0, 512); - - var extSb = new SuperBlock - { - inodes = BitConverter.ToUInt32(sbSector, 0x000), - zones = BitConverter.ToUInt32(sbSector, 0x004), - firstfreeblk = BitConverter.ToUInt32(sbSector, 0x008), - freecountblk = BitConverter.ToUInt32(sbSector, 0x00C), - firstfreeind = BitConverter.ToUInt32(sbSector, 0x010), - freecountind = BitConverter.ToUInt32(sbSector, 0x014), - firstdatazone = BitConverter.ToUInt32(sbSector, 0x018), - logzonesize = BitConverter.ToUInt32(sbSector, 0x01C), - maxsize = BitConverter.ToUInt32(sbSector, 0x020) - }; - - sb.AppendLine("ext filesystem"); - sb.AppendFormat("{0} zones on volume", extSb.zones); - sb.AppendFormat("{0} free blocks ({1} bytes)", extSb.freecountblk, extSb.freecountblk * 1024); - - sb.AppendFormat("{0} inodes on volume, {1} free ({2}%)", extSb.inodes, extSb.freecountind, - extSb.freecountind * 100 / extSb.inodes); - - sb.AppendFormat("First free inode is {0}", extSb.firstfreeind); - sb.AppendFormat("First free block is {0}", extSb.firstfreeblk); - sb.AppendFormat("First data zone is {0}", extSb.firstdatazone); - sb.AppendFormat("Log zone size: {0}", extSb.logzonesize); - sb.AppendFormat("Max zone size: {0}", extSb.maxsize); - - XmlFsType = new FileSystemType - { - Type = "ext", - FreeClusters = extSb.freecountblk, - FreeClustersSpecified = true, - ClusterSize = 1024, - Clusters = (partition.End - partition.Start + 1) * imagePlugin.Info.SectorSize / 1024 - }; - - information = sb.ToString(); - } - - /// ext superblock - [SuppressMessage("ReSharper", "InconsistentNaming")] - struct SuperBlock - { - /// 0x000, inodes on volume - public uint inodes; - /// 0x004, zones on volume - public uint zones; - /// 0x008, first free block - public uint firstfreeblk; - /// 0x00C, free blocks count - public uint freecountblk; - /// 0x010, first free inode - public uint firstfreeind; - /// 0x014, free inodes count - public uint freecountind; - /// 0x018, first data zone - public uint firstdatazone; - /// 0x01C, log zone size - public uint logzonesize; - /// 0x020, max zone size - public uint maxsize; - /// 0x024, reserved - public uint reserved1; - /// 0x028, reserved - public uint reserved2; - /// 0x02C, reserved - public uint reserved3; - /// 0x030, reserved - public uint reserved4; - /// 0x034, reserved - public uint reserved5; - /// 0x038, 0x137D (little endian) - public ushort magic; - } + /// ext superblock + [SuppressMessage("ReSharper", "InconsistentNaming")] + struct SuperBlock + { + /// 0x000, inodes on volume + public uint inodes; + /// 0x004, zones on volume + public uint zones; + /// 0x008, first free block + public uint firstfreeblk; + /// 0x00C, free blocks count + public uint freecountblk; + /// 0x010, first free inode + public uint firstfreeind; + /// 0x014, free inodes count + public uint freecountind; + /// 0x018, first data zone + public uint firstdatazone; + /// 0x01C, log zone size + public uint logzonesize; + /// 0x020, max zone size + public uint maxsize; + /// 0x024, reserved + public uint reserved1; + /// 0x028, reserved + public uint reserved2; + /// 0x02C, reserved + public uint reserved3; + /// 0x030, reserved + public uint reserved4; + /// 0x034, reserved + public uint reserved5; + /// 0x038, 0x137D (little endian) + public ushort magic; } } \ No newline at end of file diff --git a/Aaru.Filters/AppleDouble.cs b/Aaru.Filters/AppleDouble.cs index 3c8fc66b9..def6a1cf3 100644 --- a/Aaru.Filters/AppleDouble.cs +++ b/Aaru.Filters/AppleDouble.cs @@ -40,644 +40,643 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// Decodes AppleDouble files +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class AppleDouble : IFilter { - /// - /// Decodes AppleDouble files - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class AppleDouble : IFilter + const uint MAGIC = 0x00051607; + const uint VERSION = 0x00010000; + const uint VERSION2 = 0x00020000; + readonly byte[] _dosHome = { - const uint MAGIC = 0x00051607; - const uint VERSION = 0x00010000; - const uint VERSION2 = 0x00020000; - readonly byte[] _dosHome = + 0x4D, 0x53, 0x2D, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + + readonly byte[] _macintoshHome = + { + 0x4D, 0x61, 0x63, 0x69, 0x6E, 0x74, 0x6F, 0x73, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + readonly byte[] _osxHome = + { + 0x4D, 0x61, 0x63, 0x20, 0x4F, 0x53, 0x20, 0x58, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + readonly byte[] _proDosHome = + { + 0x50, 0x72, 0x6F, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + readonly byte[] _unixHome = + { + 0x55, 0x6E, 0x69, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + readonly byte[] _vmsHome = + { + 0x56, 0x41, 0x58, 0x20, 0x56, 0x4D, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + Entry _dataFork; + Header _header; + string _headerPath; + Entry _rsrcFork; + + /// + public string Name => "AppleDouble"; + /// + public Guid Id => new("1B2165EE-C9DF-4B21-BBBB-9E5892B2DF4D"); + /// + public string Author => "Natalia Portillo"; + + /// + public void Close() {} + + /// + public string BasePath { get; private set; } + + /// + public DateTime CreationTime { get; private set; } + + /// + public long DataForkLength => _dataFork.length; + + /// + public Stream GetDataForkStream() => new FileStream(BasePath, FileMode.Open, FileAccess.Read); + + /// + public string Filename => System.IO.Path.GetFileName(BasePath); + + /// + public DateTime LastWriteTime { get; private set; } + + /// + public long Length => _dataFork.length + _rsrcFork.length; + + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + + /// + public string Path => BasePath; + + /// + public long ResourceForkLength => _rsrcFork.length; + + /// + public Stream GetResourceForkStream() + { + if(_rsrcFork.length == 0) + return null; + + return new OffsetStream(_headerPath, FileMode.Open, FileAccess.Read, _rsrcFork.offset, + _rsrcFork.offset + _rsrcFork.length - 1); + } + + /// + public bool HasResourceFork => _rsrcFork.length > 0; + + /// + public bool Identify(byte[] buffer) => false; + + /// + public bool Identify(Stream stream) => false; + + /// + public bool Identify(string path) + { + string filename = System.IO.Path.GetFileName(path); + string filenameNoExt = System.IO.Path.GetFileNameWithoutExtension(path); + string parentFolder = System.IO.Path.GetDirectoryName(path); + + parentFolder ??= ""; + + if(filename is null || + filenameNoExt is null) + return false; + + // Prepend data fork name with "R." + string proDosAppleDouble = System.IO.Path.Combine(parentFolder, "R." + filename); + + // Prepend data fork name with '%' + string unixAppleDouble = System.IO.Path.Combine(parentFolder, "%" + filename); + + // Change file extension to ADF + string dosAppleDouble = System.IO.Path.Combine(parentFolder, filenameNoExt + ".ADF"); + + // Change file extension to adf + string dosAppleDoubleLower = System.IO.Path.Combine(parentFolder, filenameNoExt + ".adf"); + + // Store AppleDouble header file in ".AppleDouble" folder with same name + string netatalkAppleDouble = System.IO.Path.Combine(parentFolder, ".AppleDouble", filename); + + // Store AppleDouble header file in "resource.frk" folder with same name + string daveAppleDouble = System.IO.Path.Combine(parentFolder, "resource.frk", filename); + + // Prepend data fork name with "._" + string osxAppleDouble = System.IO.Path.Combine(parentFolder, "._" + filename); + + // Adds ".rsrc" extension + string unArAppleDouble = System.IO.Path.Combine(parentFolder, filename + ".rsrc"); + + // Check AppleDouble created by A/UX in ProDOS filesystem + if(File.Exists(proDosAppleDouble)) { - 0x4D, 0x53, 0x2D, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; + var prodosStream = new FileStream(proDosAppleDouble, FileMode.Open, FileAccess.Read); - readonly byte[] _macintoshHome = - { - 0x4D, 0x61, 0x63, 0x69, 0x6E, 0x74, 0x6F, 0x73, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - readonly byte[] _osxHome = - { - 0x4D, 0x61, 0x63, 0x20, 0x4F, 0x53, 0x20, 0x58, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - readonly byte[] _proDosHome = - { - 0x50, 0x72, 0x6F, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - readonly byte[] _unixHome = - { - 0x55, 0x6E, 0x69, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - readonly byte[] _vmsHome = - { - 0x56, 0x41, 0x58, 0x20, 0x56, 0x4D, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - Entry _dataFork; - Header _header; - string _headerPath; - Entry _rsrcFork; + if(prodosStream.Length > 26) + { + byte[] prodosB = new byte[26]; + prodosStream.Read(prodosB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(prodosB); + prodosStream.Close(); - /// - public string Name => "AppleDouble"; - /// - public Guid Id => new("1B2165EE-C9DF-4B21-BBBB-9E5892B2DF4D"); - /// - public string Author => "Natalia Portillo"; - - /// - public void Close() {} - - /// - public string BasePath { get; private set; } - - /// - public DateTime CreationTime { get; private set; } - - /// - public long DataForkLength => _dataFork.length; - - /// - public Stream GetDataForkStream() => new FileStream(BasePath, FileMode.Open, FileAccess.Read); - - /// - public string Filename => System.IO.Path.GetFileName(BasePath); - - /// - public DateTime LastWriteTime { get; private set; } - - /// - public long Length => _dataFork.length + _rsrcFork.length; - - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); - - /// - public string Path => BasePath; - - /// - public long ResourceForkLength => _rsrcFork.length; - - /// - public Stream GetResourceForkStream() - { - if(_rsrcFork.length == 0) - return null; - - return new OffsetStream(_headerPath, FileMode.Open, FileAccess.Read, _rsrcFork.offset, - _rsrcFork.offset + _rsrcFork.length - 1); + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + return true; + } } - /// - public bool HasResourceFork => _rsrcFork.length > 0; - - /// - public bool Identify(byte[] buffer) => false; - - /// - public bool Identify(Stream stream) => false; - - /// - public bool Identify(string path) + // Check AppleDouble created by A/UX in UFS filesystem + if(File.Exists(unixAppleDouble)) { - string filename = System.IO.Path.GetFileName(path); - string filenameNoExt = System.IO.Path.GetFileNameWithoutExtension(path); - string parentFolder = System.IO.Path.GetDirectoryName(path); + var unixStream = new FileStream(unixAppleDouble, FileMode.Open, FileAccess.Read); - parentFolder ??= ""; - - if(filename is null || - filenameNoExt is null) - return false; - - // Prepend data fork name with "R." - string proDosAppleDouble = System.IO.Path.Combine(parentFolder, "R." + filename); - - // Prepend data fork name with '%' - string unixAppleDouble = System.IO.Path.Combine(parentFolder, "%" + filename); - - // Change file extension to ADF - string dosAppleDouble = System.IO.Path.Combine(parentFolder, filenameNoExt + ".ADF"); - - // Change file extension to adf - string dosAppleDoubleLower = System.IO.Path.Combine(parentFolder, filenameNoExt + ".adf"); - - // Store AppleDouble header file in ".AppleDouble" folder with same name - string netatalkAppleDouble = System.IO.Path.Combine(parentFolder, ".AppleDouble", filename); - - // Store AppleDouble header file in "resource.frk" folder with same name - string daveAppleDouble = System.IO.Path.Combine(parentFolder, "resource.frk", filename); - - // Prepend data fork name with "._" - string osxAppleDouble = System.IO.Path.Combine(parentFolder, "._" + filename); - - // Adds ".rsrc" extension - string unArAppleDouble = System.IO.Path.Combine(parentFolder, filename + ".rsrc"); - - // Check AppleDouble created by A/UX in ProDOS filesystem - if(File.Exists(proDosAppleDouble)) + if(unixStream.Length > 26) { - var prodosStream = new FileStream(proDosAppleDouble, FileMode.Open, FileAccess.Read); + byte[] unixB = new byte[26]; + unixStream.Read(unixB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(unixB); + unixStream.Close(); - if(prodosStream.Length > 26) - { - byte[] prodosB = new byte[26]; - prodosStream.Read(prodosB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(prodosB); - prodosStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - return true; - } + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + return true; } + } - // Check AppleDouble created by A/UX in UFS filesystem - if(File.Exists(unixAppleDouble)) + // Check AppleDouble created by A/UX in FAT filesystem + if(File.Exists(dosAppleDouble)) + { + var dosStream = new FileStream(dosAppleDouble, FileMode.Open, FileAccess.Read); + + if(dosStream.Length > 26) { - var unixStream = new FileStream(unixAppleDouble, FileMode.Open, FileAccess.Read); + byte[] dosB = new byte[26]; + dosStream.Read(dosB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(dosB); + dosStream.Close(); - if(unixStream.Length > 26) - { - byte[] unixB = new byte[26]; - unixStream.Read(unixB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(unixB); - unixStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - return true; - } + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + return true; } + } - // Check AppleDouble created by A/UX in FAT filesystem - if(File.Exists(dosAppleDouble)) + // Check AppleDouble created by A/UX in case preserving FAT filesystem + if(File.Exists(dosAppleDoubleLower)) + { + var doslStream = new FileStream(dosAppleDoubleLower, FileMode.Open, FileAccess.Read); + + if(doslStream.Length > 26) { - var dosStream = new FileStream(dosAppleDouble, FileMode.Open, FileAccess.Read); + byte[] doslB = new byte[26]; + doslStream.Read(doslB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(doslB); + doslStream.Close(); - if(dosStream.Length > 26) - { - byte[] dosB = new byte[26]; - dosStream.Read(dosB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(dosB); - dosStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - return true; - } + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + return true; } + } - // Check AppleDouble created by A/UX in case preserving FAT filesystem - if(File.Exists(dosAppleDoubleLower)) + // Check AppleDouble created by Netatalk + if(File.Exists(netatalkAppleDouble)) + { + var netatalkStream = new FileStream(netatalkAppleDouble, FileMode.Open, FileAccess.Read); + + if(netatalkStream.Length > 26) { - var doslStream = new FileStream(dosAppleDoubleLower, FileMode.Open, FileAccess.Read); + byte[] netatalkB = new byte[26]; + netatalkStream.Read(netatalkB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(netatalkB); + netatalkStream.Close(); - if(doslStream.Length > 26) - { - byte[] doslB = new byte[26]; - doslStream.Read(doslB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(doslB); - doslStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - return true; - } + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + return true; } + } - // Check AppleDouble created by Netatalk - if(File.Exists(netatalkAppleDouble)) + // Check AppleDouble created by DAVE + if(File.Exists(daveAppleDouble)) + { + var daveStream = new FileStream(daveAppleDouble, FileMode.Open, FileAccess.Read); + + if(daveStream.Length > 26) { - var netatalkStream = new FileStream(netatalkAppleDouble, FileMode.Open, FileAccess.Read); + byte[] daveB = new byte[26]; + daveStream.Read(daveB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(daveB); + daveStream.Close(); - if(netatalkStream.Length > 26) - { - byte[] netatalkB = new byte[26]; - netatalkStream.Read(netatalkB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(netatalkB); - netatalkStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - return true; - } + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + return true; } + } - // Check AppleDouble created by DAVE - if(File.Exists(daveAppleDouble)) + // Check AppleDouble created by Mac OS X + if(File.Exists(osxAppleDouble)) + { + var osxStream = new FileStream(osxAppleDouble, FileMode.Open, FileAccess.Read); + + if(osxStream.Length > 26) { - var daveStream = new FileStream(daveAppleDouble, FileMode.Open, FileAccess.Read); + byte[] osxB = new byte[26]; + osxStream.Read(osxB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(osxB); + osxStream.Close(); - if(daveStream.Length > 26) - { - byte[] daveB = new byte[26]; - daveStream.Read(daveB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(daveB); - daveStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - return true; - } + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + return true; } + } - // Check AppleDouble created by Mac OS X - if(File.Exists(osxAppleDouble)) + // Check AppleDouble created by UnAr (from The Unarchiver) + if(!File.Exists(unArAppleDouble)) + return false; + + var unarStream = new FileStream(unArAppleDouble, FileMode.Open, FileAccess.Read); + + if(unarStream.Length <= 26) + return false; + + byte[] unarB = new byte[26]; + unarStream.Read(unarB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(unarB); + unarStream.Close(); + + return _header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2); + } + + // Now way to have two files in a single byte array + /// + public ErrorNumber Open(byte[] buffer) => ErrorNumber.NotSupported; + + // Now way to have two files in a single stream + /// + public ErrorNumber Open(Stream stream) => ErrorNumber.NotSupported; + + /// + public ErrorNumber Open(string path) + { + string filename = System.IO.Path.GetFileName(path); + string filenameNoExt = System.IO.Path.GetFileNameWithoutExtension(path); + string parentFolder = System.IO.Path.GetDirectoryName(path); + + parentFolder ??= ""; + + if(filename is null || + filenameNoExt is null) + return ErrorNumber.InvalidArgument; + + // Prepend data fork name with "R." + string proDosAppleDouble = System.IO.Path.Combine(parentFolder, "R." + filename); + + // Prepend data fork name with '%' + string unixAppleDouble = System.IO.Path.Combine(parentFolder, "%" + filename); + + // Change file extension to ADF + string dosAppleDouble = System.IO.Path.Combine(parentFolder, filenameNoExt + ".ADF"); + + // Change file extension to adf + string dosAppleDoubleLower = System.IO.Path.Combine(parentFolder, filenameNoExt + ".adf"); + + // Store AppleDouble header file in ".AppleDouble" folder with same name + string netatalkAppleDouble = System.IO.Path.Combine(parentFolder, ".AppleDouble", filename); + + // Store AppleDouble header file in "resource.frk" folder with same name + string daveAppleDouble = System.IO.Path.Combine(parentFolder, "resource.frk", filename); + + // Prepend data fork name with "._" + string osxAppleDouble = System.IO.Path.Combine(parentFolder, "._" + filename); + + // Adds ".rsrc" extension + string unArAppleDouble = System.IO.Path.Combine(parentFolder, filename + ".rsrc"); + + // Check AppleDouble created by A/UX in ProDOS filesystem + if(File.Exists(proDosAppleDouble)) + { + var prodosStream = new FileStream(proDosAppleDouble, FileMode.Open, FileAccess.Read); + + if(prodosStream.Length > 26) { - var osxStream = new FileStream(osxAppleDouble, FileMode.Open, FileAccess.Read); + byte[] prodosB = new byte[26]; + prodosStream.Read(prodosB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(prodosB); + prodosStream.Close(); - if(osxStream.Length > 26) - { - byte[] osxB = new byte[26]; - osxStream.Read(osxB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(osxB); - osxStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - return true; - } + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + _headerPath = proDosAppleDouble; } + } - // Check AppleDouble created by UnAr (from The Unarchiver) - if(!File.Exists(unArAppleDouble)) - return false; + // Check AppleDouble created by A/UX in UFS filesystem + if(File.Exists(unixAppleDouble)) + { + var unixStream = new FileStream(unixAppleDouble, FileMode.Open, FileAccess.Read); + if(unixStream.Length > 26) + { + byte[] unixB = new byte[26]; + unixStream.Read(unixB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(unixB); + unixStream.Close(); + + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + _headerPath = unixAppleDouble; + } + } + + // Check AppleDouble created by A/UX in FAT filesystem + if(File.Exists(dosAppleDouble)) + { + var dosStream = new FileStream(dosAppleDouble, FileMode.Open, FileAccess.Read); + + if(dosStream.Length > 26) + { + byte[] dosB = new byte[26]; + dosStream.Read(dosB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(dosB); + dosStream.Close(); + + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + _headerPath = dosAppleDouble; + } + } + + // Check AppleDouble created by A/UX in case preserving FAT filesystem + if(File.Exists(dosAppleDoubleLower)) + { + var doslStream = new FileStream(dosAppleDoubleLower, FileMode.Open, FileAccess.Read); + + if(doslStream.Length > 26) + { + byte[] doslB = new byte[26]; + doslStream.Read(doslB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(doslB); + doslStream.Close(); + + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + _headerPath = dosAppleDoubleLower; + } + } + + // Check AppleDouble created by Netatalk + if(File.Exists(netatalkAppleDouble)) + { + var netatalkStream = new FileStream(netatalkAppleDouble, FileMode.Open, FileAccess.Read); + + if(netatalkStream.Length > 26) + { + byte[] netatalkB = new byte[26]; + netatalkStream.Read(netatalkB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(netatalkB); + netatalkStream.Close(); + + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + _headerPath = netatalkAppleDouble; + } + } + + // Check AppleDouble created by DAVE + if(File.Exists(daveAppleDouble)) + { + var daveStream = new FileStream(daveAppleDouble, FileMode.Open, FileAccess.Read); + + if(daveStream.Length > 26) + { + byte[] daveB = new byte[26]; + daveStream.Read(daveB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(daveB); + daveStream.Close(); + + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + _headerPath = daveAppleDouble; + } + } + + // Check AppleDouble created by Mac OS X + if(File.Exists(osxAppleDouble)) + { + var osxStream = new FileStream(osxAppleDouble, FileMode.Open, FileAccess.Read); + + if(osxStream.Length > 26) + { + byte[] osxB = new byte[26]; + osxStream.Read(osxB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(osxB); + osxStream.Close(); + + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + _headerPath = osxAppleDouble; + } + } + + // Check AppleDouble created by UnAr (from The Unarchiver) + if(File.Exists(unArAppleDouble)) + { var unarStream = new FileStream(unArAppleDouble, FileMode.Open, FileAccess.Read); - if(unarStream.Length <= 26) - return false; + if(unarStream.Length > 26) + { + byte[] unarB = new byte[26]; + unarStream.Read(unarB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(unarB); + unarStream.Close(); - byte[] unarB = new byte[26]; - unarStream.Read(unarB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(unarB); - unarStream.Close(); - - return _header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2); + if(_header.magic == MAGIC && + (_header.version == VERSION || _header.version == VERSION2)) + _headerPath = unArAppleDouble; + } } - // Now way to have two files in a single byte array - /// - public ErrorNumber Open(byte[] buffer) => ErrorNumber.NotSupported; + // TODO: More appropriate error + if(_headerPath is null) + return ErrorNumber.NotSupported; - // Now way to have two files in a single stream - /// - public ErrorNumber Open(Stream stream) => ErrorNumber.NotSupported; + var fs = new FileStream(_headerPath, FileMode.Open, FileAccess.Read); + fs.Seek(0, SeekOrigin.Begin); - /// - public ErrorNumber Open(string path) + byte[] hdrB = new byte[26]; + fs.Read(hdrB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + + Entry[] entries = new Entry[_header.entries]; + + for(int i = 0; i < _header.entries; i++) { - string filename = System.IO.Path.GetFileName(path); - string filenameNoExt = System.IO.Path.GetFileNameWithoutExtension(path); - string parentFolder = System.IO.Path.GetDirectoryName(path); - - parentFolder ??= ""; - - if(filename is null || - filenameNoExt is null) - return ErrorNumber.InvalidArgument; - - // Prepend data fork name with "R." - string proDosAppleDouble = System.IO.Path.Combine(parentFolder, "R." + filename); - - // Prepend data fork name with '%' - string unixAppleDouble = System.IO.Path.Combine(parentFolder, "%" + filename); - - // Change file extension to ADF - string dosAppleDouble = System.IO.Path.Combine(parentFolder, filenameNoExt + ".ADF"); - - // Change file extension to adf - string dosAppleDoubleLower = System.IO.Path.Combine(parentFolder, filenameNoExt + ".adf"); - - // Store AppleDouble header file in ".AppleDouble" folder with same name - string netatalkAppleDouble = System.IO.Path.Combine(parentFolder, ".AppleDouble", filename); - - // Store AppleDouble header file in "resource.frk" folder with same name - string daveAppleDouble = System.IO.Path.Combine(parentFolder, "resource.frk", filename); - - // Prepend data fork name with "._" - string osxAppleDouble = System.IO.Path.Combine(parentFolder, "._" + filename); - - // Adds ".rsrc" extension - string unArAppleDouble = System.IO.Path.Combine(parentFolder, filename + ".rsrc"); - - // Check AppleDouble created by A/UX in ProDOS filesystem - if(File.Exists(proDosAppleDouble)) - { - var prodosStream = new FileStream(proDosAppleDouble, FileMode.Open, FileAccess.Read); - - if(prodosStream.Length > 26) - { - byte[] prodosB = new byte[26]; - prodosStream.Read(prodosB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(prodosB); - prodosStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - _headerPath = proDosAppleDouble; - } - } - - // Check AppleDouble created by A/UX in UFS filesystem - if(File.Exists(unixAppleDouble)) - { - var unixStream = new FileStream(unixAppleDouble, FileMode.Open, FileAccess.Read); - - if(unixStream.Length > 26) - { - byte[] unixB = new byte[26]; - unixStream.Read(unixB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(unixB); - unixStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - _headerPath = unixAppleDouble; - } - } - - // Check AppleDouble created by A/UX in FAT filesystem - if(File.Exists(dosAppleDouble)) - { - var dosStream = new FileStream(dosAppleDouble, FileMode.Open, FileAccess.Read); - - if(dosStream.Length > 26) - { - byte[] dosB = new byte[26]; - dosStream.Read(dosB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(dosB); - dosStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - _headerPath = dosAppleDouble; - } - } - - // Check AppleDouble created by A/UX in case preserving FAT filesystem - if(File.Exists(dosAppleDoubleLower)) - { - var doslStream = new FileStream(dosAppleDoubleLower, FileMode.Open, FileAccess.Read); - - if(doslStream.Length > 26) - { - byte[] doslB = new byte[26]; - doslStream.Read(doslB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(doslB); - doslStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - _headerPath = dosAppleDoubleLower; - } - } - - // Check AppleDouble created by Netatalk - if(File.Exists(netatalkAppleDouble)) - { - var netatalkStream = new FileStream(netatalkAppleDouble, FileMode.Open, FileAccess.Read); - - if(netatalkStream.Length > 26) - { - byte[] netatalkB = new byte[26]; - netatalkStream.Read(netatalkB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(netatalkB); - netatalkStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - _headerPath = netatalkAppleDouble; - } - } - - // Check AppleDouble created by DAVE - if(File.Exists(daveAppleDouble)) - { - var daveStream = new FileStream(daveAppleDouble, FileMode.Open, FileAccess.Read); - - if(daveStream.Length > 26) - { - byte[] daveB = new byte[26]; - daveStream.Read(daveB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(daveB); - daveStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - _headerPath = daveAppleDouble; - } - } - - // Check AppleDouble created by Mac OS X - if(File.Exists(osxAppleDouble)) - { - var osxStream = new FileStream(osxAppleDouble, FileMode.Open, FileAccess.Read); - - if(osxStream.Length > 26) - { - byte[] osxB = new byte[26]; - osxStream.Read(osxB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(osxB); - osxStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - _headerPath = osxAppleDouble; - } - } - - // Check AppleDouble created by UnAr (from The Unarchiver) - if(File.Exists(unArAppleDouble)) - { - var unarStream = new FileStream(unArAppleDouble, FileMode.Open, FileAccess.Read); - - if(unarStream.Length > 26) - { - byte[] unarB = new byte[26]; - unarStream.Read(unarB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(unarB); - unarStream.Close(); - - if(_header.magic == MAGIC && - (_header.version == VERSION || _header.version == VERSION2)) - _headerPath = unArAppleDouble; - } - } - - // TODO: More appropriate error - if(_headerPath is null) - return ErrorNumber.NotSupported; - - var fs = new FileStream(_headerPath, FileMode.Open, FileAccess.Read); - fs.Seek(0, SeekOrigin.Begin); - - byte[] hdrB = new byte[26]; - fs.Read(hdrB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - - Entry[] entries = new Entry[_header.entries]; - - for(int i = 0; i < _header.entries; i++) - { - byte[] entry = new byte[12]; - fs.Read(entry, 0, 12); - entries[i] = Marshal.ByteArrayToStructureBigEndian(entry); - } - - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - - foreach(Entry entry in entries) - switch((EntryId)entry.id) - { - case EntryId.DataFork: - // AppleDouble have datafork in separated file - break; - case EntryId.FileDates: - fs.Seek(entry.offset, SeekOrigin.Begin); - byte[] datesB = new byte[16]; - fs.Read(datesB, 0, 16); - - FileDates dates = Marshal.ByteArrayToStructureBigEndian(datesB); - - CreationTime = DateHandlers.UnixUnsignedToDateTime(dates.creationDate); - LastWriteTime = DateHandlers.UnixUnsignedToDateTime(dates.modificationDate); - - break; - case EntryId.FileInfo: - fs.Seek(entry.offset, SeekOrigin.Begin); - byte[] finfo = new byte[entry.length]; - fs.Read(finfo, 0, finfo.Length); - - if(_macintoshHome.SequenceEqual(_header.homeFilesystem)) - { - MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.MacToDateTime(macinfo.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); - } - else if(_proDosHome.SequenceEqual(_header.homeFilesystem)) - { - ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); - } - else if(_unixHome.SequenceEqual(_header.homeFilesystem)) - { - UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); - LastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); - } - else if(_dosHome.SequenceEqual(_header.homeFilesystem)) - { - DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - LastWriteTime = - DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); - } - - break; - case EntryId.ResourceFork: - _rsrcFork = entry; - - break; - } - - _dataFork = new Entry - { - id = (uint)EntryId.DataFork - }; - - if(File.Exists(path)) - { - var dataFs = new FileStream(path, FileMode.Open, FileAccess.Read); - _dataFork.length = (uint)dataFs.Length; - dataFs.Close(); - } - - fs.Close(); - BasePath = path; - - return ErrorNumber.NoError; + byte[] entry = new byte[12]; + fs.Read(entry, 0, 12); + entries[i] = Marshal.ByteArrayToStructureBigEndian(entry); } - enum EntryId : uint + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + + foreach(Entry entry in entries) + switch((EntryId)entry.id) + { + case EntryId.DataFork: + // AppleDouble have datafork in separated file + break; + case EntryId.FileDates: + fs.Seek(entry.offset, SeekOrigin.Begin); + byte[] datesB = new byte[16]; + fs.Read(datesB, 0, 16); + + FileDates dates = Marshal.ByteArrayToStructureBigEndian(datesB); + + CreationTime = DateHandlers.UnixUnsignedToDateTime(dates.creationDate); + LastWriteTime = DateHandlers.UnixUnsignedToDateTime(dates.modificationDate); + + break; + case EntryId.FileInfo: + fs.Seek(entry.offset, SeekOrigin.Begin); + byte[] finfo = new byte[entry.length]; + fs.Read(finfo, 0, finfo.Length); + + if(_macintoshHome.SequenceEqual(_header.homeFilesystem)) + { + MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.MacToDateTime(macinfo.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); + } + else if(_proDosHome.SequenceEqual(_header.homeFilesystem)) + { + ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); + } + else if(_unixHome.SequenceEqual(_header.homeFilesystem)) + { + UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); + LastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); + } + else if(_dosHome.SequenceEqual(_header.homeFilesystem)) + { + DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + LastWriteTime = + DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); + } + + break; + case EntryId.ResourceFork: + _rsrcFork = entry; + + break; + } + + _dataFork = new Entry { - Invalid = 0, DataFork = 1, ResourceFork = 2, - RealName = 3, Comment = 4, Icon = 5, - ColorIcon = 6, FileInfo = 7, FileDates = 8, - FinderInfo = 9, MacFileInfo = 10, ProDOSFileInfo = 11, - DOSFileInfo = 12, ShortName = 13, AfpFileInfo = 14, - DirectoryID = 15 + id = (uint)EntryId.DataFork + }; + + if(File.Exists(path)) + { + var dataFs = new FileStream(path, FileMode.Open, FileAccess.Read); + _dataFork.length = (uint)dataFs.Length; + dataFs.Close(); } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Header - { - public readonly uint magic; - public readonly uint version; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] homeFilesystem; - public readonly ushort entries; - } + fs.Close(); + BasePath = path; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct Entry - { - public uint id; - public readonly uint offset; - public uint length; - } + return ErrorNumber.NoError; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct FileDates - { - public readonly uint creationDate; - public readonly uint modificationDate; - public readonly uint backupDate; - public readonly uint accessDate; - } + enum EntryId : uint + { + Invalid = 0, DataFork = 1, ResourceFork = 2, + RealName = 3, Comment = 4, Icon = 5, + ColorIcon = 6, FileInfo = 7, FileDates = 8, + FinderInfo = 9, MacFileInfo = 10, ProDOSFileInfo = 11, + DOSFileInfo = 12, ShortName = 13, AfpFileInfo = 14, + DirectoryID = 15 + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct MacFileInfo - { - public readonly uint creationDate; - public readonly uint modificationDate; - public readonly uint backupDate; - public readonly uint accessDate; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Header + { + public readonly uint magic; + public readonly uint version; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] homeFilesystem; + public readonly ushort entries; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct UnixFileInfo - { - public readonly uint creationDate; - public readonly uint accessDate; - public readonly uint modificationDate; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct Entry + { + public uint id; + public readonly uint offset; + public uint length; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DOSFileInfo - { - public readonly ushort modificationDate; - public readonly ushort modificationTime; - public readonly ushort attributes; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct FileDates + { + public readonly uint creationDate; + public readonly uint modificationDate; + public readonly uint backupDate; + public readonly uint accessDate; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ProDOSFileInfo - { - public readonly uint creationDate; - public readonly uint modificationDate; - public readonly uint backupDate; - public readonly ushort access; - public readonly ushort fileType; - public readonly uint auxType; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct MacFileInfo + { + public readonly uint creationDate; + public readonly uint modificationDate; + public readonly uint backupDate; + public readonly uint accessDate; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct UnixFileInfo + { + public readonly uint creationDate; + public readonly uint accessDate; + public readonly uint modificationDate; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DOSFileInfo + { + public readonly ushort modificationDate; + public readonly ushort modificationTime; + public readonly ushort attributes; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ProDOSFileInfo + { + public readonly uint creationDate; + public readonly uint modificationDate; + public readonly uint backupDate; + public readonly ushort access; + public readonly ushort fileType; + public readonly uint auxType; } } \ No newline at end of file diff --git a/Aaru.Filters/AppleSingle.cs b/Aaru.Filters/AppleSingle.cs index e92da6338..b9e8a1a57 100644 --- a/Aaru.Filters/AppleSingle.cs +++ b/Aaru.Filters/AppleSingle.cs @@ -40,516 +40,515 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// Decodes AppleSingle files +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class AppleSingle : IFilter { - /// - /// Decodes AppleSingle files - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class AppleSingle : IFilter + const uint MAGIC = 0x00051600; + const uint VERSION = 0x00010000; + const uint VERSION2 = 0x00020000; + readonly byte[] _dosHome = { - const uint MAGIC = 0x00051600; - const uint VERSION = 0x00010000; - const uint VERSION2 = 0x00020000; - readonly byte[] _dosHome = - { - 0x4D, 0x53, 0x2D, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; + 0x4D, 0x53, 0x2D, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; - readonly byte[] _macintoshHome = - { - 0x4D, 0x61, 0x63, 0x69, 0x6E, 0x74, 0x6F, 0x73, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - readonly byte[] _osxHome = - { - 0x4D, 0x61, 0x63, 0x20, 0x4F, 0x53, 0x20, 0x58, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - readonly byte[] _proDosHome = - { - 0x50, 0x72, 0x6F, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - readonly byte[] _unixHome = - { - 0x55, 0x6E, 0x69, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - readonly byte[] _vmsHome = - { - 0x56, 0x41, 0x58, 0x20, 0x56, 0x4D, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 - }; - byte[] _bytes; - Entry _dataFork; - Header _header; - bool _isBytes, _isStream, _isPath; - Entry _rsrcFork; - Stream _stream; + readonly byte[] _macintoshHome = + { + 0x4D, 0x61, 0x63, 0x69, 0x6E, 0x74, 0x6F, 0x73, 0x68, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + readonly byte[] _osxHome = + { + 0x4D, 0x61, 0x63, 0x20, 0x4F, 0x53, 0x20, 0x58, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + readonly byte[] _proDosHome = + { + 0x50, 0x72, 0x6F, 0x44, 0x4F, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + readonly byte[] _unixHome = + { + 0x55, 0x6E, 0x69, 0x78, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + readonly byte[] _vmsHome = + { + 0x56, 0x41, 0x58, 0x20, 0x56, 0x4D, 0x53, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + byte[] _bytes; + Entry _dataFork; + Header _header; + bool _isBytes, _isStream, _isPath; + Entry _rsrcFork; + Stream _stream; - /// - public string Name => "AppleSingle"; - /// - public Guid Id => new("A69B20E8-F4D3-42BB-BD2B-4A7263394A05"); - /// - public string Author => "Natalia Portillo"; + /// + public string Name => "AppleSingle"; + /// + public Guid Id => new("A69B20E8-F4D3-42BB-BD2B-4A7263394A05"); + /// + public string Author => "Natalia Portillo"; - /// - public void Close() - { - _bytes = null; - _stream?.Close(); - _isBytes = false; - _isStream = false; - _isPath = false; - } + /// + public void Close() + { + _bytes = null; + _stream?.Close(); + _isBytes = false; + _isStream = false; + _isPath = false; + } - /// - public string BasePath { get; private set; } + /// + public string BasePath { get; private set; } - /// - public DateTime CreationTime { get; private set; } + /// + public DateTime CreationTime { get; private set; } - /// - public long DataForkLength => _dataFork.length; - - /// - public Stream GetDataForkStream() - { - if(_dataFork.length == 0) - return null; - - if(_isBytes) - return new OffsetStream(_bytes, _dataFork.offset, _dataFork.offset + _dataFork.length - 1); - - if(_isStream) - return new OffsetStream(_stream, _dataFork.offset, _dataFork.offset + _dataFork.length - 1); - - if(_isPath) - return new OffsetStream(BasePath, FileMode.Open, FileAccess.Read, _dataFork.offset, - _dataFork.offset + _dataFork.length - 1); + /// + public long DataForkLength => _dataFork.length; + /// + public Stream GetDataForkStream() + { + if(_dataFork.length == 0) return null; - } - /// - public string Filename => System.IO.Path.GetFileName(BasePath); + if(_isBytes) + return new OffsetStream(_bytes, _dataFork.offset, _dataFork.offset + _dataFork.length - 1); - /// - public DateTime LastWriteTime { get; private set; } + if(_isStream) + return new OffsetStream(_stream, _dataFork.offset, _dataFork.offset + _dataFork.length - 1); - /// - public long Length => _dataFork.length + _rsrcFork.length; + if(_isPath) + return new OffsetStream(BasePath, FileMode.Open, FileAccess.Read, _dataFork.offset, + _dataFork.offset + _dataFork.length - 1); - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + return null; + } - /// - public string Path => BasePath; + /// + public string Filename => System.IO.Path.GetFileName(BasePath); - /// - public long ResourceForkLength => _rsrcFork.length; + /// + public DateTime LastWriteTime { get; private set; } - /// - public Stream GetResourceForkStream() - { - if(_rsrcFork.length == 0) - return null; + /// + public long Length => _dataFork.length + _rsrcFork.length; - if(_isBytes) - return new OffsetStream(_bytes, _rsrcFork.offset, _rsrcFork.offset + _rsrcFork.length - 1); + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); - if(_isStream) - return new OffsetStream(_stream, _rsrcFork.offset, _rsrcFork.offset + _rsrcFork.length - 1); + /// + public string Path => BasePath; - if(_isPath) - return new OffsetStream(BasePath, FileMode.Open, FileAccess.Read, _rsrcFork.offset, - _rsrcFork.offset + _rsrcFork.length - 1); + /// + public long ResourceForkLength => _rsrcFork.length; + /// + public Stream GetResourceForkStream() + { + if(_rsrcFork.length == 0) return null; + + if(_isBytes) + return new OffsetStream(_bytes, _rsrcFork.offset, _rsrcFork.offset + _rsrcFork.length - 1); + + if(_isStream) + return new OffsetStream(_stream, _rsrcFork.offset, _rsrcFork.offset + _rsrcFork.length - 1); + + if(_isPath) + return new OffsetStream(BasePath, FileMode.Open, FileAccess.Read, _rsrcFork.offset, + _rsrcFork.offset + _rsrcFork.length - 1); + + return null; + } + + /// + public bool HasResourceFork => _rsrcFork.length > 0; + + /// + public bool Identify(byte[] buffer) + { + if(buffer == null || + buffer.Length < 26) + return false; + + byte[] hdrB = new byte[26]; + Array.Copy(buffer, 0, hdrB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + + return _header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2); + } + + /// + public bool Identify(Stream stream) + { + if(stream == null || + stream.Length < 26) + return false; + + byte[] hdrB = new byte[26]; + stream.Seek(0, SeekOrigin.Begin); + stream.Read(hdrB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + + return _header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2); + } + + /// + public bool Identify(string path) + { + if(!File.Exists(path)) + return false; + + var fstream = new FileStream(path, FileMode.Open, FileAccess.Read); + + if(fstream.Length < 26) + return false; + + byte[] hdrB = new byte[26]; + fstream.Read(hdrB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + + fstream.Close(); + + return _header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2); + } + + /// + public ErrorNumber Open(byte[] buffer) + { + var ms = new MemoryStream(buffer); + ms.Seek(0, SeekOrigin.Begin); + + byte[] hdrB = new byte[26]; + ms.Read(hdrB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + + Entry[] entries = new Entry[_header.entries]; + + for(int i = 0; i < _header.entries; i++) + { + byte[] entry = new byte[12]; + ms.Read(entry, 0, 12); + entries[i] = Marshal.ByteArrayToStructureBigEndian(entry); } - /// - public bool HasResourceFork => _rsrcFork.length > 0; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; - /// - public bool Identify(byte[] buffer) - { - if(buffer == null || - buffer.Length < 26) - return false; - - byte[] hdrB = new byte[26]; - Array.Copy(buffer, 0, hdrB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - - return _header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2); - } - - /// - public bool Identify(Stream stream) - { - if(stream == null || - stream.Length < 26) - return false; - - byte[] hdrB = new byte[26]; - stream.Seek(0, SeekOrigin.Begin); - stream.Read(hdrB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - - return _header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2); - } - - /// - public bool Identify(string path) - { - if(!File.Exists(path)) - return false; - - var fstream = new FileStream(path, FileMode.Open, FileAccess.Read); - - if(fstream.Length < 26) - return false; - - byte[] hdrB = new byte[26]; - fstream.Read(hdrB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - - fstream.Close(); - - return _header.magic == MAGIC && (_header.version == VERSION || _header.version == VERSION2); - } - - /// - public ErrorNumber Open(byte[] buffer) - { - var ms = new MemoryStream(buffer); - ms.Seek(0, SeekOrigin.Begin); - - byte[] hdrB = new byte[26]; - ms.Read(hdrB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - - Entry[] entries = new Entry[_header.entries]; - - for(int i = 0; i < _header.entries; i++) + foreach(Entry entry in entries) + switch((AppleSingleEntryID)entry.id) { - byte[] entry = new byte[12]; - ms.Read(entry, 0, 12); - entries[i] = Marshal.ByteArrayToStructureBigEndian(entry); + case AppleSingleEntryID.DataFork: + _dataFork = entry; + + break; + case AppleSingleEntryID.FileDates: + ms.Seek(entry.offset, SeekOrigin.Begin); + byte[] datesB = new byte[16]; + ms.Read(datesB, 0, 16); + + FileDates dates = Marshal.ByteArrayToStructureBigEndian(datesB); + + CreationTime = DateHandlers.UnixUnsignedToDateTime(dates.creationDate); + LastWriteTime = DateHandlers.UnixUnsignedToDateTime(dates.modificationDate); + + break; + case AppleSingleEntryID.FileInfo: + ms.Seek(entry.offset, SeekOrigin.Begin); + byte[] finfo = new byte[entry.length]; + ms.Read(finfo, 0, finfo.Length); + + if(_macintoshHome.SequenceEqual(_header.homeFilesystem)) + { + MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.MacToDateTime(macinfo.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); + } + else if(_proDosHome.SequenceEqual(_header.homeFilesystem)) + { + ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); + } + else if(_unixHome.SequenceEqual(_header.homeFilesystem)) + { + UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); + LastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); + } + else if(_dosHome.SequenceEqual(_header.homeFilesystem)) + { + DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + LastWriteTime = + DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); + } + + break; + case AppleSingleEntryID.ResourceFork: + _rsrcFork = entry; + + break; } - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; + ms.Close(); + _isBytes = true; + _bytes = buffer; - foreach(Entry entry in entries) - switch((AppleSingleEntryID)entry.id) - { - case AppleSingleEntryID.DataFork: - _dataFork = entry; + return ErrorNumber.NoError; + } - break; - case AppleSingleEntryID.FileDates: - ms.Seek(entry.offset, SeekOrigin.Begin); - byte[] datesB = new byte[16]; - ms.Read(datesB, 0, 16); + /// + public ErrorNumber Open(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); - FileDates dates = Marshal.ByteArrayToStructureBigEndian(datesB); + byte[] hdrB = new byte[26]; + stream.Read(hdrB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - CreationTime = DateHandlers.UnixUnsignedToDateTime(dates.creationDate); - LastWriteTime = DateHandlers.UnixUnsignedToDateTime(dates.modificationDate); + Entry[] entries = new Entry[_header.entries]; - break; - case AppleSingleEntryID.FileInfo: - ms.Seek(entry.offset, SeekOrigin.Begin); - byte[] finfo = new byte[entry.length]; - ms.Read(finfo, 0, finfo.Length); - - if(_macintoshHome.SequenceEqual(_header.homeFilesystem)) - { - MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.MacToDateTime(macinfo.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); - } - else if(_proDosHome.SequenceEqual(_header.homeFilesystem)) - { - ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); - } - else if(_unixHome.SequenceEqual(_header.homeFilesystem)) - { - UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); - LastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); - } - else if(_dosHome.SequenceEqual(_header.homeFilesystem)) - { - DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - LastWriteTime = - DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); - } - - break; - case AppleSingleEntryID.ResourceFork: - _rsrcFork = entry; - - break; - } - - ms.Close(); - _isBytes = true; - _bytes = buffer; - - return ErrorNumber.NoError; + for(int i = 0; i < _header.entries; i++) + { + byte[] entry = new byte[12]; + stream.Read(entry, 0, 12); + entries[i] = Marshal.ByteArrayToStructureBigEndian(entry); } - /// - public ErrorNumber Open(Stream stream) - { - stream.Seek(0, SeekOrigin.Begin); + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; - byte[] hdrB = new byte[26]; - stream.Read(hdrB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - - Entry[] entries = new Entry[_header.entries]; - - for(int i = 0; i < _header.entries; i++) + foreach(Entry entry in entries) + switch((AppleSingleEntryID)entry.id) { - byte[] entry = new byte[12]; - stream.Read(entry, 0, 12); - entries[i] = Marshal.ByteArrayToStructureBigEndian(entry); + case AppleSingleEntryID.DataFork: + _dataFork = entry; + + break; + case AppleSingleEntryID.FileDates: + stream.Seek(entry.offset, SeekOrigin.Begin); + byte[] datesB = new byte[16]; + stream.Read(datesB, 0, 16); + + FileDates dates = Marshal.ByteArrayToStructureBigEndian(datesB); + + CreationTime = DateHandlers.MacToDateTime(dates.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(dates.modificationDate); + + break; + case AppleSingleEntryID.FileInfo: + stream.Seek(entry.offset, SeekOrigin.Begin); + byte[] finfo = new byte[entry.length]; + stream.Read(finfo, 0, finfo.Length); + + if(_macintoshHome.SequenceEqual(_header.homeFilesystem)) + { + MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.MacToDateTime(macinfo.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); + } + else if(_proDosHome.SequenceEqual(_header.homeFilesystem)) + { + ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); + } + else if(_unixHome.SequenceEqual(_header.homeFilesystem)) + { + UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); + LastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); + } + else if(_dosHome.SequenceEqual(_header.homeFilesystem)) + { + DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + LastWriteTime = + DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); + } + + break; + case AppleSingleEntryID.ResourceFork: + _rsrcFork = entry; + + break; } - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; + stream.Seek(0, SeekOrigin.Begin); + _isStream = true; + _stream = stream; - foreach(Entry entry in entries) - switch((AppleSingleEntryID)entry.id) - { - case AppleSingleEntryID.DataFork: - _dataFork = entry; + return ErrorNumber.NoError; + } - break; - case AppleSingleEntryID.FileDates: - stream.Seek(entry.offset, SeekOrigin.Begin); - byte[] datesB = new byte[16]; - stream.Read(datesB, 0, 16); + /// + public ErrorNumber Open(string path) + { + var fs = new FileStream(path, FileMode.Open, FileAccess.Read); + fs.Seek(0, SeekOrigin.Begin); - FileDates dates = Marshal.ByteArrayToStructureBigEndian(datesB); + byte[] hdrB = new byte[26]; + fs.Read(hdrB, 0, 26); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - CreationTime = DateHandlers.MacToDateTime(dates.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(dates.modificationDate); + Entry[] entries = new Entry[_header.entries]; - break; - case AppleSingleEntryID.FileInfo: - stream.Seek(entry.offset, SeekOrigin.Begin); - byte[] finfo = new byte[entry.length]; - stream.Read(finfo, 0, finfo.Length); - - if(_macintoshHome.SequenceEqual(_header.homeFilesystem)) - { - MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.MacToDateTime(macinfo.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); - } - else if(_proDosHome.SequenceEqual(_header.homeFilesystem)) - { - ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); - } - else if(_unixHome.SequenceEqual(_header.homeFilesystem)) - { - UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - CreationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); - LastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); - } - else if(_dosHome.SequenceEqual(_header.homeFilesystem)) - { - DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - LastWriteTime = - DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); - } - - break; - case AppleSingleEntryID.ResourceFork: - _rsrcFork = entry; - - break; - } - - stream.Seek(0, SeekOrigin.Begin); - _isStream = true; - _stream = stream; - - return ErrorNumber.NoError; + for(int i = 0; i < _header.entries; i++) + { + byte[] entry = new byte[12]; + fs.Read(entry, 0, 12); + entries[i] = Marshal.ByteArrayToStructureBigEndian(entry); } - /// - public ErrorNumber Open(string path) - { - var fs = new FileStream(path, FileMode.Open, FileAccess.Read); - fs.Seek(0, SeekOrigin.Begin); + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; - byte[] hdrB = new byte[26]; - fs.Read(hdrB, 0, 26); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - - Entry[] entries = new Entry[_header.entries]; - - for(int i = 0; i < _header.entries; i++) + foreach(Entry entry in entries) + switch((AppleSingleEntryID)entry.id) { - byte[] entry = new byte[12]; - fs.Read(entry, 0, 12); - entries[i] = Marshal.ByteArrayToStructureBigEndian(entry); + case AppleSingleEntryID.DataFork: + _dataFork = entry; + + break; + case AppleSingleEntryID.FileDates: + fs.Seek(entry.offset, SeekOrigin.Begin); + byte[] datesB = new byte[16]; + fs.Read(datesB, 0, 16); + + FileDates dates = Marshal.ByteArrayToStructureBigEndian(datesB); + + CreationTime = DateHandlers.MacToDateTime(dates.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(dates.modificationDate); + + break; + case AppleSingleEntryID.FileInfo: + fs.Seek(entry.offset, SeekOrigin.Begin); + byte[] finfo = new byte[entry.length]; + fs.Read(finfo, 0, finfo.Length); + + if(_macintoshHome.SequenceEqual(_header.homeFilesystem)) + { + MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.MacToDateTime(macinfo.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); + } + else if(_proDosHome.SequenceEqual(_header.homeFilesystem)) + { + ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); + LastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); + } + else if(_unixHome.SequenceEqual(_header.homeFilesystem)) + { + UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + CreationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); + LastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); + } + else if(_dosHome.SequenceEqual(_header.homeFilesystem)) + { + DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + + LastWriteTime = + DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); + } + + break; + case AppleSingleEntryID.ResourceFork: + _rsrcFork = entry; + + break; } - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; + fs.Close(); + _isPath = true; + BasePath = path; - foreach(Entry entry in entries) - switch((AppleSingleEntryID)entry.id) - { - case AppleSingleEntryID.DataFork: - _dataFork = entry; + return ErrorNumber.NoError; + } - break; - case AppleSingleEntryID.FileDates: - fs.Seek(entry.offset, SeekOrigin.Begin); - byte[] datesB = new byte[16]; - fs.Read(datesB, 0, 16); + enum AppleSingleEntryID : uint + { + Invalid = 0, DataFork = 1, ResourceFork = 2, + RealName = 3, Comment = 4, Icon = 5, + ColorIcon = 6, FileInfo = 7, FileDates = 8, + FinderInfo = 9, MacFileInfo = 10, ProDOSFileInfo = 11, + DOSFileInfo = 12, ShortName = 13, AfpFileInfo = 14, + DirectoryID = 15 + } - FileDates dates = Marshal.ByteArrayToStructureBigEndian(datesB); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Header + { + public readonly uint magic; + public readonly uint version; + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] + public readonly byte[] homeFilesystem; + public readonly ushort entries; + } - CreationTime = DateHandlers.MacToDateTime(dates.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(dates.modificationDate); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Entry + { + public readonly uint id; + public readonly uint offset; + public readonly uint length; + } - break; - case AppleSingleEntryID.FileInfo: - fs.Seek(entry.offset, SeekOrigin.Begin); - byte[] finfo = new byte[entry.length]; - fs.Read(finfo, 0, finfo.Length); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct FileDates + { + public readonly uint creationDate; + public readonly uint modificationDate; + public readonly uint backupDate; + public readonly uint accessDate; + } - if(_macintoshHome.SequenceEqual(_header.homeFilesystem)) - { - MacFileInfo macinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct MacFileInfo + { + public readonly uint creationDate; + public readonly uint modificationDate; + public readonly uint backupDate; + public readonly uint accessDate; + } - CreationTime = DateHandlers.MacToDateTime(macinfo.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(macinfo.modificationDate); - } - else if(_proDosHome.SequenceEqual(_header.homeFilesystem)) - { - ProDOSFileInfo prodosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct UnixFileInfo + { + public readonly uint creationDate; + public readonly uint accessDate; + public readonly uint modificationDate; + } - CreationTime = DateHandlers.MacToDateTime(prodosinfo.creationDate); - LastWriteTime = DateHandlers.MacToDateTime(prodosinfo.modificationDate); - } - else if(_unixHome.SequenceEqual(_header.homeFilesystem)) - { - UnixFileInfo unixinfo = Marshal.ByteArrayToStructureBigEndian(finfo); + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct DOSFileInfo + { + public readonly ushort modificationDate; + public readonly ushort modificationTime; + public readonly ushort attributes; + } - CreationTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.creationDate); - LastWriteTime = DateHandlers.UnixUnsignedToDateTime(unixinfo.modificationDate); - } - else if(_dosHome.SequenceEqual(_header.homeFilesystem)) - { - DOSFileInfo dosinfo = Marshal.ByteArrayToStructureBigEndian(finfo); - - LastWriteTime = - DateHandlers.DosToDateTime(dosinfo.modificationDate, dosinfo.modificationTime); - } - - break; - case AppleSingleEntryID.ResourceFork: - _rsrcFork = entry; - - break; - } - - fs.Close(); - _isPath = true; - BasePath = path; - - return ErrorNumber.NoError; - } - - enum AppleSingleEntryID : uint - { - Invalid = 0, DataFork = 1, ResourceFork = 2, - RealName = 3, Comment = 4, Icon = 5, - ColorIcon = 6, FileInfo = 7, FileDates = 8, - FinderInfo = 9, MacFileInfo = 10, ProDOSFileInfo = 11, - DOSFileInfo = 12, ShortName = 13, AfpFileInfo = 14, - DirectoryID = 15 - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Header - { - public readonly uint magic; - public readonly uint version; - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] - public readonly byte[] homeFilesystem; - public readonly ushort entries; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Entry - { - public readonly uint id; - public readonly uint offset; - public readonly uint length; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct FileDates - { - public readonly uint creationDate; - public readonly uint modificationDate; - public readonly uint backupDate; - public readonly uint accessDate; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct MacFileInfo - { - public readonly uint creationDate; - public readonly uint modificationDate; - public readonly uint backupDate; - public readonly uint accessDate; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct UnixFileInfo - { - public readonly uint creationDate; - public readonly uint accessDate; - public readonly uint modificationDate; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct DOSFileInfo - { - public readonly ushort modificationDate; - public readonly ushort modificationTime; - public readonly ushort attributes; - } - - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct ProDOSFileInfo - { - public readonly uint creationDate; - public readonly uint modificationDate; - public readonly uint backupDate; - public readonly ushort access; - public readonly ushort fileType; - public readonly uint auxType; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct ProDOSFileInfo + { + public readonly uint creationDate; + public readonly uint modificationDate; + public readonly uint backupDate; + public readonly ushort access; + public readonly ushort fileType; + public readonly uint auxType; } } \ No newline at end of file diff --git a/Aaru.Filters/BZip2.cs b/Aaru.Filters/BZip2.cs index 919d5dd50..abc9b2511 100644 --- a/Aaru.Filters/BZip2.cs +++ b/Aaru.Filters/BZip2.cs @@ -37,189 +37,188 @@ using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; using Ionic.BZip2; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// Decompress bz2 files while reading +public class BZip2 : IFilter { + Stream _dataStream; + Stream _innerStream; + /// - /// Decompress bz2 files while reading - public class BZip2 : IFilter + public string Name => "BZip2"; + /// + public Guid Id => new("FCCFB0C3-32EF-40D8-9714-2333F6AC72A9"); + /// + public string Author => "Natalia Portillo"; + + /// + public void Close() { - Stream _dataStream; - Stream _innerStream; - - /// - public string Name => "BZip2"; - /// - public Guid Id => new("FCCFB0C3-32EF-40D8-9714-2333F6AC72A9"); - /// - public string Author => "Natalia Portillo"; - - /// - public void Close() - { - _dataStream?.Close(); - _dataStream = null; - BasePath = null; - } - - /// - public string BasePath { get; private set; } - - /// - public Stream GetDataForkStream() => _innerStream; - - /// - public string Path => BasePath; - - /// - public Stream GetResourceForkStream() => null; - - /// - public bool HasResourceFork => false; - - /// - public bool Identify(byte[] buffer) - { - if(buffer[0] != 0x42 || - buffer[1] != 0x5A || - buffer[2] != 0x68 || - buffer[3] < 0x31 || - buffer[3] > 0x39) - return false; - - if(buffer.Length <= 512) - return true; - - return buffer[^512] != 0x6B || buffer[^511] != 0x6F || buffer[^510] != 0x6C || buffer[^509] != 0x79; - } - - /// - public bool Identify(Stream stream) - { - byte[] buffer = new byte[4]; - - stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, 4); - stream.Seek(0, SeekOrigin.Begin); - - if(buffer[0] != 0x42 || - buffer[1] != 0x5A || - buffer[2] != 0x68 || - buffer[3] < 0x31 || - buffer[3] > 0x39) - return false; - - if(stream.Length <= 512) - return true; - - stream.Seek(-512, SeekOrigin.End); - stream.Read(buffer, 0, 4); - stream.Seek(0, SeekOrigin.Begin); - - // Check it is not an UDIF - return buffer[0] != 0x6B || buffer[1] != 0x6F || buffer[2] != 0x6C || buffer[3] != 0x79; - } - - /// - public bool Identify(string path) - { - if(!File.Exists(path)) - return false; - - var stream = new FileStream(path, FileMode.Open, FileAccess.Read); - byte[] buffer = new byte[4]; - - stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, 4); - stream.Seek(0, SeekOrigin.Begin); - - if(buffer[0] != 0x42 || - buffer[1] != 0x5A || - buffer[2] != 0x68 || - buffer[3] < 0x31 || - buffer[3] > 0x39) - return false; - - if(stream.Length <= 512) - return true; - - stream.Seek(-512, SeekOrigin.End); - stream.Read(buffer, 0, 4); - stream.Seek(0, SeekOrigin.Begin); - - // Check it is not an UDIF - return buffer[0] != 0x6B || buffer[1] != 0x6F || buffer[2] != 0x6C || buffer[3] != 0x79; - } - - /// - public ErrorNumber Open(byte[] buffer) - { - _dataStream = new MemoryStream(buffer); - BasePath = null; - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - _innerStream = new ForcedSeekStream(_dataStream, false); - DataForkLength = _innerStream.Length; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(Stream stream) - { - _dataStream = stream; - BasePath = null; - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - _innerStream = new ForcedSeekStream(_dataStream, false); - DataForkLength = _innerStream.Length; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(string path) - { - _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); - BasePath = System.IO.Path.GetFullPath(path); - - var fi = new FileInfo(path); - CreationTime = fi.CreationTimeUtc; - LastWriteTime = fi.LastWriteTimeUtc; - _innerStream = new ForcedSeekStream(_dataStream, false); - DataForkLength = _innerStream.Length; - - return ErrorNumber.NoError; - } - - /// - public DateTime CreationTime { get; private set; } - - /// - public long DataForkLength { get; private set; } - - /// - public DateTime LastWriteTime { get; private set; } - - /// - public long Length => DataForkLength; - - /// - public long ResourceForkLength => 0; - - /// - public string Filename - { - get - { - if(BasePath?.EndsWith(".bz2", StringComparison.InvariantCultureIgnoreCase) == true) - return BasePath.Substring(0, BasePath.Length - 4); - - return BasePath?.EndsWith(".bzip2", StringComparison.InvariantCultureIgnoreCase) == true - ? BasePath.Substring(0, BasePath.Length - 6) : BasePath; - } - } - - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + _dataStream?.Close(); + _dataStream = null; + BasePath = null; } + + /// + public string BasePath { get; private set; } + + /// + public Stream GetDataForkStream() => _innerStream; + + /// + public string Path => BasePath; + + /// + public Stream GetResourceForkStream() => null; + + /// + public bool HasResourceFork => false; + + /// + public bool Identify(byte[] buffer) + { + if(buffer[0] != 0x42 || + buffer[1] != 0x5A || + buffer[2] != 0x68 || + buffer[3] < 0x31 || + buffer[3] > 0x39) + return false; + + if(buffer.Length <= 512) + return true; + + return buffer[^512] != 0x6B || buffer[^511] != 0x6F || buffer[^510] != 0x6C || buffer[^509] != 0x79; + } + + /// + public bool Identify(Stream stream) + { + byte[] buffer = new byte[4]; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 4); + stream.Seek(0, SeekOrigin.Begin); + + if(buffer[0] != 0x42 || + buffer[1] != 0x5A || + buffer[2] != 0x68 || + buffer[3] < 0x31 || + buffer[3] > 0x39) + return false; + + if(stream.Length <= 512) + return true; + + stream.Seek(-512, SeekOrigin.End); + stream.Read(buffer, 0, 4); + stream.Seek(0, SeekOrigin.Begin); + + // Check it is not an UDIF + return buffer[0] != 0x6B || buffer[1] != 0x6F || buffer[2] != 0x6C || buffer[3] != 0x79; + } + + /// + public bool Identify(string path) + { + if(!File.Exists(path)) + return false; + + var stream = new FileStream(path, FileMode.Open, FileAccess.Read); + byte[] buffer = new byte[4]; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 4); + stream.Seek(0, SeekOrigin.Begin); + + if(buffer[0] != 0x42 || + buffer[1] != 0x5A || + buffer[2] != 0x68 || + buffer[3] < 0x31 || + buffer[3] > 0x39) + return false; + + if(stream.Length <= 512) + return true; + + stream.Seek(-512, SeekOrigin.End); + stream.Read(buffer, 0, 4); + stream.Seek(0, SeekOrigin.Begin); + + // Check it is not an UDIF + return buffer[0] != 0x6B || buffer[1] != 0x6F || buffer[2] != 0x6C || buffer[3] != 0x79; + } + + /// + public ErrorNumber Open(byte[] buffer) + { + _dataStream = new MemoryStream(buffer); + BasePath = null; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + _innerStream = new ForcedSeekStream(_dataStream, false); + DataForkLength = _innerStream.Length; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(Stream stream) + { + _dataStream = stream; + BasePath = null; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + _innerStream = new ForcedSeekStream(_dataStream, false); + DataForkLength = _innerStream.Length; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(string path) + { + _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); + BasePath = System.IO.Path.GetFullPath(path); + + var fi = new FileInfo(path); + CreationTime = fi.CreationTimeUtc; + LastWriteTime = fi.LastWriteTimeUtc; + _innerStream = new ForcedSeekStream(_dataStream, false); + DataForkLength = _innerStream.Length; + + return ErrorNumber.NoError; + } + + /// + public DateTime CreationTime { get; private set; } + + /// + public long DataForkLength { get; private set; } + + /// + public DateTime LastWriteTime { get; private set; } + + /// + public long Length => DataForkLength; + + /// + public long ResourceForkLength => 0; + + /// + public string Filename + { + get + { + if(BasePath?.EndsWith(".bz2", StringComparison.InvariantCultureIgnoreCase) == true) + return BasePath.Substring(0, BasePath.Length - 4); + + return BasePath?.EndsWith(".bzip2", StringComparison.InvariantCultureIgnoreCase) == true + ? BasePath.Substring(0, BasePath.Length - 6) : BasePath; + } + } + + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); } \ No newline at end of file diff --git a/Aaru.Filters/ForcedSeekStream.cs b/Aaru.Filters/ForcedSeekStream.cs index a225e7ef4..d1483e097 100644 --- a/Aaru.Filters/ForcedSeekStream.cs +++ b/Aaru.Filters/ForcedSeekStream.cs @@ -33,129 +33,115 @@ using System; using System.IO; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// ForcedSeekStream allows to seek a forward-readable stream (like System.IO.Compression streams) by doing the +/// slow and known trick of rewinding and forward reading until arriving the desired position. +/// +/// +public sealed class ForcedSeekStream : Stream where T : Stream { - /// - /// ForcedSeekStream allows to seek a forward-readable stream (like System.IO.Compression streams) by doing the - /// slow and known trick of rewinding and forward reading until arriving the desired position. - /// + const int BUFFER_LEN = 1048576; + readonly string _backFile; + readonly FileStream _backStream; + readonly T _baseStream; + long _streamLength; + + /// Initializes a new instance of the class. + /// The real (uncompressed) length of the stream. + /// Parameters that are used to create the base stream. /// - public sealed class ForcedSeekStream : Stream where T : Stream + public ForcedSeekStream(long length, params object[] args) { - const int BUFFER_LEN = 1048576; - readonly string _backFile; - readonly FileStream _backStream; - readonly T _baseStream; - long _streamLength; + _streamLength = length; + _baseStream = (T)Activator.CreateInstance(typeof(T), args); + _backFile = Path.GetTempFileName(); + _backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None); - /// Initializes a new instance of the class. - /// The real (uncompressed) length of the stream. - /// Parameters that are used to create the base stream. - /// - public ForcedSeekStream(long length, params object[] args) - { - _streamLength = length; - _baseStream = (T)Activator.CreateInstance(typeof(T), args); - _backFile = Path.GetTempFileName(); - _backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None); - - if(length == 0) - CalculateLength(); - } - - /// Initializes a new instance of the class. - /// Parameters that are used to create the base stream. - /// - public ForcedSeekStream(params object[] args) - { - _baseStream = (T)Activator.CreateInstance(typeof(T), args); - _backFile = Path.GetTempFileName(); - _backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None); + if(length == 0) CalculateLength(); + } + + /// Initializes a new instance of the class. + /// Parameters that are used to create the base stream. + /// + public ForcedSeekStream(params object[] args) + { + _baseStream = (T)Activator.CreateInstance(typeof(T), args); + _backFile = Path.GetTempFileName(); + _backStream = new FileStream(_backFile, FileMode.Open, FileAccess.ReadWrite, FileShare.None); + CalculateLength(); + } + + /// + public override bool CanRead => _baseStream.CanRead; + + /// + public override bool CanSeek => true; + + /// + public override bool CanWrite => false; + + /// + public override long Length => _streamLength; + + /// + public override long Position + { + get => _backStream.Position; + + set => SetPosition(value); + } + + /// + /// Calculates the real (uncompressed) length of the stream. It basically reads (uncompresses) the whole stream to + /// memory discarding its contents, so it should be used as a last resort. + /// + /// The length. + public void CalculateLength() + { + int read; + + do + { + byte[] buffer = new byte[BUFFER_LEN]; + read = _baseStream.Read(buffer, 0, BUFFER_LEN); + _backStream.Write(buffer, 0, read); + } while(read == BUFFER_LEN); + + _streamLength = _backStream.Length; + _backStream.Position = 0; + } + + void SetPosition(long position) + { + if(position == _backStream.Position) + return; + + if(position < _backStream.Length) + { + _backStream.Position = position; + + return; } - /// - public override bool CanRead => _baseStream.CanRead; + if(position > _streamLength) + position = _streamLength; - /// - public override bool CanSeek => true; + _backStream.Position = _backStream.Length; + long toPosition = position - _backStream.Position; + int fullBufferReads = (int)(toPosition / BUFFER_LEN); + int restToRead = (int)(toPosition % BUFFER_LEN); + byte[] buffer; + var bufPos = 0; + var left = BUFFER_LEN; - /// - public override bool CanWrite => false; - - /// - public override long Length => _streamLength; - - /// - public override long Position + for(int i = 0; i < fullBufferReads; i++) { - get => _backStream.Position; - - set => SetPosition(value); - } - - /// - /// Calculates the real (uncompressed) length of the stream. It basically reads (uncompresses) the whole stream to - /// memory discarding its contents, so it should be used as a last resort. - /// - /// The length. - public void CalculateLength() - { - int read; - - do - { - byte[] buffer = new byte[BUFFER_LEN]; - read = _baseStream.Read(buffer, 0, BUFFER_LEN); - _backStream.Write(buffer, 0, read); - } while(read == BUFFER_LEN); - - _streamLength = _backStream.Length; - _backStream.Position = 0; - } - - void SetPosition(long position) - { - if(position == _backStream.Position) - return; - - if(position < _backStream.Length) - { - _backStream.Position = position; - - return; - } - - if(position > _streamLength) - position = _streamLength; - - _backStream.Position = _backStream.Length; - long toPosition = position - _backStream.Position; - int fullBufferReads = (int)(toPosition / BUFFER_LEN); - int restToRead = (int)(toPosition % BUFFER_LEN); - byte[] buffer; - var bufPos = 0; - var left = BUFFER_LEN; - - for(int i = 0; i < fullBufferReads; i++) - { - buffer = new byte[BUFFER_LEN]; - bufPos = 0; - left = BUFFER_LEN; - - while(left > 0) - { - var done = _baseStream.Read(buffer, bufPos, left); - left -= done; - bufPos += done; - } - - _backStream.Write(buffer, 0, BUFFER_LEN); - } - - buffer = new byte[restToRead]; + buffer = new byte[BUFFER_LEN]; bufPos = 0; - left = restToRead; + left = BUFFER_LEN; while(left > 0) { @@ -164,95 +150,108 @@ namespace Aaru.Filters bufPos += done; } - _backStream.Write(buffer, 0, restToRead); + _backStream.Write(buffer, 0, BUFFER_LEN); } - /// - public override void Flush() + buffer = new byte[restToRead]; + bufPos = 0; + left = restToRead; + + while(left > 0) { - _baseStream.Flush(); - _backStream.Flush(); + var done = _baseStream.Read(buffer, bufPos, left); + left -= done; + bufPos += done; } - /// - public override int Read(byte[] buffer, int offset, int count) - { - if(_backStream.Position + count > _streamLength) - count = (int)(_streamLength - _backStream.Position); + _backStream.Write(buffer, 0, restToRead); + } - if(_backStream.Position + count <= _backStream.Length) - return _backStream.Read(buffer, offset, count); + /// + public override void Flush() + { + _baseStream.Flush(); + _backStream.Flush(); + } - var oldPosition = _backStream.Position; - SetPosition(_backStream.Position + count); - SetPosition(oldPosition); + /// + public override int Read(byte[] buffer, int offset, int count) + { + if(_backStream.Position + count > _streamLength) + count = (int)(_streamLength - _backStream.Position); + if(_backStream.Position + count <= _backStream.Length) return _backStream.Read(buffer, offset, count); - } - /// - public override int ReadByte() - { - if(_backStream.Position + 1 > _streamLength) - return -1; + var oldPosition = _backStream.Position; + SetPosition(_backStream.Position + count); + SetPosition(oldPosition); - if(_backStream.Position + 1 <= _backStream.Length) - return _backStream.ReadByte(); + return _backStream.Read(buffer, offset, count); + } - SetPosition(_backStream.Position + 1); - SetPosition(_backStream.Position - 1); + /// + public override int ReadByte() + { + if(_backStream.Position + 1 > _streamLength) + return -1; + if(_backStream.Position + 1 <= _backStream.Length) return _backStream.ReadByte(); - } - /// - public override long Seek(long offset, SeekOrigin origin) + SetPosition(_backStream.Position + 1); + SetPosition(_backStream.Position - 1); + + return _backStream.ReadByte(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) + { + switch(origin) { - switch(origin) - { - case SeekOrigin.Begin: - if(offset < 0) - throw new IOException("Cannot seek before stream start."); + case SeekOrigin.Begin: + if(offset < 0) + throw new IOException("Cannot seek before stream start."); - SetPosition(offset); + SetPosition(offset); - break; - case SeekOrigin.End: - if(offset > 0) - throw new IOException("Cannot seek after stream end."); + break; + case SeekOrigin.End: + if(offset > 0) + throw new IOException("Cannot seek after stream end."); - if(_streamLength == 0) - CalculateLength(); + if(_streamLength == 0) + CalculateLength(); - SetPosition(_streamLength + offset); + SetPosition(_streamLength + offset); - break; - default: - SetPosition(_backStream.Position + offset); + break; + default: + SetPosition(_backStream.Position + offset); - break; - } - - return _backStream.Position; + break; } - /// - public override void SetLength(long value) => throw new NotSupportedException(); + return _backStream.Position; + } - /// - public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + /// + public override void SetLength(long value) => throw new NotSupportedException(); - /// - public override void Close() - { - _backStream?.Close(); - File.Delete(_backFile); - } + /// + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); - ~ForcedSeekStream() - { - _backStream?.Close(); - File.Delete(_backFile); - } + /// + public override void Close() + { + _backStream?.Close(); + File.Delete(_backFile); + } + + ~ForcedSeekStream() + { + _backStream?.Close(); + File.Delete(_backFile); } } \ No newline at end of file diff --git a/Aaru.Filters/GZip.cs b/Aaru.Filters/GZip.cs index 4decb70e9..4aae13e57 100644 --- a/Aaru.Filters/GZip.cs +++ b/Aaru.Filters/GZip.cs @@ -37,187 +37,186 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// Decompress gzip files while reading +public sealed class GZip : IFilter { + Stream _dataStream; + uint _decompressedSize; + Stream _zStream; + /// - /// Decompress gzip files while reading - public sealed class GZip : IFilter + public string Name => "GZip"; + /// + public Guid Id => new("F4996661-4A29-42C9-A2C7-3904EF40F3B0"); + /// + public string Author => "Natalia Portillo"; + + /// + public void Close() { - Stream _dataStream; - uint _decompressedSize; - Stream _zStream; - - /// - public string Name => "GZip"; - /// - public Guid Id => new("F4996661-4A29-42C9-A2C7-3904EF40F3B0"); - /// - public string Author => "Natalia Portillo"; - - /// - public void Close() - { - _dataStream?.Close(); - _dataStream = null; - BasePath = null; - } - - /// - public string BasePath { get; private set; } - - /// - public Stream GetDataForkStream() => _zStream; - - /// - public string Path => BasePath; - - /// - public Stream GetResourceForkStream() => null; - - /// - public bool HasResourceFork => false; - - /// - public bool Identify(byte[] buffer) => buffer[0] == 0x1F && buffer[1] == 0x8B && buffer[2] == 0x08; - - /// - public bool Identify(Stream stream) - { - byte[] buffer = new byte[3]; - - stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, 3); - stream.Seek(0, SeekOrigin.Begin); - - return buffer[0] == 0x1F && buffer[1] == 0x8B && buffer[2] == 0x08; - } - - /// - public bool Identify(string path) - { - if(!File.Exists(path)) - return false; - - var stream = new FileStream(path, FileMode.Open, FileAccess.Read); - byte[] buffer = new byte[3]; - - stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, 3); - stream.Seek(0, SeekOrigin.Begin); - - return buffer[0] == 0x1F && buffer[1] == 0x8B && buffer[2] == 0x08; - } - - /// - public ErrorNumber Open(byte[] buffer) - { - byte[] mtimeB = new byte[4]; - byte[] isizeB = new byte[4]; - - _dataStream = new MemoryStream(buffer); - BasePath = null; - - _dataStream.Seek(4, SeekOrigin.Begin); - _dataStream.Read(mtimeB, 0, 4); - _dataStream.Seek(-4, SeekOrigin.End); - _dataStream.Read(isizeB, 0, 4); - _dataStream.Seek(0, SeekOrigin.Begin); - - uint mtime = BitConverter.ToUInt32(mtimeB, 0); - uint isize = BitConverter.ToUInt32(isizeB, 0); - - _decompressedSize = isize; - CreationTime = DateHandlers.UnixUnsignedToDateTime(mtime); - LastWriteTime = CreationTime; - - _zStream = new ForcedSeekStream(_decompressedSize, _dataStream, CompressionMode.Decompress); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(Stream stream) - { - byte[] mtimeB = new byte[4]; - byte[] isizeB = new byte[4]; - - _dataStream = stream; - BasePath = null; - - _dataStream.Seek(4, SeekOrigin.Begin); - _dataStream.Read(mtimeB, 0, 4); - _dataStream.Seek(-4, SeekOrigin.End); - _dataStream.Read(isizeB, 0, 4); - _dataStream.Seek(0, SeekOrigin.Begin); - - uint mtime = BitConverter.ToUInt32(mtimeB, 0); - uint isize = BitConverter.ToUInt32(isizeB, 0); - - _decompressedSize = isize; - CreationTime = DateHandlers.UnixUnsignedToDateTime(mtime); - LastWriteTime = CreationTime; - - _zStream = new ForcedSeekStream(_decompressedSize, _dataStream, CompressionMode.Decompress); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(string path) - { - byte[] mtimeB = new byte[4]; - byte[] isizeB = new byte[4]; - - _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); - BasePath = System.IO.Path.GetFullPath(path); - - _dataStream.Seek(4, SeekOrigin.Begin); - _dataStream.Read(mtimeB, 0, 4); - _dataStream.Seek(-4, SeekOrigin.End); - _dataStream.Read(isizeB, 0, 4); - _dataStream.Seek(0, SeekOrigin.Begin); - - uint mtime = BitConverter.ToUInt32(mtimeB, 0); - uint isize = BitConverter.ToUInt32(isizeB, 0); - - _decompressedSize = isize; - var fi = new FileInfo(path); - CreationTime = fi.CreationTimeUtc; - LastWriteTime = DateHandlers.UnixUnsignedToDateTime(mtime); - _zStream = new ForcedSeekStream(_decompressedSize, _dataStream, CompressionMode.Decompress); - - return ErrorNumber.NoError; - } - - /// - public DateTime CreationTime { get; private set; } - - /// - public long DataForkLength => _decompressedSize; - - /// - public DateTime LastWriteTime { get; private set; } - - /// - public long Length => _decompressedSize; - - /// - public long ResourceForkLength => 0; - - /// - public string Filename - { - get - { - if(BasePath?.EndsWith(".gz", StringComparison.InvariantCultureIgnoreCase) == true) - return BasePath.Substring(0, BasePath.Length - 3); - - return BasePath?.EndsWith(".gzip", StringComparison.InvariantCultureIgnoreCase) == true - ? BasePath.Substring(0, BasePath.Length - 5) : BasePath; - } - } - - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + _dataStream?.Close(); + _dataStream = null; + BasePath = null; } + + /// + public string BasePath { get; private set; } + + /// + public Stream GetDataForkStream() => _zStream; + + /// + public string Path => BasePath; + + /// + public Stream GetResourceForkStream() => null; + + /// + public bool HasResourceFork => false; + + /// + public bool Identify(byte[] buffer) => buffer[0] == 0x1F && buffer[1] == 0x8B && buffer[2] == 0x08; + + /// + public bool Identify(Stream stream) + { + byte[] buffer = new byte[3]; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 3); + stream.Seek(0, SeekOrigin.Begin); + + return buffer[0] == 0x1F && buffer[1] == 0x8B && buffer[2] == 0x08; + } + + /// + public bool Identify(string path) + { + if(!File.Exists(path)) + return false; + + var stream = new FileStream(path, FileMode.Open, FileAccess.Read); + byte[] buffer = new byte[3]; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 3); + stream.Seek(0, SeekOrigin.Begin); + + return buffer[0] == 0x1F && buffer[1] == 0x8B && buffer[2] == 0x08; + } + + /// + public ErrorNumber Open(byte[] buffer) + { + byte[] mtimeB = new byte[4]; + byte[] isizeB = new byte[4]; + + _dataStream = new MemoryStream(buffer); + BasePath = null; + + _dataStream.Seek(4, SeekOrigin.Begin); + _dataStream.Read(mtimeB, 0, 4); + _dataStream.Seek(-4, SeekOrigin.End); + _dataStream.Read(isizeB, 0, 4); + _dataStream.Seek(0, SeekOrigin.Begin); + + uint mtime = BitConverter.ToUInt32(mtimeB, 0); + uint isize = BitConverter.ToUInt32(isizeB, 0); + + _decompressedSize = isize; + CreationTime = DateHandlers.UnixUnsignedToDateTime(mtime); + LastWriteTime = CreationTime; + + _zStream = new ForcedSeekStream(_decompressedSize, _dataStream, CompressionMode.Decompress); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(Stream stream) + { + byte[] mtimeB = new byte[4]; + byte[] isizeB = new byte[4]; + + _dataStream = stream; + BasePath = null; + + _dataStream.Seek(4, SeekOrigin.Begin); + _dataStream.Read(mtimeB, 0, 4); + _dataStream.Seek(-4, SeekOrigin.End); + _dataStream.Read(isizeB, 0, 4); + _dataStream.Seek(0, SeekOrigin.Begin); + + uint mtime = BitConverter.ToUInt32(mtimeB, 0); + uint isize = BitConverter.ToUInt32(isizeB, 0); + + _decompressedSize = isize; + CreationTime = DateHandlers.UnixUnsignedToDateTime(mtime); + LastWriteTime = CreationTime; + + _zStream = new ForcedSeekStream(_decompressedSize, _dataStream, CompressionMode.Decompress); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(string path) + { + byte[] mtimeB = new byte[4]; + byte[] isizeB = new byte[4]; + + _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); + BasePath = System.IO.Path.GetFullPath(path); + + _dataStream.Seek(4, SeekOrigin.Begin); + _dataStream.Read(mtimeB, 0, 4); + _dataStream.Seek(-4, SeekOrigin.End); + _dataStream.Read(isizeB, 0, 4); + _dataStream.Seek(0, SeekOrigin.Begin); + + uint mtime = BitConverter.ToUInt32(mtimeB, 0); + uint isize = BitConverter.ToUInt32(isizeB, 0); + + _decompressedSize = isize; + var fi = new FileInfo(path); + CreationTime = fi.CreationTimeUtc; + LastWriteTime = DateHandlers.UnixUnsignedToDateTime(mtime); + _zStream = new ForcedSeekStream(_decompressedSize, _dataStream, CompressionMode.Decompress); + + return ErrorNumber.NoError; + } + + /// + public DateTime CreationTime { get; private set; } + + /// + public long DataForkLength => _decompressedSize; + + /// + public DateTime LastWriteTime { get; private set; } + + /// + public long Length => _decompressedSize; + + /// + public long ResourceForkLength => 0; + + /// + public string Filename + { + get + { + if(BasePath?.EndsWith(".gz", StringComparison.InvariantCultureIgnoreCase) == true) + return BasePath.Substring(0, BasePath.Length - 3); + + return BasePath?.EndsWith(".gzip", StringComparison.InvariantCultureIgnoreCase) == true + ? BasePath.Substring(0, BasePath.Length - 5) : BasePath; + } + } + + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); } \ No newline at end of file diff --git a/Aaru.Filters/LZip.cs b/Aaru.Filters/LZip.cs index 80cf06ad3..1cb47db0a 100644 --- a/Aaru.Filters/LZip.cs +++ b/Aaru.Filters/LZip.cs @@ -37,158 +37,157 @@ using Aaru.CommonTypes.Interfaces; using SharpCompress.Compressors; using SharpCompress.Compressors.LZMA; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// Decompress lzip files while reading +public sealed class LZip : IFilter { + Stream _dataStream; + Stream _innerStream; + /// - /// Decompress lzip files while reading - public sealed class LZip : IFilter + public string Name => "LZip"; + /// + public Guid Id => new("09D715E9-20C0-48B1-A8D9-D8897CEC57C9"); + /// + public string Author => "Natalia Portillo"; + + /// + public void Close() { - Stream _dataStream; - Stream _innerStream; - - /// - public string Name => "LZip"; - /// - public Guid Id => new("09D715E9-20C0-48B1-A8D9-D8897CEC57C9"); - /// - public string Author => "Natalia Portillo"; - - /// - public void Close() - { - _dataStream?.Close(); - _dataStream = null; - BasePath = null; - } - - /// - public string BasePath { get; private set; } - - /// - public Stream GetDataForkStream() => _innerStream; - - /// - public string Path => BasePath; - - /// - public Stream GetResourceForkStream() => null; - - /// - public bool HasResourceFork => false; - - /// - public bool Identify(byte[] buffer) => buffer[0] == 0x4C && buffer[1] == 0x5A && buffer[2] == 0x49 && - buffer[3] == 0x50 && buffer[4] == 0x01; - - /// - public bool Identify(Stream stream) - { - byte[] buffer = new byte[5]; - - stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, 5); - stream.Seek(0, SeekOrigin.Begin); - - return buffer[0] == 0x4C && buffer[1] == 0x5A && buffer[2] == 0x49 && buffer[3] == 0x50 && - buffer[4] == 0x01; - } - - /// - public bool Identify(string path) - { - if(!File.Exists(path)) - return false; - - var stream = new FileStream(path, FileMode.Open, FileAccess.Read); - byte[] buffer = new byte[5]; - - stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, 5); - stream.Seek(0, SeekOrigin.Begin); - - return buffer[0] == 0x4C && buffer[1] == 0x5A && buffer[2] == 0x49 && buffer[3] == 0x50 && - buffer[4] == 0x01; - } - - /// - public ErrorNumber Open(byte[] buffer) - { - _dataStream = new MemoryStream(buffer); - BasePath = null; - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - DataForkLength = BitConverter.ToInt64(buffer, buffer.Length - 16); - - _innerStream = new ForcedSeekStream(DataForkLength, _dataStream, CompressionMode.Decompress); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(Stream stream) - { - _dataStream = stream; - BasePath = null; - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - byte[] tmp = new byte[8]; - _dataStream.Seek(-16, SeekOrigin.End); - _dataStream.Read(tmp, 0, 8); - DataForkLength = BitConverter.ToInt64(tmp, 0); - _dataStream.Seek(0, SeekOrigin.Begin); - _innerStream = new ForcedSeekStream(DataForkLength, _dataStream, CompressionMode.Decompress); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(string path) - { - _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); - BasePath = System.IO.Path.GetFullPath(path); - - var fi = new FileInfo(path); - CreationTime = fi.CreationTimeUtc; - LastWriteTime = fi.LastWriteTimeUtc; - byte[] tmp = new byte[8]; - _dataStream.Seek(-16, SeekOrigin.End); - _dataStream.Read(tmp, 0, 8); - DataForkLength = BitConverter.ToInt64(tmp, 0); - _dataStream.Seek(0, SeekOrigin.Begin); - _innerStream = new ForcedSeekStream(DataForkLength, _dataStream, CompressionMode.Decompress); - - return ErrorNumber.NoError; - } - - /// - public DateTime CreationTime { get; private set; } - - /// - public long DataForkLength { get; private set; } - - /// - public DateTime LastWriteTime { get; private set; } - - /// - public long Length => DataForkLength; - - /// - public long ResourceForkLength => 0; - - /// - public string Filename - { - get - { - if(BasePath?.EndsWith(".lz", StringComparison.InvariantCultureIgnoreCase) == true) - return BasePath.Substring(0, BasePath.Length - 3); - - return BasePath?.EndsWith(".lzip", StringComparison.InvariantCultureIgnoreCase) == true - ? BasePath.Substring(0, BasePath.Length - 5) : BasePath; - } - } - - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + _dataStream?.Close(); + _dataStream = null; + BasePath = null; } + + /// + public string BasePath { get; private set; } + + /// + public Stream GetDataForkStream() => _innerStream; + + /// + public string Path => BasePath; + + /// + public Stream GetResourceForkStream() => null; + + /// + public bool HasResourceFork => false; + + /// + public bool Identify(byte[] buffer) => buffer[0] == 0x4C && buffer[1] == 0x5A && buffer[2] == 0x49 && + buffer[3] == 0x50 && buffer[4] == 0x01; + + /// + public bool Identify(Stream stream) + { + byte[] buffer = new byte[5]; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 5); + stream.Seek(0, SeekOrigin.Begin); + + return buffer[0] == 0x4C && buffer[1] == 0x5A && buffer[2] == 0x49 && buffer[3] == 0x50 && + buffer[4] == 0x01; + } + + /// + public bool Identify(string path) + { + if(!File.Exists(path)) + return false; + + var stream = new FileStream(path, FileMode.Open, FileAccess.Read); + byte[] buffer = new byte[5]; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 5); + stream.Seek(0, SeekOrigin.Begin); + + return buffer[0] == 0x4C && buffer[1] == 0x5A && buffer[2] == 0x49 && buffer[3] == 0x50 && + buffer[4] == 0x01; + } + + /// + public ErrorNumber Open(byte[] buffer) + { + _dataStream = new MemoryStream(buffer); + BasePath = null; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + DataForkLength = BitConverter.ToInt64(buffer, buffer.Length - 16); + + _innerStream = new ForcedSeekStream(DataForkLength, _dataStream, CompressionMode.Decompress); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(Stream stream) + { + _dataStream = stream; + BasePath = null; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + byte[] tmp = new byte[8]; + _dataStream.Seek(-16, SeekOrigin.End); + _dataStream.Read(tmp, 0, 8); + DataForkLength = BitConverter.ToInt64(tmp, 0); + _dataStream.Seek(0, SeekOrigin.Begin); + _innerStream = new ForcedSeekStream(DataForkLength, _dataStream, CompressionMode.Decompress); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(string path) + { + _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); + BasePath = System.IO.Path.GetFullPath(path); + + var fi = new FileInfo(path); + CreationTime = fi.CreationTimeUtc; + LastWriteTime = fi.LastWriteTimeUtc; + byte[] tmp = new byte[8]; + _dataStream.Seek(-16, SeekOrigin.End); + _dataStream.Read(tmp, 0, 8); + DataForkLength = BitConverter.ToInt64(tmp, 0); + _dataStream.Seek(0, SeekOrigin.Begin); + _innerStream = new ForcedSeekStream(DataForkLength, _dataStream, CompressionMode.Decompress); + + return ErrorNumber.NoError; + } + + /// + public DateTime CreationTime { get; private set; } + + /// + public long DataForkLength { get; private set; } + + /// + public DateTime LastWriteTime { get; private set; } + + /// + public long Length => DataForkLength; + + /// + public long ResourceForkLength => 0; + + /// + public string Filename + { + get + { + if(BasePath?.EndsWith(".lz", StringComparison.InvariantCultureIgnoreCase) == true) + return BasePath.Substring(0, BasePath.Length - 3); + + return BasePath?.EndsWith(".lzip", StringComparison.InvariantCultureIgnoreCase) == true + ? BasePath.Substring(0, BasePath.Length - 5) : BasePath; + } + } + + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); } \ No newline at end of file diff --git a/Aaru.Filters/MacBinary.cs b/Aaru.Filters/MacBinary.cs index ff72ed9ad..b4ca54d3b 100644 --- a/Aaru.Filters/MacBinary.cs +++ b/Aaru.Filters/MacBinary.cs @@ -40,334 +40,333 @@ using Aaru.CommonTypes.Structs; using Aaru.Helpers; using Marshal = Aaru.Helpers.Marshal; -namespace Aaru.Filters +namespace Aaru.Filters; + +// TODO: Interpret fdScript +/// +/// Decodes MacBinary files +public sealed class MacBinary : IFilter { - // TODO: Interpret fdScript + const uint MAGIC = 0x6D42494E; + byte[] _bytes; + long _dataForkOff; + Header _header; + bool _isBytes, _isStream, _isPath; + long _rsrcForkOff; + Stream _stream; + /// - /// Decodes MacBinary files - public sealed class MacBinary : IFilter + public string Name => "MacBinary"; + /// + public Guid Id => new("D7C321D3-E51F-45DF-A150-F6BFDF0D7704"); + /// + public string Author => "Natalia Portillo"; + + /// + public void Close() { - const uint MAGIC = 0x6D42494E; - byte[] _bytes; - long _dataForkOff; - Header _header; - bool _isBytes, _isStream, _isPath; - long _rsrcForkOff; - Stream _stream; + _bytes = null; + _stream?.Close(); + _isBytes = false; + _isStream = false; + _isPath = false; + } - /// - public string Name => "MacBinary"; - /// - public Guid Id => new("D7C321D3-E51F-45DF-A150-F6BFDF0D7704"); - /// - public string Author => "Natalia Portillo"; + /// + public string BasePath { get; private set; } - /// - public void Close() - { - _bytes = null; - _stream?.Close(); - _isBytes = false; - _isStream = false; - _isPath = false; - } + /// + public DateTime CreationTime { get; private set; } - /// - public string BasePath { get; private set; } - - /// - public DateTime CreationTime { get; private set; } - - /// - public long DataForkLength => _header.dataLength; - - /// - public Stream GetDataForkStream() - { - if(_header.dataLength == 0) - return null; - - if(_isBytes) - return new OffsetStream(_bytes, _dataForkOff, _dataForkOff + _header.dataLength - 1); - - if(_isStream) - return new OffsetStream(_stream, _dataForkOff, _dataForkOff + _header.dataLength - 1); - - if(_isPath) - return new OffsetStream(BasePath, FileMode.Open, FileAccess.Read, _dataForkOff, - _dataForkOff + _header.dataLength - 1); + /// + public long DataForkLength => _header.dataLength; + /// + public Stream GetDataForkStream() + { + if(_header.dataLength == 0) return null; - } - /// - public string Filename { get; private set; } + if(_isBytes) + return new OffsetStream(_bytes, _dataForkOff, _dataForkOff + _header.dataLength - 1); - /// - public DateTime LastWriteTime { get; private set; } + if(_isStream) + return new OffsetStream(_stream, _dataForkOff, _dataForkOff + _header.dataLength - 1); - /// - public long Length => _header.dataLength + _header.resourceLength; + if(_isPath) + return new OffsetStream(BasePath, FileMode.Open, FileAccess.Read, _dataForkOff, + _dataForkOff + _header.dataLength - 1); - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + return null; + } - /// - public string Path => BasePath; + /// + public string Filename { get; private set; } - /// - public long ResourceForkLength => _header.resourceLength; + /// + public DateTime LastWriteTime { get; private set; } - /// - public Stream GetResourceForkStream() - { - if(_header.resourceLength == 0) - return null; + /// + public long Length => _header.dataLength + _header.resourceLength; - if(_isBytes) - return new OffsetStream(_bytes, _rsrcForkOff, _rsrcForkOff + _header.resourceLength - 1); + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); - if(_isStream) - return new OffsetStream(_stream, _rsrcForkOff, _rsrcForkOff + _header.resourceLength - 1); + /// + public string Path => BasePath; - if(_isPath) - return new OffsetStream(BasePath, FileMode.Open, FileAccess.Read, _rsrcForkOff, - _rsrcForkOff + _header.resourceLength - 1); + /// + public long ResourceForkLength => _header.resourceLength; + /// + public Stream GetResourceForkStream() + { + if(_header.resourceLength == 0) return null; - } - /// - public bool HasResourceFork => _header.resourceLength > 0; + if(_isBytes) + return new OffsetStream(_bytes, _rsrcForkOff, _rsrcForkOff + _header.resourceLength - 1); - /// - public bool Identify(byte[] buffer) - { - if(buffer == null || - buffer.Length < 128) - return false; + if(_isStream) + return new OffsetStream(_stream, _rsrcForkOff, _rsrcForkOff + _header.resourceLength - 1); - byte[] hdrB = new byte[128]; - Array.Copy(buffer, 0, hdrB, 0, 128); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + if(_isPath) + return new OffsetStream(BasePath, FileMode.Open, FileAccess.Read, _rsrcForkOff, + _rsrcForkOff + _header.resourceLength - 1); - return _header.magic == MAGIC || (_header.version == 0 && _header.filename[0] > 0 && - _header.filename[0] < 64 && _header.zero1 == 0 && _header.zero2 == 0 && - _header.reserved == 0 && - (_header.dataLength > 0 || _header.resourceLength > 0)); - } + return null; + } - /// - public bool Identify(Stream stream) - { - if(stream == null || - stream.Length < 128) - return false; + /// + public bool HasResourceFork => _header.resourceLength > 0; - byte[] hdrB = new byte[128]; - stream.Seek(0, SeekOrigin.Begin); - stream.Read(hdrB, 0, 128); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + /// + public bool Identify(byte[] buffer) + { + if(buffer == null || + buffer.Length < 128) + return false; - return _header.magic == MAGIC || (_header.version == 0 && _header.filename[0] > 0 && - _header.filename[0] < 64 && _header.zero1 == 0 && _header.zero2 == 0 && - _header.reserved == 0 && - (_header.dataLength > 0 || _header.resourceLength > 0)); - } + byte[] hdrB = new byte[128]; + Array.Copy(buffer, 0, hdrB, 0, 128); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - /// - public bool Identify(string path) - { - if(!File.Exists(path)) - return false; + return _header.magic == MAGIC || (_header.version == 0 && _header.filename[0] > 0 && + _header.filename[0] < 64 && _header.zero1 == 0 && _header.zero2 == 0 && + _header.reserved == 0 && + (_header.dataLength > 0 || _header.resourceLength > 0)); + } - var fstream = new FileStream(path, FileMode.Open, FileAccess.Read); + /// + public bool Identify(Stream stream) + { + if(stream == null || + stream.Length < 128) + return false; - if(fstream.Length < 128) - return false; + byte[] hdrB = new byte[128]; + stream.Seek(0, SeekOrigin.Begin); + stream.Read(hdrB, 0, 128); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - byte[] hdrB = new byte[128]; - fstream.Read(hdrB, 0, 128); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + return _header.magic == MAGIC || (_header.version == 0 && _header.filename[0] > 0 && + _header.filename[0] < 64 && _header.zero1 == 0 && _header.zero2 == 0 && + _header.reserved == 0 && + (_header.dataLength > 0 || _header.resourceLength > 0)); + } - fstream.Close(); + /// + public bool Identify(string path) + { + if(!File.Exists(path)) + return false; - return _header.magic == MAGIC || (_header.version == 0 && _header.filename[0] > 0 && - _header.filename[0] < 64 && _header.zero1 == 0 && _header.zero2 == 0 && - _header.reserved == 0 && - (_header.dataLength > 0 || _header.resourceLength > 0)); - } + var fstream = new FileStream(path, FileMode.Open, FileAccess.Read); - /// - public ErrorNumber Open(byte[] buffer) - { - var ms = new MemoryStream(buffer); - ms.Seek(0, SeekOrigin.Begin); + if(fstream.Length < 128) + return false; - byte[] hdrB = new byte[128]; - ms.Read(hdrB, 0, 128); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + byte[] hdrB = new byte[128]; + fstream.Read(hdrB, 0, 128); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - uint blocks = 1; - blocks += (uint)(_header.secondaryHeaderLength / 128); + fstream.Close(); - if(_header.secondaryHeaderLength % 128 > 0) - blocks++; + return _header.magic == MAGIC || (_header.version == 0 && _header.filename[0] > 0 && + _header.filename[0] < 64 && _header.zero1 == 0 && _header.zero2 == 0 && + _header.reserved == 0 && + (_header.dataLength > 0 || _header.resourceLength > 0)); + } - _dataForkOff = blocks * 128; - blocks += _header.dataLength / 128; + /// + public ErrorNumber Open(byte[] buffer) + { + var ms = new MemoryStream(buffer); + ms.Seek(0, SeekOrigin.Begin); - if(_header.dataLength % 128 > 0) - blocks++; + byte[] hdrB = new byte[128]; + ms.Read(hdrB, 0, 128); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - _rsrcForkOff = blocks * 128; + uint blocks = 1; + blocks += (uint)(_header.secondaryHeaderLength / 128); - Filename = StringHandlers.PascalToString(_header.filename, Encoding.GetEncoding("macintosh")); - CreationTime = DateHandlers.MacToDateTime(_header.creationTime); - LastWriteTime = DateHandlers.MacToDateTime(_header.modificationTime); + if(_header.secondaryHeaderLength % 128 > 0) + blocks++; - ms.Close(); - _isBytes = true; - _bytes = buffer; + _dataForkOff = blocks * 128; + blocks += _header.dataLength / 128; - return ErrorNumber.NoError; - } + if(_header.dataLength % 128 > 0) + blocks++; - /// - public ErrorNumber Open(Stream stream) - { - stream.Seek(0, SeekOrigin.Begin); + _rsrcForkOff = blocks * 128; - byte[] hdrB = new byte[128]; - stream.Read(hdrB, 0, 128); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + Filename = StringHandlers.PascalToString(_header.filename, Encoding.GetEncoding("macintosh")); + CreationTime = DateHandlers.MacToDateTime(_header.creationTime); + LastWriteTime = DateHandlers.MacToDateTime(_header.modificationTime); - uint blocks = 1; - blocks += (uint)(_header.secondaryHeaderLength / 128); + ms.Close(); + _isBytes = true; + _bytes = buffer; - if(_header.secondaryHeaderLength % 128 > 0) - blocks++; + return ErrorNumber.NoError; + } - _dataForkOff = blocks * 128; - blocks += _header.dataLength / 128; + /// + public ErrorNumber Open(Stream stream) + { + stream.Seek(0, SeekOrigin.Begin); - if(_header.dataLength % 128 > 0) - blocks++; + byte[] hdrB = new byte[128]; + stream.Read(hdrB, 0, 128); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - _rsrcForkOff = blocks * 128; + uint blocks = 1; + blocks += (uint)(_header.secondaryHeaderLength / 128); - Filename = StringHandlers.PascalToString(_header.filename, Encoding.GetEncoding("macintosh")); - CreationTime = DateHandlers.MacToDateTime(_header.creationTime); - LastWriteTime = DateHandlers.MacToDateTime(_header.modificationTime); + if(_header.secondaryHeaderLength % 128 > 0) + blocks++; - stream.Seek(0, SeekOrigin.Begin); - _isStream = true; - _stream = stream; + _dataForkOff = blocks * 128; + blocks += _header.dataLength / 128; - return ErrorNumber.NoError; - } + if(_header.dataLength % 128 > 0) + blocks++; - /// - public ErrorNumber Open(string path) - { - var fs = new FileStream(path, FileMode.Open, FileAccess.Read); - fs.Seek(0, SeekOrigin.Begin); + _rsrcForkOff = blocks * 128; - byte[] hdrB = new byte[128]; - fs.Read(hdrB, 0, 128); - _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); + Filename = StringHandlers.PascalToString(_header.filename, Encoding.GetEncoding("macintosh")); + CreationTime = DateHandlers.MacToDateTime(_header.creationTime); + LastWriteTime = DateHandlers.MacToDateTime(_header.modificationTime); - uint blocks = 1; - blocks += (uint)(_header.secondaryHeaderLength / 128); + stream.Seek(0, SeekOrigin.Begin); + _isStream = true; + _stream = stream; - if(_header.secondaryHeaderLength % 128 > 0) - blocks++; + return ErrorNumber.NoError; + } - _dataForkOff = blocks * 128; - blocks += _header.dataLength / 128; + /// + public ErrorNumber Open(string path) + { + var fs = new FileStream(path, FileMode.Open, FileAccess.Read); + fs.Seek(0, SeekOrigin.Begin); - if(_header.dataLength % 128 > 0) - blocks++; + byte[] hdrB = new byte[128]; + fs.Read(hdrB, 0, 128); + _header = Marshal.ByteArrayToStructureBigEndian
(hdrB); - _rsrcForkOff = blocks * 128; + uint blocks = 1; + blocks += (uint)(_header.secondaryHeaderLength / 128); - Filename = StringHandlers.PascalToString(_header.filename, Encoding.GetEncoding("macintosh")); - CreationTime = DateHandlers.MacToDateTime(_header.creationTime); - LastWriteTime = DateHandlers.MacToDateTime(_header.modificationTime); + if(_header.secondaryHeaderLength % 128 > 0) + blocks++; - fs.Close(); - _isPath = true; - BasePath = path; + _dataForkOff = blocks * 128; + blocks += _header.dataLength / 128; - return ErrorNumber.NoError; - } + if(_header.dataLength % 128 > 0) + blocks++; - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct Header - { - /// 0x00, MacBinary version, 0 - public readonly byte version; - /// 0x01, Str63 Pascal filename - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] - public readonly byte[] filename; - /// 0x41, File type - public readonly uint type; - /// 0x45, File creator - public readonly uint creator; - /// 0x49, High byte of Finder flags - public readonly byte finderFlags; - /// 0x4A, Must be 0 - public readonly byte zero1; - /// 0x4B, File's icon vertical position within its window - public readonly ushort verticalPosition; - /// 0x4D, File's icon horizontal position within its window - public readonly ushort horizontalPosition; - /// 0x4F, File's window or folder ID - public readonly short windowID; - /// 0x51, Protected flag - public readonly byte protect; - /// 0x52, Must be 0 - public readonly byte zero2; - /// 0x53, Size of data fork - public readonly uint dataLength; - /// 0x57, Size of resource fork - public readonly uint resourceLength; - /// 0x5B, File's creation time - public readonly uint creationTime; - /// 0x5F, File's last modified time - public readonly uint modificationTime; - /// 0x63, Length of Get Info comment - public readonly ushort commentLength; - /// 0x65, Low byte of Finder flags - public readonly byte finderFlags2; + _rsrcForkOff = blocks * 128; - #region MacBinary III - /// 0x66, magic identifier, "mBIN" - public readonly uint magic; - /// 0x6A, fdScript from fxInfo, identifies codepage of filename - public readonly byte fdScript; - /// 0x6B, fdXFlags from fxInfo, extended Mac OS 8 finder flags - public readonly byte fdXFlags; - #endregion MacBinary III + Filename = StringHandlers.PascalToString(_header.filename, Encoding.GetEncoding("macintosh")); + CreationTime = DateHandlers.MacToDateTime(_header.creationTime); + LastWriteTime = DateHandlers.MacToDateTime(_header.modificationTime); - /// 0x6C, unused - public readonly ulong reserved; - /// 0x74, Total unpacked files - public readonly uint totalPackedFiles; + fs.Close(); + _isPath = true; + BasePath = path; - #region MacBinary II - /// 0x78, Length of secondary header - public readonly ushort secondaryHeaderLength; - /// 0x7A, version number of MacBinary that wrote this file, starts at 129 - public readonly byte version2; - /// 0x7B, version number of MacBinary required to open this file, starts at 129 - public readonly byte minVersion; - /// 0x7C, CRC of previous bytes - public readonly short crc; - #endregion MacBinary II + return ErrorNumber.NoError; + } - /// 0x7E, Reserved for computer type and OS ID - public readonly short computerID; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct Header + { + /// 0x00, MacBinary version, 0 + public readonly byte version; + /// 0x01, Str63 Pascal filename + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)] + public readonly byte[] filename; + /// 0x41, File type + public readonly uint type; + /// 0x45, File creator + public readonly uint creator; + /// 0x49, High byte of Finder flags + public readonly byte finderFlags; + /// 0x4A, Must be 0 + public readonly byte zero1; + /// 0x4B, File's icon vertical position within its window + public readonly ushort verticalPosition; + /// 0x4D, File's icon horizontal position within its window + public readonly ushort horizontalPosition; + /// 0x4F, File's window or folder ID + public readonly short windowID; + /// 0x51, Protected flag + public readonly byte protect; + /// 0x52, Must be 0 + public readonly byte zero2; + /// 0x53, Size of data fork + public readonly uint dataLength; + /// 0x57, Size of resource fork + public readonly uint resourceLength; + /// 0x5B, File's creation time + public readonly uint creationTime; + /// 0x5F, File's last modified time + public readonly uint modificationTime; + /// 0x63, Length of Get Info comment + public readonly ushort commentLength; + /// 0x65, Low byte of Finder flags + public readonly byte finderFlags2; + + #region MacBinary III + /// 0x66, magic identifier, "mBIN" + public readonly uint magic; + /// 0x6A, fdScript from fxInfo, identifies codepage of filename + public readonly byte fdScript; + /// 0x6B, fdXFlags from fxInfo, extended Mac OS 8 finder flags + public readonly byte fdXFlags; + #endregion MacBinary III + + /// 0x6C, unused + public readonly ulong reserved; + /// 0x74, Total unpacked files + public readonly uint totalPackedFiles; + + #region MacBinary II + /// 0x78, Length of secondary header + public readonly ushort secondaryHeaderLength; + /// 0x7A, version number of MacBinary that wrote this file, starts at 129 + public readonly byte version2; + /// 0x7B, version number of MacBinary required to open this file, starts at 129 + public readonly byte minVersion; + /// 0x7C, CRC of previous bytes + public readonly short crc; + #endregion MacBinary II + + /// 0x7E, Reserved for computer type and OS ID + public readonly short computerID; } } \ No newline at end of file diff --git a/Aaru.Filters/PCExchange.cs b/Aaru.Filters/PCExchange.cs index 99ad98c62..e051cb428 100644 --- a/Aaru.Filters/PCExchange.cs +++ b/Aaru.Filters/PCExchange.cs @@ -42,242 +42,241 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; using Marshal = System.Runtime.InteropServices.Marshal; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// Decodes PCExchange files +[SuppressMessage("ReSharper", "UnusedMember.Local")] +public sealed class PcExchange : IFilter { + const string FILE_ID = "FILEID.DAT"; + const string FINDER_INFO = "FINDER.DAT"; + const string RESOURCES = "RESOURCE.FRK"; + string _dataPath; + string _rsrcPath; + /// - /// Decodes PCExchange files - [SuppressMessage("ReSharper", "UnusedMember.Local")] - public sealed class PcExchange : IFilter + public string Name => "PCExchange"; + /// + public Guid Id => new("9264EB9F-D634-4F9B-BE12-C24CD44988C6"); + /// + public string Author => "Natalia Portillo"; + + /// + public void Close() {} + + /// + public string BasePath { get; private set; } + + /// + public DateTime CreationTime { get; private set; } + + /// + public long DataForkLength { get; private set; } + + /// + public Stream GetDataForkStream() => new FileStream(_dataPath, FileMode.Open, FileAccess.Read); + + /// + public string Filename => System.IO.Path.GetFileName(BasePath); + + /// + public DateTime LastWriteTime { get; private set; } + + /// + public long Length => DataForkLength + ResourceForkLength; + + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + + /// + public string Path => BasePath; + + /// + public long ResourceForkLength { get; private set; } + + /// + public Stream GetResourceForkStream() => new FileStream(_rsrcPath, FileMode.Open, FileAccess.Read); + + /// + public bool HasResourceFork => _rsrcPath != null; + + /// + public bool Identify(byte[] buffer) => false; + + /// + public bool Identify(Stream stream) => false; + + /// + public bool Identify(string path) { - const string FILE_ID = "FILEID.DAT"; - const string FINDER_INFO = "FINDER.DAT"; - const string RESOURCES = "RESOURCE.FRK"; - string _dataPath; - string _rsrcPath; + string parentFolder = System.IO.Path.GetDirectoryName(path); - /// - public string Name => "PCExchange"; - /// - public Guid Id => new("9264EB9F-D634-4F9B-BE12-C24CD44988C6"); - /// - public string Author => "Natalia Portillo"; + parentFolder ??= ""; - /// - public void Close() {} + if(!File.Exists(System.IO.Path.Combine(parentFolder, FINDER_INFO))) + return false; - /// - public string BasePath { get; private set; } + if(!Directory.Exists(System.IO.Path.Combine(parentFolder, RESOURCES))) + return false; - /// - public DateTime CreationTime { get; private set; } + string baseFilename = System.IO.Path.GetFileName(path); - /// - public long DataForkLength { get; private set; } + bool dataFound = false; + bool rsrcFound = false; - /// - public Stream GetDataForkStream() => new FileStream(_dataPath, FileMode.Open, FileAccess.Read); + var finderDatStream = new FileStream(System.IO.Path.Combine(parentFolder, FINDER_INFO), FileMode.Open, + FileAccess.Read); - /// - public string Filename => System.IO.Path.GetFileName(BasePath); - - /// - public DateTime LastWriteTime { get; private set; } - - /// - public long Length => DataForkLength + ResourceForkLength; - - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); - - /// - public string Path => BasePath; - - /// - public long ResourceForkLength { get; private set; } - - /// - public Stream GetResourceForkStream() => new FileStream(_rsrcPath, FileMode.Open, FileAccess.Read); - - /// - public bool HasResourceFork => _rsrcPath != null; - - /// - public bool Identify(byte[] buffer) => false; - - /// - public bool Identify(Stream stream) => false; - - /// - public bool Identify(string path) + while(finderDatStream.Position + 0x5C <= finderDatStream.Length) { - string parentFolder = System.IO.Path.GetDirectoryName(path); + var datEntry = new Entry(); + byte[] datEntryB = new byte[Marshal.SizeOf(datEntry)]; + finderDatStream.Read(datEntryB, 0, Marshal.SizeOf(datEntry)); + datEntry = Helpers.Marshal.ByteArrayToStructureBigEndian(datEntryB); - parentFolder ??= ""; + // TODO: Add support for encoding on filters + string macName = StringHandlers.PascalToString(datEntry.macName, Encoding.GetEncoding("macintosh")); - if(!File.Exists(System.IO.Path.Combine(parentFolder, FINDER_INFO))) - return false; + byte[] tmpDosNameB = new byte[8]; + byte[] tmpDosExtB = new byte[3]; + Array.Copy(datEntry.dosName, 0, tmpDosNameB, 0, 8); + Array.Copy(datEntry.dosName, 8, tmpDosExtB, 0, 3); - if(!Directory.Exists(System.IO.Path.Combine(parentFolder, RESOURCES))) - return false; + string dosName = Encoding.ASCII.GetString(tmpDosNameB).Trim() + "." + + Encoding.ASCII.GetString(tmpDosExtB).Trim(); - string baseFilename = System.IO.Path.GetFileName(path); + string dosNameLow = dosName.ToLower(CultureInfo.CurrentCulture); - bool dataFound = false; - bool rsrcFound = false; + if(baseFilename != macName && + baseFilename != dosName && + baseFilename != dosNameLow) + continue; - var finderDatStream = new FileStream(System.IO.Path.Combine(parentFolder, FINDER_INFO), FileMode.Open, - FileAccess.Read); + dataFound |= + File.Exists(System.IO.Path.Combine(parentFolder, + macName ?? throw new InvalidOperationException())) || + File.Exists(System.IO.Path.Combine(parentFolder, dosName)) || + File.Exists(System.IO.Path.Combine(parentFolder, dosNameLow)); - while(finderDatStream.Position + 0x5C <= finderDatStream.Length) - { - var datEntry = new Entry(); - byte[] datEntryB = new byte[Marshal.SizeOf(datEntry)]; - finderDatStream.Read(datEntryB, 0, Marshal.SizeOf(datEntry)); - datEntry = Helpers.Marshal.ByteArrayToStructureBigEndian(datEntryB); + rsrcFound |= File.Exists(System.IO.Path.Combine(parentFolder, RESOURCES, dosName)) || + File.Exists(System.IO.Path.Combine(parentFolder, RESOURCES, dosNameLow)); - // TODO: Add support for encoding on filters - string macName = StringHandlers.PascalToString(datEntry.macName, Encoding.GetEncoding("macintosh")); - - byte[] tmpDosNameB = new byte[8]; - byte[] tmpDosExtB = new byte[3]; - Array.Copy(datEntry.dosName, 0, tmpDosNameB, 0, 8); - Array.Copy(datEntry.dosName, 8, tmpDosExtB, 0, 3); - - string dosName = Encoding.ASCII.GetString(tmpDosNameB).Trim() + "." + - Encoding.ASCII.GetString(tmpDosExtB).Trim(); - - string dosNameLow = dosName.ToLower(CultureInfo.CurrentCulture); - - if(baseFilename != macName && - baseFilename != dosName && - baseFilename != dosNameLow) - continue; - - dataFound |= - File.Exists(System.IO.Path.Combine(parentFolder, - macName ?? throw new InvalidOperationException())) || - File.Exists(System.IO.Path.Combine(parentFolder, dosName)) || - File.Exists(System.IO.Path.Combine(parentFolder, dosNameLow)); - - rsrcFound |= File.Exists(System.IO.Path.Combine(parentFolder, RESOURCES, dosName)) || - File.Exists(System.IO.Path.Combine(parentFolder, RESOURCES, dosNameLow)); - - break; - } - - finderDatStream.Close(); - - return dataFound && rsrcFound; + break; } - /// - public ErrorNumber Open(byte[] buffer) => ErrorNumber.NotSupported; + finderDatStream.Close(); - /// - public ErrorNumber Open(Stream stream) => ErrorNumber.NotSupported; + return dataFound && rsrcFound; + } - /// - public ErrorNumber Open(string path) + /// + public ErrorNumber Open(byte[] buffer) => ErrorNumber.NotSupported; + + /// + public ErrorNumber Open(Stream stream) => ErrorNumber.NotSupported; + + /// + public ErrorNumber Open(string path) + { + string parentFolder = System.IO.Path.GetDirectoryName(path); + string baseFilename = System.IO.Path.GetFileName(path); + + parentFolder ??= ""; + + var finderDatStream = new FileStream(System.IO.Path.Combine(parentFolder, FINDER_INFO), FileMode.Open, + FileAccess.Read); + + while(finderDatStream.Position + 0x5C <= finderDatStream.Length) { - string parentFolder = System.IO.Path.GetDirectoryName(path); - string baseFilename = System.IO.Path.GetFileName(path); + var datEntry = new Entry(); + byte[] datEntryB = new byte[Marshal.SizeOf(datEntry)]; + finderDatStream.Read(datEntryB, 0, Marshal.SizeOf(datEntry)); + datEntry = Helpers.Marshal.ByteArrayToStructureBigEndian(datEntryB); - parentFolder ??= ""; + string macName = StringHandlers.PascalToString(datEntry.macName, Encoding.GetEncoding("macintosh")); - var finderDatStream = new FileStream(System.IO.Path.Combine(parentFolder, FINDER_INFO), FileMode.Open, - FileAccess.Read); + byte[] tmpDosNameB = new byte[8]; + byte[] tmpDosExtB = new byte[3]; + Array.Copy(datEntry.dosName, 0, tmpDosNameB, 0, 8); + Array.Copy(datEntry.dosName, 8, tmpDosExtB, 0, 3); - while(finderDatStream.Position + 0x5C <= finderDatStream.Length) - { - var datEntry = new Entry(); - byte[] datEntryB = new byte[Marshal.SizeOf(datEntry)]; - finderDatStream.Read(datEntryB, 0, Marshal.SizeOf(datEntry)); - datEntry = Helpers.Marshal.ByteArrayToStructureBigEndian(datEntryB); + string dosName = Encoding.ASCII.GetString(tmpDosNameB).Trim() + "." + + Encoding.ASCII.GetString(tmpDosExtB).Trim(); - string macName = StringHandlers.PascalToString(datEntry.macName, Encoding.GetEncoding("macintosh")); + string dosNameLow = dosName.ToLower(CultureInfo.CurrentCulture); - byte[] tmpDosNameB = new byte[8]; - byte[] tmpDosExtB = new byte[3]; - Array.Copy(datEntry.dosName, 0, tmpDosNameB, 0, 8); - Array.Copy(datEntry.dosName, 8, tmpDosExtB, 0, 3); + if(baseFilename != macName && + baseFilename != dosName && + baseFilename != dosNameLow) + continue; - string dosName = Encoding.ASCII.GetString(tmpDosNameB).Trim() + "." + - Encoding.ASCII.GetString(tmpDosExtB).Trim(); + if(File.Exists(System.IO.Path.Combine(parentFolder, macName ?? throw new InvalidOperationException()))) + _dataPath = System.IO.Path.Combine(parentFolder, macName); + else if(File.Exists(System.IO.Path.Combine(parentFolder, dosName))) + _dataPath = System.IO.Path.Combine(parentFolder, dosName); + else if(File.Exists(System.IO.Path.Combine(parentFolder, dosNameLow))) + _dataPath = System.IO.Path.Combine(parentFolder, dosNameLow); + else + _dataPath = null; - string dosNameLow = dosName.ToLower(CultureInfo.CurrentCulture); + if(File.Exists(System.IO.Path.Combine(parentFolder, RESOURCES, dosName))) + _rsrcPath = System.IO.Path.Combine(parentFolder, RESOURCES, dosName); + else if(File.Exists(System.IO.Path.Combine(parentFolder, RESOURCES, dosNameLow))) + _rsrcPath = System.IO.Path.Combine(parentFolder, RESOURCES, dosNameLow); + else + _rsrcPath = null; - if(baseFilename != macName && - baseFilename != dosName && - baseFilename != dosNameLow) - continue; + LastWriteTime = DateHandlers.MacToDateTime(datEntry.modificationDate); + CreationTime = DateHandlers.MacToDateTime(datEntry.creationDate); - if(File.Exists(System.IO.Path.Combine(parentFolder, macName ?? throw new InvalidOperationException()))) - _dataPath = System.IO.Path.Combine(parentFolder, macName); - else if(File.Exists(System.IO.Path.Combine(parentFolder, dosName))) - _dataPath = System.IO.Path.Combine(parentFolder, dosName); - else if(File.Exists(System.IO.Path.Combine(parentFolder, dosNameLow))) - _dataPath = System.IO.Path.Combine(parentFolder, dosNameLow); - else - _dataPath = null; - - if(File.Exists(System.IO.Path.Combine(parentFolder, RESOURCES, dosName))) - _rsrcPath = System.IO.Path.Combine(parentFolder, RESOURCES, dosName); - else if(File.Exists(System.IO.Path.Combine(parentFolder, RESOURCES, dosNameLow))) - _rsrcPath = System.IO.Path.Combine(parentFolder, RESOURCES, dosNameLow); - else - _rsrcPath = null; - - LastWriteTime = DateHandlers.MacToDateTime(datEntry.modificationDate); - CreationTime = DateHandlers.MacToDateTime(datEntry.creationDate); - - break; - } - - DataForkLength = new FileInfo(_dataPath ?? throw new InvalidOperationException()).Length; - ResourceForkLength = new FileInfo(_rsrcPath ?? throw new InvalidOperationException()).Length; - - BasePath = path; - - finderDatStream.Close(); - - return ErrorNumber.NoError; + break; } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - readonly struct Entry - { - /// - /// Name in Macintosh. If PCExchange version supports FAT's LFN they are the same. Illegal characters for FAT get - /// substituted with '_' both here and in FAT's LFN entry. - /// - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] - public readonly byte[] macName; - /// File type - public readonly uint type; - /// File creator - public readonly uint creator; - /// Finder flags - public readonly ushort fdFlags; - /// File's icon vertical position within its window - public readonly ushort verticalPosition; - /// File's icon horizontal position within its window - public readonly ushort horizontalPosition; - /// Unknown, all bytes are empty but last, except in volume's label entry - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)] - public readonly byte[] unknown1; - /// File's creation date - public readonly uint creationDate; - /// File's modification date - public readonly uint modificationDate; - /// File's last backup date - public readonly uint backupDate; - /// Unknown, but is unique, starts 0x7FFFFFFF and counts in reverse. Probably file ID for alias look up? - public readonly uint unknown2; - /// Name as in FAT entry (not LFN). Resource fork file is always using this name, never LFN. - [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] - public readonly byte[] dosName; - /// Unknown, flags? - public readonly byte unknown3; - } + DataForkLength = new FileInfo(_dataPath ?? throw new InvalidOperationException()).Length; + ResourceForkLength = new FileInfo(_rsrcPath ?? throw new InvalidOperationException()).Length; + + BasePath = path; + + finderDatStream.Close(); + + return ErrorNumber.NoError; + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + readonly struct Entry + { + /// + /// Name in Macintosh. If PCExchange version supports FAT's LFN they are the same. Illegal characters for FAT get + /// substituted with '_' both here and in FAT's LFN entry. + /// + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] + public readonly byte[] macName; + /// File type + public readonly uint type; + /// File creator + public readonly uint creator; + /// Finder flags + public readonly ushort fdFlags; + /// File's icon vertical position within its window + public readonly ushort verticalPosition; + /// File's icon horizontal position within its window + public readonly ushort horizontalPosition; + /// Unknown, all bytes are empty but last, except in volume's label entry + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)] + public readonly byte[] unknown1; + /// File's creation date + public readonly uint creationDate; + /// File's modification date + public readonly uint modificationDate; + /// File's last backup date + public readonly uint backupDate; + /// Unknown, but is unique, starts 0x7FFFFFFF and counts in reverse. Probably file ID for alias look up? + public readonly uint unknown2; + /// Name as in FAT entry (not LFN). Resource fork file is always using this name, never LFN. + [MarshalAs(UnmanagedType.ByValArray, SizeConst = 11)] + public readonly byte[] dosName; + /// Unknown, flags? + public readonly byte unknown3; } } \ No newline at end of file diff --git a/Aaru.Filters/SplitJoinStream.cs b/Aaru.Filters/SplitJoinStream.cs index b6d1bf5ce..89718e4d3 100644 --- a/Aaru.Filters/SplitJoinStream.cs +++ b/Aaru.Filters/SplitJoinStream.cs @@ -6,371 +6,370 @@ using System.Linq; using Aaru.CommonTypes.Interfaces; using Microsoft.Win32.SafeHandles; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// Implements a stream that joins two or more files (sequentially) as a single stream +public class SplitJoinStream : Stream { + readonly Dictionary _baseStreams; + long _position; + long _streamLength; + /// - /// Implements a stream that joins two or more files (sequentially) as a single stream - public class SplitJoinStream : Stream + public SplitJoinStream() { - readonly Dictionary _baseStreams; - long _position; - long _streamLength; + _baseStreams = new Dictionary(); + _streamLength = 0; + _position = 0; + Filter = new ZZZNoFilter(); + Filter.Open(this); + } - /// - public SplitJoinStream() + /// + public override bool CanRead => true; + + /// + public override bool CanSeek => true; + + /// + public override bool CanWrite => false; + + /// + public override long Length => _streamLength; + + /// + public override long Position + { + get => _position; + + set { - _baseStreams = new Dictionary(); - _streamLength = 0; - _position = 0; - Filter = new ZZZNoFilter(); - Filter.Open(this); + if(value >= _streamLength) + throw new IOException("Cannot set position past stream end."); + + _position = value; + } + } + + /// Gets a filter from this stream + public IFilter Filter { get; } + + /// Adds a stream at the end of the current stream + /// Stream to add + /// The specified stream is non-readable or non-seekable + public void Add(Stream stream) + { + if(!stream.CanSeek) + throw new ArgumentException("Non-seekable streams are not supported"); + + if(!stream.CanRead) + throw new ArgumentException("Non-readable streams are not supported"); + + _baseStreams[_streamLength] = stream; + _streamLength += stream.Length; + } + + /// Adds the specified file to the end of the current stream + /// A relative or absolute path for the file that the stream will encapsulate. + /// One of the enumeration values that determines how to open or create the file. + /// + /// A bitwise combination of the enumeration values that determines how the file can be accessed by a + /// object. + /// + /// + /// A bitwise combination of the enumeration values that determines how the file will be shared by + /// processes. + /// + /// + /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is + /// 4096. + /// + /// A bitwise combination of the enumeration values that specifies additional file options. + public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, + FileOptions options) => Add(new FileStream(path, mode, access, share, bufferSize, options)); + + /// Adds the specified file to the end of the current stream + /// A file handle for the file that the stream will encapsulate. + /// + /// A bitwise combination of the enumeration values that determines how the file can be accessed by a + /// object. + /// + public void Add(SafeFileHandle handle, FileAccess access) => Add(new FileStream(handle, access)); + + /// Adds the specified file to the end of the current stream + /// A file handle for the file that the stream will encapsulate. + /// + /// A bitwise combination of the enumeration values that determines how the file can be accessed by a + /// object. + /// + /// + /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is + /// 4096. + /// + public void Add(SafeFileHandle handle, FileAccess access, int bufferSize) => + Add(new FileStream(handle, access, bufferSize)); + + /// Adds the specified file to the end of the current stream + /// A file handle for the file that the stream will encapsulate. + /// + /// A bitwise combination of the enumeration values that determines how the file can be accessed by a + /// object. + /// + /// + /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is + /// 4096. + /// + /// Specifies whether to use asynchronous I/O or synchronous I/O. + public void Add(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) => + Add(new FileStream(handle, access, bufferSize, isAsync)); + + /// Adds the specified file to the end of the current stream + /// A relative or absolute path for the file that the stream will encapsulate. + /// One of the enumeration values that determines how to open or create the file. + /// + /// A bitwise combination of the enumeration values that determines how the file can be accessed by a + /// object. + /// + /// + /// A bitwise combination of the enumeration values that determines how the file will be shared by + /// processes. + /// + /// + /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is + /// 4096. + /// + /// Specifies whether to use asynchronous I/O or synchronous I/O. + public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, + bool useAsync) => Add(new FileStream(path, mode, access, share, bufferSize, useAsync)); + + /// Adds the specified file to the end of the current stream + /// A relative or absolute path for the file that the stream will encapsulate. + /// One of the enumeration values that determines how to open or create the file. + /// + /// A bitwise combination of the enumeration values that determines how the file can be accessed by a + /// object. + /// + /// + /// A bitwise combination of the enumeration values that determines how the file will be shared by + /// processes. + /// + /// + /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is + /// 4096. + /// + public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) => + Add(new FileStream(path, mode, access, share, bufferSize)); + + /// Adds the specified file to the end of the current stream + /// A relative or absolute path for the file that the stream will encapsulate. + /// One of the enumeration values that determines how to open or create the file. + /// + /// A bitwise combination of the enumeration values that determines how the file can be accessed by a + /// object. + /// + /// + /// A bitwise combination of the enumeration values that determines how the file will be shared by + /// processes. + /// + public void Add(string path, FileMode mode, FileAccess access, FileShare share) => + Add(new FileStream(path, mode, access, share)); + + /// Adds the specified file to the end of the current stream + /// A relative or absolute path for the file that the stream will encapsulate. + /// One of the enumeration values that determines how to open or create the file. + /// + /// A bitwise combination of the enumeration values that determines how the file can be accessed by a + /// object. + /// + public void Add(string path, FileMode mode, FileAccess access) => Add(new FileStream(path, mode, access)); + + /// Adds the specified file to the end of the current stream + /// A relative or absolute path for the file that the stream will encapsulate. + /// One of the enumeration values that determines how to open or create the file. + public void Add(string path, FileMode mode) => Add(new FileStream(path, mode)); + + /// Adds the specified byte array to the end of the current stream + /// The array of unsigned bytes to add at the end of this stream. + /// The index into at which the stream begins. + /// The length in bytes to add to the end of the current stream. + /// The setting of the CanWrite property, currently ignored. + /// Currently ignored. + public void Add(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) => + Add(new MemoryStream(buffer, index, count, writable, publiclyVisible)); + + /// Adds the specified byte array to the end of the current stream + /// The array of unsigned bytes to add at the end of this stream. + /// The index into at which the stream begins. + /// The length in bytes to add to the end of the current stream. + /// The setting of the CanWrite property, currently ignored. + public void Add(byte[] buffer, int index, int count, bool writable) => + Add(new MemoryStream(buffer, index, count, writable)); + + /// Adds the specified byte array to the end of the current stream + /// The array of unsigned bytes to add at the end of this stream. + /// The index into at which the stream begins. + /// The length in bytes to add to the end of the current stream. + public void Add(byte[] buffer, int index, int count) => Add(new MemoryStream(buffer, index, count)); + + /// Adds the specified byte array to the end of the current stream + /// The array of unsigned bytes to add at the end of this stream. + /// The setting of the CanWrite property, currently ignored. + public void Add(byte[] buffer, bool writable) => Add(new MemoryStream(buffer, writable)); + + /// Adds the specified byte array to the end of the current stream + /// The array of unsigned bytes to add at the end of this stream. + public void Add(byte[] buffer) => Add(new MemoryStream(buffer)); + + /// Adds the data fork of the specified filter to the end of the current stream + /// Filter + public void Add(IFilter filter) => Add(filter.GetDataForkStream()); + + /// Adds a range of files to the end of the current stream, alphabetically sorted + /// Base file path, directory path only + /// Counter format, includes filename and a formatting string + /// Counter start, defaults to 0 + public void AddRange(string basePath, string counterFormat = "{0:D3}", int counterStart = 0) + { + while(true) + { + string filePath = Path.Combine(basePath, string.Format(counterFormat, counterStart)); + + if(!File.Exists(filePath)) + break; + + Add(filePath, FileMode.Open); + + counterStart++; + } + } + + ~SplitJoinStream() + { + foreach(Stream stream in _baseStreams.Values) + { + stream.Close(); + stream.Dispose(); } - /// - public override bool CanRead => true; + _baseStreams.Clear(); + _position = 0; + } - /// - public override bool CanSeek => true; + /// + public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, + object state) => + throw new NotSupportedException("Asynchronous I/O is not supported."); - /// - public override bool CanWrite => false; + /// + public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, + object state) => + throw new NotSupportedException("Asynchronous I/O is not supported."); - /// - public override long Length => _streamLength; + /// + public override void Close() + { + foreach(Stream stream in _baseStreams.Values) + stream.Close(); - /// - public override long Position + _baseStreams.Clear(); + _position = 0; + } + + /// + public override int EndRead(IAsyncResult asyncResult) => + throw new NotSupportedException("Asynchronous I/O is not supported."); + + /// + public override void EndWrite(IAsyncResult asyncResult) => + throw new NotSupportedException("Asynchronous I/O is not supported."); + + /// + public override int ReadByte() + { + if(_position >= _streamLength) + return -1; + + KeyValuePair baseStream = _baseStreams.FirstOrDefault(s => s.Key >= _position); + + if(baseStream.Value == null) + return -1; + + baseStream.Value.Position = _position - baseStream.Key; + _position++; + + return baseStream.Value.ReadByte(); + } + + /// + public override void WriteByte(byte value) => throw new ReadOnlyException("This stream is read-only"); + + /// + public override void Flush() {} + + /// + public override int Read(byte[] buffer, int offset, int count) + { + int read = 0; + + while(count > 0) { - get => _position; - - set - { - if(value >= _streamLength) - throw new IOException("Cannot set position past stream end."); - - _position = value; - } - } - - /// Gets a filter from this stream - public IFilter Filter { get; } - - /// Adds a stream at the end of the current stream - /// Stream to add - /// The specified stream is non-readable or non-seekable - public void Add(Stream stream) - { - if(!stream.CanSeek) - throw new ArgumentException("Non-seekable streams are not supported"); - - if(!stream.CanRead) - throw new ArgumentException("Non-readable streams are not supported"); - - _baseStreams[_streamLength] = stream; - _streamLength += stream.Length; - } - - /// Adds the specified file to the end of the current stream - /// A relative or absolute path for the file that the stream will encapsulate. - /// One of the enumeration values that determines how to open or create the file. - /// - /// A bitwise combination of the enumeration values that determines how the file can be accessed by a - /// object. - /// - /// - /// A bitwise combination of the enumeration values that determines how the file will be shared by - /// processes. - /// - /// - /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is - /// 4096. - /// - /// A bitwise combination of the enumeration values that specifies additional file options. - public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, - FileOptions options) => Add(new FileStream(path, mode, access, share, bufferSize, options)); - - /// Adds the specified file to the end of the current stream - /// A file handle for the file that the stream will encapsulate. - /// - /// A bitwise combination of the enumeration values that determines how the file can be accessed by a - /// object. - /// - public void Add(SafeFileHandle handle, FileAccess access) => Add(new FileStream(handle, access)); - - /// Adds the specified file to the end of the current stream - /// A file handle for the file that the stream will encapsulate. - /// - /// A bitwise combination of the enumeration values that determines how the file can be accessed by a - /// object. - /// - /// - /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is - /// 4096. - /// - public void Add(SafeFileHandle handle, FileAccess access, int bufferSize) => - Add(new FileStream(handle, access, bufferSize)); - - /// Adds the specified file to the end of the current stream - /// A file handle for the file that the stream will encapsulate. - /// - /// A bitwise combination of the enumeration values that determines how the file can be accessed by a - /// object. - /// - /// - /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is - /// 4096. - /// - /// Specifies whether to use asynchronous I/O or synchronous I/O. - public void Add(SafeFileHandle handle, FileAccess access, int bufferSize, bool isAsync) => - Add(new FileStream(handle, access, bufferSize, isAsync)); - - /// Adds the specified file to the end of the current stream - /// A relative or absolute path for the file that the stream will encapsulate. - /// One of the enumeration values that determines how to open or create the file. - /// - /// A bitwise combination of the enumeration values that determines how the file can be accessed by a - /// object. - /// - /// - /// A bitwise combination of the enumeration values that determines how the file will be shared by - /// processes. - /// - /// - /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is - /// 4096. - /// - /// Specifies whether to use asynchronous I/O or synchronous I/O. - public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize, - bool useAsync) => Add(new FileStream(path, mode, access, share, bufferSize, useAsync)); - - /// Adds the specified file to the end of the current stream - /// A relative or absolute path for the file that the stream will encapsulate. - /// One of the enumeration values that determines how to open or create the file. - /// - /// A bitwise combination of the enumeration values that determines how the file can be accessed by a - /// object. - /// - /// - /// A bitwise combination of the enumeration values that determines how the file will be shared by - /// processes. - /// - /// - /// A positive Int32 value greater than 0 indicating the buffer size. The default buffer size is - /// 4096. - /// - public void Add(string path, FileMode mode, FileAccess access, FileShare share, int bufferSize) => - Add(new FileStream(path, mode, access, share, bufferSize)); - - /// Adds the specified file to the end of the current stream - /// A relative or absolute path for the file that the stream will encapsulate. - /// One of the enumeration values that determines how to open or create the file. - /// - /// A bitwise combination of the enumeration values that determines how the file can be accessed by a - /// object. - /// - /// - /// A bitwise combination of the enumeration values that determines how the file will be shared by - /// processes. - /// - public void Add(string path, FileMode mode, FileAccess access, FileShare share) => - Add(new FileStream(path, mode, access, share)); - - /// Adds the specified file to the end of the current stream - /// A relative or absolute path for the file that the stream will encapsulate. - /// One of the enumeration values that determines how to open or create the file. - /// - /// A bitwise combination of the enumeration values that determines how the file can be accessed by a - /// object. - /// - public void Add(string path, FileMode mode, FileAccess access) => Add(new FileStream(path, mode, access)); - - /// Adds the specified file to the end of the current stream - /// A relative or absolute path for the file that the stream will encapsulate. - /// One of the enumeration values that determines how to open or create the file. - public void Add(string path, FileMode mode) => Add(new FileStream(path, mode)); - - /// Adds the specified byte array to the end of the current stream - /// The array of unsigned bytes to add at the end of this stream. - /// The index into at which the stream begins. - /// The length in bytes to add to the end of the current stream. - /// The setting of the CanWrite property, currently ignored. - /// Currently ignored. - public void Add(byte[] buffer, int index, int count, bool writable, bool publiclyVisible) => - Add(new MemoryStream(buffer, index, count, writable, publiclyVisible)); - - /// Adds the specified byte array to the end of the current stream - /// The array of unsigned bytes to add at the end of this stream. - /// The index into at which the stream begins. - /// The length in bytes to add to the end of the current stream. - /// The setting of the CanWrite property, currently ignored. - public void Add(byte[] buffer, int index, int count, bool writable) => - Add(new MemoryStream(buffer, index, count, writable)); - - /// Adds the specified byte array to the end of the current stream - /// The array of unsigned bytes to add at the end of this stream. - /// The index into at which the stream begins. - /// The length in bytes to add to the end of the current stream. - public void Add(byte[] buffer, int index, int count) => Add(new MemoryStream(buffer, index, count)); - - /// Adds the specified byte array to the end of the current stream - /// The array of unsigned bytes to add at the end of this stream. - /// The setting of the CanWrite property, currently ignored. - public void Add(byte[] buffer, bool writable) => Add(new MemoryStream(buffer, writable)); - - /// Adds the specified byte array to the end of the current stream - /// The array of unsigned bytes to add at the end of this stream. - public void Add(byte[] buffer) => Add(new MemoryStream(buffer)); - - /// Adds the data fork of the specified filter to the end of the current stream - /// Filter - public void Add(IFilter filter) => Add(filter.GetDataForkStream()); - - /// Adds a range of files to the end of the current stream, alphabetically sorted - /// Base file path, directory path only - /// Counter format, includes filename and a formatting string - /// Counter start, defaults to 0 - public void AddRange(string basePath, string counterFormat = "{0:D3}", int counterStart = 0) - { - while(true) - { - string filePath = Path.Combine(basePath, string.Format(counterFormat, counterStart)); - - if(!File.Exists(filePath)) - break; - - Add(filePath, FileMode.Open); - - counterStart++; - } - } - - ~SplitJoinStream() - { - foreach(Stream stream in _baseStreams.Values) - { - stream.Close(); - stream.Dispose(); - } - - _baseStreams.Clear(); - _position = 0; - } - - /// - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, - object state) => - throw new NotSupportedException("Asynchronous I/O is not supported."); - - /// - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, - object state) => - throw new NotSupportedException("Asynchronous I/O is not supported."); - - /// - public override void Close() - { - foreach(Stream stream in _baseStreams.Values) - stream.Close(); - - _baseStreams.Clear(); - _position = 0; - } - - /// - public override int EndRead(IAsyncResult asyncResult) => - throw new NotSupportedException("Asynchronous I/O is not supported."); - - /// - public override void EndWrite(IAsyncResult asyncResult) => - throw new NotSupportedException("Asynchronous I/O is not supported."); - - /// - public override int ReadByte() - { - if(_position >= _streamLength) - return -1; - - KeyValuePair baseStream = _baseStreams.FirstOrDefault(s => s.Key >= _position); + KeyValuePair baseStream = _baseStreams.LastOrDefault(s => s.Key <= _position); if(baseStream.Value == null) - return -1; + break; baseStream.Value.Position = _position - baseStream.Key; - _position++; - return baseStream.Value.ReadByte(); + int currentCount = count; + + if(baseStream.Value.Position + currentCount > baseStream.Value.Length) + currentCount = (int)(baseStream.Value.Length - baseStream.Value.Position); + + read += baseStream.Value.Read(buffer, offset, currentCount); + + count -= currentCount; + offset += currentCount; } - /// - public override void WriteByte(byte value) => throw new ReadOnlyException("This stream is read-only"); - - /// - public override void Flush() {} - - /// - public override int Read(byte[] buffer, int offset, int count) - { - int read = 0; - - while(count > 0) - { - KeyValuePair baseStream = _baseStreams.LastOrDefault(s => s.Key <= _position); - - if(baseStream.Value == null) - break; - - baseStream.Value.Position = _position - baseStream.Key; - - int currentCount = count; - - if(baseStream.Value.Position + currentCount > baseStream.Value.Length) - currentCount = (int)(baseStream.Value.Length - baseStream.Value.Position); - - read += baseStream.Value.Read(buffer, offset, currentCount); - - count -= currentCount; - offset += currentCount; - } - - return read; - } - - /// - public override long Seek(long offset, SeekOrigin origin) - { - switch(origin) - { - case SeekOrigin.Begin: - if(offset >= _streamLength) - throw new IOException("Cannot seek past stream end."); - - _position = offset; - - break; - case SeekOrigin.End: - if(_position - offset < 0) - throw new IOException("Cannot seek before stream start."); - - _position -= offset; - - break; - default: - if(_position + offset >= _streamLength) - throw new IOException("Cannot seek past stream end."); - - _position += offset; - - break; - } - - return _position; - } - - /// - public override void SetLength(long value) => throw new ReadOnlyException("This stream is read-only"); - - /// - public override void Write(byte[] buffer, int offset, int count) => - throw new ReadOnlyException("This stream is read-only"); + return read; } + + /// + public override long Seek(long offset, SeekOrigin origin) + { + switch(origin) + { + case SeekOrigin.Begin: + if(offset >= _streamLength) + throw new IOException("Cannot seek past stream end."); + + _position = offset; + + break; + case SeekOrigin.End: + if(_position - offset < 0) + throw new IOException("Cannot seek before stream start."); + + _position -= offset; + + break; + default: + if(_position + offset >= _streamLength) + throw new IOException("Cannot seek past stream end."); + + _position += offset; + + break; + } + + return _position; + } + + /// + public override void SetLength(long value) => throw new ReadOnlyException("This stream is read-only"); + + /// + public override void Write(byte[] buffer, int offset, int count) => + throw new ReadOnlyException("This stream is read-only"); } \ No newline at end of file diff --git a/Aaru.Filters/XZ.cs b/Aaru.Filters/XZ.cs index 5b445fd08..834a10cbb 100644 --- a/Aaru.Filters/XZ.cs +++ b/Aaru.Filters/XZ.cs @@ -36,214 +36,213 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using SharpCompress.Compressors.Xz; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// Decompress xz files while reading +public sealed class XZ : IFilter { + Stream _dataStream; + Stream _innerStream; + /// - /// Decompress xz files while reading - public sealed class XZ : IFilter + public string Name => "XZ"; + /// + public Guid Id => new("666A8617-0444-4C05-9F4F-DF0FD758D0D2"); + /// + public string Author => "Natalia Portillo"; + + /// + public void Close() { - Stream _dataStream; - Stream _innerStream; + _dataStream?.Close(); + _dataStream = null; + BasePath = null; + } - /// - public string Name => "XZ"; - /// - public Guid Id => new("666A8617-0444-4C05-9F4F-DF0FD758D0D2"); - /// - public string Author => "Natalia Portillo"; + /// + public string BasePath { get; private set; } - /// - public void Close() + /// + public Stream GetDataForkStream() => _innerStream; + + /// + public string Path => BasePath; + + /// + public Stream GetResourceForkStream() => null; + + /// + public bool HasResourceFork => false; + + /// + public bool Identify(byte[] buffer) => buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && + buffer[3] == 0x58 && buffer[4] == 0x5A && buffer[5] == 0x00 && + buffer[^2] == 0x59 && buffer[^1] == 0x5A; + + /// + public bool Identify(Stream stream) + { + byte[] buffer = new byte[6]; + byte[] footer = new byte[2]; + + if(stream.Length < 8) + return false; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 6); + stream.Seek(-2, SeekOrigin.End); + stream.Read(footer, 0, 2); + stream.Seek(0, SeekOrigin.Begin); + + return buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && buffer[3] == 0x58 && + buffer[4] == 0x5A && buffer[5] == 0x00 && footer[0] == 0x59 && footer[1] == 0x5A; + } + + /// + public bool Identify(string path) + { + if(!File.Exists(path)) + return false; + + var stream = new FileStream(path, FileMode.Open, FileAccess.Read); + byte[] buffer = new byte[6]; + byte[] footer = new byte[2]; + + if(stream.Length < 8) + return false; + + stream.Seek(0, SeekOrigin.Begin); + stream.Read(buffer, 0, 6); + stream.Seek(-2, SeekOrigin.End); + stream.Read(footer, 0, 2); + stream.Seek(0, SeekOrigin.Begin); + + return buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && buffer[3] == 0x58 && + buffer[4] == 0x5A && buffer[5] == 0x00 && footer[0] == 0x59 && footer[1] == 0x5A; + } + + /// + public ErrorNumber Open(byte[] buffer) + { + _dataStream = new MemoryStream(buffer); + BasePath = null; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + GuessSize(); + _innerStream = new ForcedSeekStream(DataForkLength, _dataStream); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(Stream stream) + { + _dataStream = stream; + BasePath = null; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + GuessSize(); + _innerStream = new ForcedSeekStream(DataForkLength, _dataStream); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(string path) + { + _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); + BasePath = System.IO.Path.GetFullPath(path); + + var fi = new FileInfo(path); + CreationTime = fi.CreationTimeUtc; + LastWriteTime = fi.LastWriteTimeUtc; + GuessSize(); + _innerStream = new ForcedSeekStream(DataForkLength, _dataStream); + + return ErrorNumber.NoError; + } + + /// + public DateTime CreationTime { get; private set; } + + /// + public long DataForkLength { get; private set; } + + /// + public DateTime LastWriteTime { get; private set; } + + /// + public long Length => DataForkLength; + + /// + public long ResourceForkLength => 0; + + /// + public string Filename + { + get { - _dataStream?.Close(); - _dataStream = null; - BasePath = null; - } + if(BasePath?.EndsWith(".xz", StringComparison.InvariantCultureIgnoreCase) == true) + return BasePath.Substring(0, BasePath.Length - 3); - /// - public string BasePath { get; private set; } - - /// - public Stream GetDataForkStream() => _innerStream; - - /// - public string Path => BasePath; - - /// - public Stream GetResourceForkStream() => null; - - /// - public bool HasResourceFork => false; - - /// - public bool Identify(byte[] buffer) => buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && - buffer[3] == 0x58 && buffer[4] == 0x5A && buffer[5] == 0x00 && - buffer[^2] == 0x59 && buffer[^1] == 0x5A; - - /// - public bool Identify(Stream stream) - { - byte[] buffer = new byte[6]; - byte[] footer = new byte[2]; - - if(stream.Length < 8) - return false; - - stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, 6); - stream.Seek(-2, SeekOrigin.End); - stream.Read(footer, 0, 2); - stream.Seek(0, SeekOrigin.Begin); - - return buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && buffer[3] == 0x58 && - buffer[4] == 0x5A && buffer[5] == 0x00 && footer[0] == 0x59 && footer[1] == 0x5A; - } - - /// - public bool Identify(string path) - { - if(!File.Exists(path)) - return false; - - var stream = new FileStream(path, FileMode.Open, FileAccess.Read); - byte[] buffer = new byte[6]; - byte[] footer = new byte[2]; - - if(stream.Length < 8) - return false; - - stream.Seek(0, SeekOrigin.Begin); - stream.Read(buffer, 0, 6); - stream.Seek(-2, SeekOrigin.End); - stream.Read(footer, 0, 2); - stream.Seek(0, SeekOrigin.Begin); - - return buffer[0] == 0xFD && buffer[1] == 0x37 && buffer[2] == 0x7A && buffer[3] == 0x58 && - buffer[4] == 0x5A && buffer[5] == 0x00 && footer[0] == 0x59 && footer[1] == 0x5A; - } - - /// - public ErrorNumber Open(byte[] buffer) - { - _dataStream = new MemoryStream(buffer); - BasePath = null; - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - GuessSize(); - _innerStream = new ForcedSeekStream(DataForkLength, _dataStream); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(Stream stream) - { - _dataStream = stream; - BasePath = null; - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - GuessSize(); - _innerStream = new ForcedSeekStream(DataForkLength, _dataStream); - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(string path) - { - _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); - BasePath = System.IO.Path.GetFullPath(path); - - var fi = new FileInfo(path); - CreationTime = fi.CreationTimeUtc; - LastWriteTime = fi.LastWriteTimeUtc; - GuessSize(); - _innerStream = new ForcedSeekStream(DataForkLength, _dataStream); - - return ErrorNumber.NoError; - } - - /// - public DateTime CreationTime { get; private set; } - - /// - public long DataForkLength { get; private set; } - - /// - public DateTime LastWriteTime { get; private set; } - - /// - public long Length => DataForkLength; - - /// - public long ResourceForkLength => 0; - - /// - public string Filename - { - get - { - if(BasePath?.EndsWith(".xz", StringComparison.InvariantCultureIgnoreCase) == true) - return BasePath.Substring(0, BasePath.Length - 3); - - return BasePath?.EndsWith(".xzip", StringComparison.InvariantCultureIgnoreCase) == true - ? BasePath.Substring(0, BasePath.Length - 5) : BasePath; - } - } - - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); - - void GuessSize() - { - DataForkLength = 0; - - // Seek to footer backwards size field - _dataStream.Seek(-8, SeekOrigin.End); - byte[] tmp = new byte[4]; - _dataStream.Read(tmp, 0, 4); - uint backwardSize = (BitConverter.ToUInt32(tmp, 0) + 1) * 4; - - // Seek to first indexed record - _dataStream.Seek(-12 - (backwardSize - 2), SeekOrigin.End); - - // Skip compressed size - tmp = new byte[backwardSize - 2]; - _dataStream.Read(tmp, 0, tmp.Length); - ulong number = 0; - int ignore = Decode(tmp, tmp.Length, ref number); - - // Get compressed size - _dataStream.Seek(-12 - (backwardSize - 2 - ignore), SeekOrigin.End); - tmp = new byte[backwardSize - 2 - ignore]; - _dataStream.Read(tmp, 0, tmp.Length); - Decode(tmp, tmp.Length, ref number); - DataForkLength = (long)number; - - _dataStream.Seek(0, SeekOrigin.Begin); - } - - int Decode(byte[] buf, int sizeMax, ref ulong num) - { - if(sizeMax == 0) - return 0; - - if(sizeMax > 9) - sizeMax = 9; - - num = (ulong)(buf[0] & 0x7F); - int i = 0; - - while((buf[i++] & 0x80) == 0x80) - { - if(i >= sizeMax || - buf[i] == 0x00) - return 0; - - num |= (ulong)(buf[i] & 0x7F) << (i * 7); - } - - return i; + return BasePath?.EndsWith(".xzip", StringComparison.InvariantCultureIgnoreCase) == true + ? BasePath.Substring(0, BasePath.Length - 5) : BasePath; } } + + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + + void GuessSize() + { + DataForkLength = 0; + + // Seek to footer backwards size field + _dataStream.Seek(-8, SeekOrigin.End); + byte[] tmp = new byte[4]; + _dataStream.Read(tmp, 0, 4); + uint backwardSize = (BitConverter.ToUInt32(tmp, 0) + 1) * 4; + + // Seek to first indexed record + _dataStream.Seek(-12 - (backwardSize - 2), SeekOrigin.End); + + // Skip compressed size + tmp = new byte[backwardSize - 2]; + _dataStream.Read(tmp, 0, tmp.Length); + ulong number = 0; + int ignore = Decode(tmp, tmp.Length, ref number); + + // Get compressed size + _dataStream.Seek(-12 - (backwardSize - 2 - ignore), SeekOrigin.End); + tmp = new byte[backwardSize - 2 - ignore]; + _dataStream.Read(tmp, 0, tmp.Length); + Decode(tmp, tmp.Length, ref number); + DataForkLength = (long)number; + + _dataStream.Seek(0, SeekOrigin.Begin); + } + + int Decode(byte[] buf, int sizeMax, ref ulong num) + { + if(sizeMax == 0) + return 0; + + if(sizeMax > 9) + sizeMax = 9; + + num = (ulong)(buf[0] & 0x7F); + int i = 0; + + while((buf[i++] & 0x80) == 0x80) + { + if(i >= sizeMax || + buf[i] == 0x00) + return 0; + + num |= (ulong)(buf[i] & 0x7F) << (i * 7); + } + + return i; + } } \ No newline at end of file diff --git a/Aaru.Filters/ZZZNoFilter.cs b/Aaru.Filters/ZZZNoFilter.cs index d0369b1e8..01a330af3 100644 --- a/Aaru.Filters/ZZZNoFilter.cs +++ b/Aaru.Filters/ZZZNoFilter.cs @@ -35,106 +35,105 @@ using System.IO; using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Filters +namespace Aaru.Filters; + +/// +/// No filter for reading files not recognized by any filter +public sealed class ZZZNoFilter : IFilter { + Stream _dataStream; + /// - /// No filter for reading files not recognized by any filter - public sealed class ZZZNoFilter : IFilter + public string Name => "No filter"; + /// + public Guid Id => new("12345678-AAAA-BBBB-CCCC-123456789000"); + /// + public string Author => "Natalia Portillo"; + + /// + public void Close() { - Stream _dataStream; - - /// - public string Name => "No filter"; - /// - public Guid Id => new("12345678-AAAA-BBBB-CCCC-123456789000"); - /// - public string Author => "Natalia Portillo"; - - /// - public void Close() - { - _dataStream?.Close(); - _dataStream = null; - BasePath = null; - } - - /// - public string BasePath { get; private set; } - - /// - public Stream GetDataForkStream() => _dataStream; - - /// - public string Path => BasePath; - - /// - public Stream GetResourceForkStream() => null; - - /// - public bool HasResourceFork => false; - - /// - public bool Identify(byte[] buffer) => buffer != null && buffer.Length > 0; - - /// - public bool Identify(Stream stream) => stream != null && stream.Length > 0; - - /// - public bool Identify(string path) => File.Exists(path); - - /// - public ErrorNumber Open(byte[] buffer) - { - _dataStream = new MemoryStream(buffer); - BasePath = null; - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(Stream stream) - { - _dataStream = stream; - BasePath = null; - CreationTime = DateTime.UtcNow; - LastWriteTime = CreationTime; - - return ErrorNumber.NoError; - } - - /// - public ErrorNumber Open(string path) - { - _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); - BasePath = System.IO.Path.GetFullPath(path); - var fi = new FileInfo(path); - CreationTime = fi.CreationTimeUtc; - LastWriteTime = fi.LastWriteTimeUtc; - - return ErrorNumber.NoError; - } - - /// - public DateTime CreationTime { get; private set; } - - /// - public long DataForkLength => _dataStream.Length; - - /// - public DateTime LastWriteTime { get; private set; } - - /// - public long Length => _dataStream.Length; - - /// - public long ResourceForkLength => 0; - - /// - public string Filename => System.IO.Path.GetFileName(BasePath); - - /// - public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); + _dataStream?.Close(); + _dataStream = null; + BasePath = null; } + + /// + public string BasePath { get; private set; } + + /// + public Stream GetDataForkStream() => _dataStream; + + /// + public string Path => BasePath; + + /// + public Stream GetResourceForkStream() => null; + + /// + public bool HasResourceFork => false; + + /// + public bool Identify(byte[] buffer) => buffer != null && buffer.Length > 0; + + /// + public bool Identify(Stream stream) => stream != null && stream.Length > 0; + + /// + public bool Identify(string path) => File.Exists(path); + + /// + public ErrorNumber Open(byte[] buffer) + { + _dataStream = new MemoryStream(buffer); + BasePath = null; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(Stream stream) + { + _dataStream = stream; + BasePath = null; + CreationTime = DateTime.UtcNow; + LastWriteTime = CreationTime; + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber Open(string path) + { + _dataStream = new FileStream(path, FileMode.Open, FileAccess.Read); + BasePath = System.IO.Path.GetFullPath(path); + var fi = new FileInfo(path); + CreationTime = fi.CreationTimeUtc; + LastWriteTime = fi.LastWriteTimeUtc; + + return ErrorNumber.NoError; + } + + /// + public DateTime CreationTime { get; private set; } + + /// + public long DataForkLength => _dataStream.Length; + + /// + public DateTime LastWriteTime { get; private set; } + + /// + public long Length => _dataStream.Length; + + /// + public long ResourceForkLength => 0; + + /// + public string Filename => System.IO.Path.GetFileName(BasePath); + + /// + public string ParentFolder => System.IO.Path.GetDirectoryName(BasePath); } \ No newline at end of file diff --git a/Aaru.Gui/App.xaml.cs b/Aaru.Gui/App.xaml.cs index ee6606459..bc9a5799f 100644 --- a/Aaru.Gui/App.xaml.cs +++ b/Aaru.Gui/App.xaml.cs @@ -41,77 +41,76 @@ using Avalonia.Markup.Xaml; // ReSharper disable UnusedMember.Local // ReSharper disable UnusedParameter.Local -namespace Aaru.Gui +namespace Aaru.Gui; + +public sealed class App : Application { - public sealed class App : Application + public override void Initialize() => AvaloniaXamlLoader.Load(this); + + public override void OnFrameworkInitializationCompleted() { - public override void Initialize() => AvaloniaXamlLoader.Load(this); - - public override void OnFrameworkInitializationCompleted() + if(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { - if(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) - { - var splashWindow = new SplashWindow(); - var swvm = new SplashWindowViewModel(splashWindow); - swvm.WorkFinished += OnSplashFinished; - splashWindow.DataContext = swvm; - desktop.MainWindow = splashWindow; - } - - base.OnFrameworkInitializationCompleted(); + var splashWindow = new SplashWindow(); + var swvm = new SplashWindowViewModel(splashWindow); + swvm.WorkFinished += OnSplashFinished; + splashWindow.DataContext = swvm; + desktop.MainWindow = splashWindow; } - void OnSplashFinished(object sender, EventArgs e) - { - if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)) - return; + base.OnFrameworkInitializationCompleted(); + } - // Ensure not exit - desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown; + void OnSplashFinished(object sender, EventArgs e) + { + if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)) + return; - // Close splash window - desktop.MainWindow.Close(); + // Ensure not exit + desktop.ShutdownMode = ShutdownMode.OnExplicitShutdown; - // Create and show main window - desktop.MainWindow = new MainWindow(); - desktop.MainWindow.DataContext = new MainWindowViewModel(desktop.MainWindow as MainWindow); - desktop.MainWindow.Show(); + // Close splash window + desktop.MainWindow.Close(); - // Now can close when all windows are closed - desktop.ShutdownMode = ShutdownMode.OnLastWindowClose; - } + // Create and show main window + desktop.MainWindow = new MainWindow(); + desktop.MainWindow.DataContext = new MainWindowViewModel(desktop.MainWindow as MainWindow); + desktop.MainWindow.Show(); - void OnAboutClicked(object sender, EventArgs args) - { - if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime - { - MainWindow: MainWindow { DataContext: MainWindowViewModel mainWindowViewModel } - })) - return; + // Now can close when all windows are closed + desktop.ShutdownMode = ShutdownMode.OnLastWindowClose; + } - mainWindowViewModel.ExecuteAboutCommand(); - } + void OnAboutClicked(object sender, EventArgs args) + { + if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime + { + MainWindow: MainWindow { DataContext: MainWindowViewModel mainWindowViewModel } + })) + return; - void OnQuitClicked(object sender, EventArgs args) - { - if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime - { - MainWindow: MainWindow { DataContext: MainWindowViewModel mainWindowViewModel } - })) - return; + mainWindowViewModel.ExecuteAboutCommand(); + } - mainWindowViewModel.ExecuteExitCommand(); - } + void OnQuitClicked(object sender, EventArgs args) + { + if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime + { + MainWindow: MainWindow { DataContext: MainWindowViewModel mainWindowViewModel } + })) + return; - void OnPreferencesClicked(object sender, EventArgs args) - { - if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime - { - MainWindow: MainWindow { DataContext: MainWindowViewModel mainWindowViewModel } - })) - return; + mainWindowViewModel.ExecuteExitCommand(); + } - mainWindowViewModel.ExecuteSettingsCommand(); - } + void OnPreferencesClicked(object sender, EventArgs args) + { + if(!(ApplicationLifetime is IClassicDesktopStyleApplicationLifetime + { + MainWindow: MainWindow { DataContext: MainWindowViewModel mainWindowViewModel } + })) + return; + + mainWindowViewModel.ExecuteSettingsCommand(); } } \ No newline at end of file diff --git a/Aaru.Gui/ConsoleHandler.cs b/Aaru.Gui/ConsoleHandler.cs index 86c481ba7..b8dea796a 100644 --- a/Aaru.Gui/ConsoleHandler.cs +++ b/Aaru.Gui/ConsoleHandler.cs @@ -36,121 +36,120 @@ using System.Collections.ObjectModel; using Aaru.Console; using JetBrains.Annotations; -namespace Aaru.Gui +namespace Aaru.Gui; + +internal static class ConsoleHandler { - internal static class ConsoleHandler + static bool _debug; + static bool _verbose; + + public static bool Debug { - static bool _debug; - static bool _verbose; - - public static bool Debug + get => _debug; + set { - get => _debug; - set - { - if(_debug == value) - return; - - _debug = value; - - if(_debug) - AaruConsole.DebugWithModuleWriteLineEvent += OnDebugWriteHandler; - else - AaruConsole.DebugWithModuleWriteLineEvent -= OnDebugWriteHandler; - } - } - - public static bool Verbose - { - get => _verbose; - set - { - if(_verbose == value) - return; - - _verbose = value; - - if(_verbose) - AaruConsole.VerboseWriteLineEvent += OnVerboseWriteHandler; - else - AaruConsole.VerboseWriteLineEvent -= OnVerboseWriteHandler; - } - } - - public static ObservableCollection Entries { get; } = new ObservableCollection(); - - internal static void Init() - { - AaruConsole.WriteLineEvent += OnWriteHandler; - AaruConsole.ErrorWriteLineEvent += OnErrorWriteHandler; - } - - static void OnWriteHandler([CanBeNull] string format, [CanBeNull] params object[] arg) - { - if(format == null || - arg == null) + if(_debug == value) return; - Entries.Add(new LogEntry - { - Message = string.Format(format, arg), - Module = null, - Timestamp = DateTime.Now, - Type = "Info" - }); - } + _debug = value; - static void OnErrorWriteHandler([CanBeNull] string format, [CanBeNull] params object[] arg) - { - if(format == null || - arg == null) - return; - - Entries.Add(new LogEntry - { - Message = string.Format(format, arg), - Module = null, - Timestamp = DateTime.Now, - Type = "Error" - }); - } - - static void OnVerboseWriteHandler([CanBeNull] string format, [CanBeNull] params object[] arg) - { - if(format == null || - arg == null) - return; - - Entries.Add(new LogEntry - { - Message = string.Format(format, arg), - Module = null, - Timestamp = DateTime.Now, - Type = "Verbose" - }); - } - - static void OnDebugWriteHandler(string module, [CanBeNull] string format, [CanBeNull] params object[] arg) - { - if(format == null || - arg == null) - return; - - Entries.Add(new LogEntry - { - Message = string.Format(format, arg), - Module = module, - Timestamp = DateTime.Now, - Type = "Debug" - }); + if(_debug) + AaruConsole.DebugWithModuleWriteLineEvent += OnDebugWriteHandler; + else + AaruConsole.DebugWithModuleWriteLineEvent -= OnDebugWriteHandler; } } - public sealed class LogEntry + public static bool Verbose { - public string Message { get; set; } - public string Module { get; set; } - public DateTime Timestamp { get; set; } - public string Type { get; set; } + get => _verbose; + set + { + if(_verbose == value) + return; + + _verbose = value; + + if(_verbose) + AaruConsole.VerboseWriteLineEvent += OnVerboseWriteHandler; + else + AaruConsole.VerboseWriteLineEvent -= OnVerboseWriteHandler; + } } + + public static ObservableCollection Entries { get; } = new ObservableCollection(); + + internal static void Init() + { + AaruConsole.WriteLineEvent += OnWriteHandler; + AaruConsole.ErrorWriteLineEvent += OnErrorWriteHandler; + } + + static void OnWriteHandler([CanBeNull] string format, [CanBeNull] params object[] arg) + { + if(format == null || + arg == null) + return; + + Entries.Add(new LogEntry + { + Message = string.Format(format, arg), + Module = null, + Timestamp = DateTime.Now, + Type = "Info" + }); + } + + static void OnErrorWriteHandler([CanBeNull] string format, [CanBeNull] params object[] arg) + { + if(format == null || + arg == null) + return; + + Entries.Add(new LogEntry + { + Message = string.Format(format, arg), + Module = null, + Timestamp = DateTime.Now, + Type = "Error" + }); + } + + static void OnVerboseWriteHandler([CanBeNull] string format, [CanBeNull] params object[] arg) + { + if(format == null || + arg == null) + return; + + Entries.Add(new LogEntry + { + Message = string.Format(format, arg), + Module = null, + Timestamp = DateTime.Now, + Type = "Verbose" + }); + } + + static void OnDebugWriteHandler(string module, [CanBeNull] string format, [CanBeNull] params object[] arg) + { + if(format == null || + arg == null) + return; + + Entries.Add(new LogEntry + { + Message = string.Format(format, arg), + Module = module, + Timestamp = DateTime.Now, + Type = "Debug" + }); + } +} + +public sealed class LogEntry +{ + public string Message { get; set; } + public string Module { get; set; } + public DateTime Timestamp { get; set; } + public string Type { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Controls/BlockMap.cs b/Aaru.Gui/Controls/BlockMap.cs index 63a3f86bd..ee454e7ab 100644 --- a/Aaru.Gui/Controls/BlockMap.cs +++ b/Aaru.Gui/Controls/BlockMap.cs @@ -43,450 +43,449 @@ using Avalonia.Threading; using Avalonia.Visuals.Media.Imaging; using JetBrains.Annotations; -namespace Aaru.Gui.Controls +namespace Aaru.Gui.Controls; + +// TODO: Partially fill clusters +// TODO: React to size changes +// TODO: Optimize block size to viewport +// TODO: Writing one more than it should +public sealed class BlockMap : ItemsControl { - // TODO: Partially fill clusters - // TODO: React to size changes - // TODO: Optimize block size to viewport - // TODO: Writing one more than it should - public sealed class BlockMap : ItemsControl + const int BLOCK_SIZE = 15; + public static readonly StyledProperty BlocksProperty = + AvaloniaProperty.Register(nameof(Blocks)); + + public static readonly StyledProperty SuperFastColorProperty = + AvaloniaProperty.Register(nameof(SuperFastColor), Brushes.LightGreen); + + public static readonly StyledProperty FastColorProperty = + AvaloniaProperty.Register(nameof(FastColor), Brushes.Green); + + public static readonly StyledProperty AverageColorProperty = + AvaloniaProperty.Register(nameof(AverageColor), Brushes.DarkGreen); + + public static readonly StyledProperty SlowColorProperty = + AvaloniaProperty.Register(nameof(SlowColor), Brushes.Yellow); + + public static readonly StyledProperty SuperSlowColorProperty = + AvaloniaProperty.Register(nameof(SuperSlowColor), Brushes.Orange); + + public static readonly StyledProperty ProblematicColorProperty = + AvaloniaProperty.Register(nameof(ProblematicColor), Brushes.Red); + + public static readonly StyledProperty SuperFastMaxTimeProperty = + AvaloniaProperty.Register(nameof(SuperFastMaxTime), 3); + + public static readonly StyledProperty FastMaxTimeProperty = + AvaloniaProperty.Register(nameof(FastMaxTime), 10); + + public static readonly StyledProperty AverageMaxTimeProperty = + AvaloniaProperty.Register(nameof(AverageMaxTime), 50); + + public static readonly StyledProperty SlowMaxTimeProperty = + AvaloniaProperty.Register(nameof(SlowMaxTime), 150); + + public static readonly StyledProperty SuperSlowMaxTimeProperty = + AvaloniaProperty.Register(nameof(SuperSlowMaxTime), 500); + RenderTargetBitmap _bitmap; + ulong _clusterSize; + ulong _maxBlocks; + + public double SuperFastMaxTime { - const int BLOCK_SIZE = 15; - public static readonly StyledProperty BlocksProperty = - AvaloniaProperty.Register(nameof(Blocks)); + get => GetValue(SuperFastMaxTimeProperty); + set => SetValue(SuperFastMaxTimeProperty, value); + } - public static readonly StyledProperty SuperFastColorProperty = - AvaloniaProperty.Register(nameof(SuperFastColor), Brushes.LightGreen); + public double FastMaxTime + { + get => GetValue(FastMaxTimeProperty); + set => SetValue(FastMaxTimeProperty, value); + } - public static readonly StyledProperty FastColorProperty = - AvaloniaProperty.Register(nameof(FastColor), Brushes.Green); + public double AverageMaxTime + { + get => GetValue(AverageMaxTimeProperty); + set => SetValue(AverageMaxTimeProperty, value); + } - public static readonly StyledProperty AverageColorProperty = - AvaloniaProperty.Register(nameof(AverageColor), Brushes.DarkGreen); + public double SlowMaxTime + { + get => GetValue(SlowMaxTimeProperty); + set => SetValue(SlowMaxTimeProperty, value); + } - public static readonly StyledProperty SlowColorProperty = - AvaloniaProperty.Register(nameof(SlowColor), Brushes.Yellow); + public double SuperSlowMaxTime + { + get => GetValue(SuperSlowMaxTimeProperty); + set => SetValue(SuperSlowMaxTimeProperty, value); + } - public static readonly StyledProperty SuperSlowColorProperty = - AvaloniaProperty.Register(nameof(SuperSlowColor), Brushes.Orange); + public IBrush SuperFastColor + { + get => GetValue(SuperFastColorProperty); + set => SetValue(SuperFastColorProperty, value); + } - public static readonly StyledProperty ProblematicColorProperty = - AvaloniaProperty.Register(nameof(ProblematicColor), Brushes.Red); + public IBrush FastColor + { + get => GetValue(FastColorProperty); + set => SetValue(FastColorProperty, value); + } - public static readonly StyledProperty SuperFastMaxTimeProperty = - AvaloniaProperty.Register(nameof(SuperFastMaxTime), 3); + public IBrush AverageColor + { + get => GetValue(AverageColorProperty); + set => SetValue(AverageColorProperty, value); + } - public static readonly StyledProperty FastMaxTimeProperty = - AvaloniaProperty.Register(nameof(FastMaxTime), 10); + public IBrush SlowColor + { + get => GetValue(SlowColorProperty); + set => SetValue(SlowColorProperty, value); + } - public static readonly StyledProperty AverageMaxTimeProperty = - AvaloniaProperty.Register(nameof(AverageMaxTime), 50); + public IBrush SuperSlowColor + { + get => GetValue(SuperSlowColorProperty); + set => SetValue(SuperSlowColorProperty, value); + } - public static readonly StyledProperty SlowMaxTimeProperty = - AvaloniaProperty.Register(nameof(SlowMaxTime), 150); + public IBrush ProblematicColor + { + get => GetValue(ProblematicColorProperty); + set => SetValue(ProblematicColorProperty, value); + } - public static readonly StyledProperty SuperSlowMaxTimeProperty = - AvaloniaProperty.Register(nameof(SuperSlowMaxTime), 500); - RenderTargetBitmap _bitmap; - ulong _clusterSize; - ulong _maxBlocks; + public ulong Blocks + { + get => GetValue(BlocksProperty); + set => SetValue(BlocksProperty, value); + } - public double SuperFastMaxTime + protected override void OnPropertyChanged([NotNull] AvaloniaPropertyChangedEventArgs e) + { + base.OnPropertyChanged(e); + + switch(e.Property.Name) { - get => GetValue(SuperFastMaxTimeProperty); - set => SetValue(SuperFastMaxTimeProperty, value); - } + case nameof(Blocks): + if(_maxBlocks == 0) + _maxBlocks = (ulong)(Width / BLOCK_SIZE * (Height / BLOCK_SIZE)); - public double FastMaxTime - { - get => GetValue(FastMaxTimeProperty); - set => SetValue(FastMaxTimeProperty, value); - } + if(Blocks > _maxBlocks) + { + _clusterSize = Blocks / _maxBlocks; - public double AverageMaxTime - { - get => GetValue(AverageMaxTimeProperty); - set => SetValue(AverageMaxTimeProperty, value); - } + if(Blocks % _maxBlocks > 0) + _clusterSize++; - public double SlowMaxTime - { - get => GetValue(SlowMaxTimeProperty); - set => SetValue(SlowMaxTimeProperty, value); - } - - public double SuperSlowMaxTime - { - get => GetValue(SuperSlowMaxTimeProperty); - set => SetValue(SuperSlowMaxTimeProperty, value); - } - - public IBrush SuperFastColor - { - get => GetValue(SuperFastColorProperty); - set => SetValue(SuperFastColorProperty, value); - } - - public IBrush FastColor - { - get => GetValue(FastColorProperty); - set => SetValue(FastColorProperty, value); - } - - public IBrush AverageColor - { - get => GetValue(AverageColorProperty); - set => SetValue(AverageColorProperty, value); - } - - public IBrush SlowColor - { - get => GetValue(SlowColorProperty); - set => SetValue(SlowColorProperty, value); - } - - public IBrush SuperSlowColor - { - get => GetValue(SuperSlowColorProperty); - set => SetValue(SuperSlowColorProperty, value); - } - - public IBrush ProblematicColor - { - get => GetValue(ProblematicColorProperty); - set => SetValue(ProblematicColorProperty, value); - } - - public ulong Blocks - { - get => GetValue(BlocksProperty); - set => SetValue(BlocksProperty, value); - } - - protected override void OnPropertyChanged([NotNull] AvaloniaPropertyChangedEventArgs e) - { - base.OnPropertyChanged(e); - - switch(e.Property.Name) - { - case nameof(Blocks): - if(_maxBlocks == 0) - _maxBlocks = (ulong)(Width / BLOCK_SIZE * (Height / BLOCK_SIZE)); - - if(Blocks > _maxBlocks) + if(Blocks / _clusterSize < _maxBlocks) { - _clusterSize = Blocks / _maxBlocks; + _maxBlocks = Blocks / _clusterSize; - if(Blocks % _maxBlocks > 0) - _clusterSize++; - - if(Blocks / _clusterSize < _maxBlocks) - { - _maxBlocks = Blocks / _clusterSize; - - if(Blocks % _clusterSize > 0) - _maxBlocks++; - } - } - else - { - _clusterSize = 1; - _maxBlocks = Blocks; + if(Blocks % _clusterSize > 0) + _maxBlocks++; } + } + else + { + _clusterSize = 1; + _maxBlocks = Blocks; + } - CreateBitmap(); - DrawGrid(); - RedrawAll(); - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - - break; - case nameof(SuperFastMaxTime): - case nameof(FastMaxTime): - case nameof(AverageMaxTime): - case nameof(SlowMaxTime): - case nameof(SuperSlowMaxTime): - case nameof(SuperFastColor): - case nameof(FastColor): - case nameof(AverageColor): - case nameof(SlowColor): - case nameof(SuperSlowColor): - case nameof(ProblematicColor): - - CreateBitmap(); - DrawGrid(); - RedrawAll(); - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - - break; - } - } - - public override void Render([NotNull] DrawingContext context) - { - if((int?)_bitmap?.Size.Height != (int)Height || - (int?)_bitmap?.Size.Width != (int)Width) - { - _maxBlocks = (ulong)(Width / BLOCK_SIZE * (Height / BLOCK_SIZE)); CreateBitmap(); - } - - context.DrawImage(_bitmap, new Rect(0, 0, Width, Height), new Rect(0, 0, Width, Height), - BitmapInterpolationMode.HighQuality); - - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - base.Render(context); - } - - protected override void ItemsCollectionChanged(object sender, [NotNull] NotifyCollectionChangedEventArgs e) - { - base.ItemsCollectionChanged(sender, e); - - switch(e.Action) - { - case NotifyCollectionChangedAction.Add: - case NotifyCollectionChangedAction.Replace: - { - if(!(e.NewItems is {} items)) - throw new ArgumentException("Invalid list of items"); - - using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); - using var ctx = new DrawingContext(ctxi, false); - - foreach(object item in items) - { - if(!(item is ValueTuple block)) - throw new ArgumentException("Invalid item in list", nameof(Items)); - - DrawCluster(block.Item1, block.Item2, false, ctx); - } - - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - - break; - } - case NotifyCollectionChangedAction.Remove: - case NotifyCollectionChangedAction.Move: - { - if(!(e.NewItems is {} newItems) || - !(e.OldItems is {} oldItems)) - throw new ArgumentException("Invalid list of items"); - - using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); - using var ctx = new DrawingContext(ctxi, false); - - foreach(object item in oldItems) - { - if(!(item is ValueTuple block)) - throw new ArgumentException("Invalid item in list", nameof(Items)); - - DrawCluster(block.Item1, block.Item2, false, ctx); - } - - foreach(object item in newItems) - { - if(!(item is ValueTuple block)) - throw new ArgumentException("Invalid item in list", nameof(Items)); - - DrawCluster(block.Item1, block.Item2, false, ctx); - } - - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - - break; - } - case NotifyCollectionChangedAction.Reset: - CreateBitmap(); - DrawGrid(); - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - - break; - default: throw new ArgumentOutOfRangeException(); - } - } - - void RedrawAll() - { - if(Items is null) - return; - - using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); - using var ctx = new DrawingContext(ctxi, false); - - foreach(object item in Items) - { - if(!(item is ValueTuple block)) - throw new ArgumentException("Invalid item in list", nameof(Items)); - - DrawCluster(block.Item1, block.Item2, false, ctx); - } - - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - } - - void DrawCluster(ulong block, double duration, bool clear = false, DrawingContext ctx = null) - { - if(double.IsNegative(duration) || - double.IsInfinity(duration)) - throw new ArgumentException("Duration cannot be negative or infinite", nameof(duration)); - - bool newContext = ctx is null; - ulong clustersPerRow = (ulong)Width / BLOCK_SIZE; - ulong cluster = block / _clusterSize; - ulong row = cluster / clustersPerRow; - ulong column = cluster % clustersPerRow; - ulong x = column * BLOCK_SIZE; - ulong y = row * BLOCK_SIZE; - var pen = new Pen(Foreground); - - IBrush brush; - - if(clear) - brush = Background; - else if(duration < SuperFastMaxTime) - brush = SuperFastColor; - else if(duration >= SuperFastMaxTime && - duration < FastMaxTime) - brush = FastColor; - else if(duration >= FastMaxTime && - duration < AverageMaxTime) - brush = AverageColor; - else if(duration >= AverageMaxTime && - duration < SlowMaxTime) - brush = SlowColor; - else if(duration >= SlowMaxTime && - duration < SuperSlowMaxTime) - brush = SuperSlowColor; - else if(duration >= SuperSlowMaxTime || - double.IsNaN(duration)) - brush = ProblematicColor; - else - brush = Background; - - if(newContext) - { - using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); - ctx = new DrawingContext(ctxi, false); - } - - ctx.FillRectangle(brush, new Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)); - ctx.DrawRectangle(pen, new Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)); - - if(double.IsNaN(duration)) - { - ctx.DrawLine(pen, new Point(x, y), new Point(x + BLOCK_SIZE, y + BLOCK_SIZE)); - ctx.DrawLine(pen, new Point(x, y + BLOCK_SIZE), new Point(x + BLOCK_SIZE, y)); - } - - if(newContext) + DrawGrid(); + RedrawAll(); Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - } - protected override void ItemsChanged([NotNull] AvaloniaPropertyChangedEventArgs e) - { - if(e.NewValue != null && - !(e.NewValue is IList<(ulong, double)>)) - { - throw new - ArgumentException("Items must be a IList<(ulong, double)> with ulong being the block and double being the time spent reading it, or NaN for an error."); - } + break; + case nameof(SuperFastMaxTime): + case nameof(FastMaxTime): + case nameof(AverageMaxTime): + case nameof(SlowMaxTime): + case nameof(SuperSlowMaxTime): + case nameof(SuperFastColor): + case nameof(FastColor): + case nameof(AverageColor): + case nameof(SlowColor): + case nameof(SuperSlowColor): + case nameof(ProblematicColor): - base.ItemsChanged(e); + CreateBitmap(); + DrawGrid(); + RedrawAll(); + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - CreateBitmap(); - DrawGrid(); - RedrawAll(); - Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); - } - - void CreateBitmap() - { - if(_maxBlocks == 0) - _maxBlocks = (ulong)(Width / BLOCK_SIZE * (Height / BLOCK_SIZE)); - - _bitmap?.Dispose(); - - _bitmap = new RenderTargetBitmap(new PixelSize((int)Width, (int)Height), new Vector(96, 96)); - - using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); - using var ctx = new DrawingContext(ctxi, false); - - ctx.FillRectangle(Background, new Rect(0, 0, Width, Height)); - } - - void DrawGrid() - { - using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); - using var ctx = new DrawingContext(ctxi, false); - - ulong clustersPerRow = (ulong)Width / BLOCK_SIZE; - - bool allBlocksDrawn = false; - - for(ulong y = 0; y < Height && !allBlocksDrawn; y += BLOCK_SIZE) - { - for(ulong x = 0; x < Width; x += BLOCK_SIZE) - { - ulong currentBlockValue = (y * clustersPerRow / BLOCK_SIZE) + (x / BLOCK_SIZE); - - if(currentBlockValue >= _maxBlocks || - currentBlockValue >= Blocks) - { - allBlocksDrawn = true; - - break; - } - - ctx.DrawRectangle(new Pen(Foreground), new Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)); - } - } - } - - void DrawSquares(Color[] colors, int borderWidth, int sideLength) - { - using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); - using var ctx = new DrawingContext(ctxi, false); - - int squareWidth = (sideLength - (2 * borderWidth)) / colors.Length; - int squareHeight = squareWidth; - int x = 0; - int y = 0; - - foreach(Color color in colors) - { - ctx.FillRectangle(new SolidColorBrush(color), new Rect(x, y, squareWidth, squareHeight)); - x += squareWidth + (2 * borderWidth); - - if(x >= sideLength) - { - x = 0; - y += squareHeight + (2 * borderWidth); - } - } - } - - protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) - { - if(Width < 1 || - Height < 1 || - double.IsNaN(Width) || - double.IsNaN(Height)) - { - base.OnAttachedToLogicalTree(e); - - return; - } - - CreateBitmap(); - DrawGrid(); - - base.OnAttachedToLogicalTree(e); - } - - protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) - { - _bitmap.Dispose(); - _bitmap = null; - base.OnDetachedFromLogicalTree(e); + break; } } + + public override void Render([NotNull] DrawingContext context) + { + if((int?)_bitmap?.Size.Height != (int)Height || + (int?)_bitmap?.Size.Width != (int)Width) + { + _maxBlocks = (ulong)(Width / BLOCK_SIZE * (Height / BLOCK_SIZE)); + CreateBitmap(); + } + + context.DrawImage(_bitmap, new Rect(0, 0, Width, Height), new Rect(0, 0, Width, Height), + BitmapInterpolationMode.HighQuality); + + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + base.Render(context); + } + + protected override void ItemsCollectionChanged(object sender, [NotNull] NotifyCollectionChangedEventArgs e) + { + base.ItemsCollectionChanged(sender, e); + + switch(e.Action) + { + case NotifyCollectionChangedAction.Add: + case NotifyCollectionChangedAction.Replace: + { + if(!(e.NewItems is {} items)) + throw new ArgumentException("Invalid list of items"); + + using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); + using var ctx = new DrawingContext(ctxi, false); + + foreach(object item in items) + { + if(!(item is ValueTuple block)) + throw new ArgumentException("Invalid item in list", nameof(Items)); + + DrawCluster(block.Item1, block.Item2, false, ctx); + } + + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + + break; + } + case NotifyCollectionChangedAction.Remove: + case NotifyCollectionChangedAction.Move: + { + if(!(e.NewItems is {} newItems) || + !(e.OldItems is {} oldItems)) + throw new ArgumentException("Invalid list of items"); + + using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); + using var ctx = new DrawingContext(ctxi, false); + + foreach(object item in oldItems) + { + if(!(item is ValueTuple block)) + throw new ArgumentException("Invalid item in list", nameof(Items)); + + DrawCluster(block.Item1, block.Item2, false, ctx); + } + + foreach(object item in newItems) + { + if(!(item is ValueTuple block)) + throw new ArgumentException("Invalid item in list", nameof(Items)); + + DrawCluster(block.Item1, block.Item2, false, ctx); + } + + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + + break; + } + case NotifyCollectionChangedAction.Reset: + CreateBitmap(); + DrawGrid(); + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + + break; + default: throw new ArgumentOutOfRangeException(); + } + } + + void RedrawAll() + { + if(Items is null) + return; + + using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); + using var ctx = new DrawingContext(ctxi, false); + + foreach(object item in Items) + { + if(!(item is ValueTuple block)) + throw new ArgumentException("Invalid item in list", nameof(Items)); + + DrawCluster(block.Item1, block.Item2, false, ctx); + } + + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + } + + void DrawCluster(ulong block, double duration, bool clear = false, DrawingContext ctx = null) + { + if(double.IsNegative(duration) || + double.IsInfinity(duration)) + throw new ArgumentException("Duration cannot be negative or infinite", nameof(duration)); + + bool newContext = ctx is null; + ulong clustersPerRow = (ulong)Width / BLOCK_SIZE; + ulong cluster = block / _clusterSize; + ulong row = cluster / clustersPerRow; + ulong column = cluster % clustersPerRow; + ulong x = column * BLOCK_SIZE; + ulong y = row * BLOCK_SIZE; + var pen = new Pen(Foreground); + + IBrush brush; + + if(clear) + brush = Background; + else if(duration < SuperFastMaxTime) + brush = SuperFastColor; + else if(duration >= SuperFastMaxTime && + duration < FastMaxTime) + brush = FastColor; + else if(duration >= FastMaxTime && + duration < AverageMaxTime) + brush = AverageColor; + else if(duration >= AverageMaxTime && + duration < SlowMaxTime) + brush = SlowColor; + else if(duration >= SlowMaxTime && + duration < SuperSlowMaxTime) + brush = SuperSlowColor; + else if(duration >= SuperSlowMaxTime || + double.IsNaN(duration)) + brush = ProblematicColor; + else + brush = Background; + + if(newContext) + { + using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); + ctx = new DrawingContext(ctxi, false); + } + + ctx.FillRectangle(brush, new Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)); + ctx.DrawRectangle(pen, new Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)); + + if(double.IsNaN(duration)) + { + ctx.DrawLine(pen, new Point(x, y), new Point(x + BLOCK_SIZE, y + BLOCK_SIZE)); + ctx.DrawLine(pen, new Point(x, y + BLOCK_SIZE), new Point(x + BLOCK_SIZE, y)); + } + + if(newContext) + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + } + + protected override void ItemsChanged([NotNull] AvaloniaPropertyChangedEventArgs e) + { + if(e.NewValue != null && + !(e.NewValue is IList<(ulong, double)>)) + { + throw new + ArgumentException("Items must be a IList<(ulong, double)> with ulong being the block and double being the time spent reading it, or NaN for an error."); + } + + base.ItemsChanged(e); + + CreateBitmap(); + DrawGrid(); + RedrawAll(); + Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background); + } + + void CreateBitmap() + { + if(_maxBlocks == 0) + _maxBlocks = (ulong)(Width / BLOCK_SIZE * (Height / BLOCK_SIZE)); + + _bitmap?.Dispose(); + + _bitmap = new RenderTargetBitmap(new PixelSize((int)Width, (int)Height), new Vector(96, 96)); + + using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); + using var ctx = new DrawingContext(ctxi, false); + + ctx.FillRectangle(Background, new Rect(0, 0, Width, Height)); + } + + void DrawGrid() + { + using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); + using var ctx = new DrawingContext(ctxi, false); + + ulong clustersPerRow = (ulong)Width / BLOCK_SIZE; + + bool allBlocksDrawn = false; + + for(ulong y = 0; y < Height && !allBlocksDrawn; y += BLOCK_SIZE) + { + for(ulong x = 0; x < Width; x += BLOCK_SIZE) + { + ulong currentBlockValue = (y * clustersPerRow / BLOCK_SIZE) + (x / BLOCK_SIZE); + + if(currentBlockValue >= _maxBlocks || + currentBlockValue >= Blocks) + { + allBlocksDrawn = true; + + break; + } + + ctx.DrawRectangle(new Pen(Foreground), new Rect(x, y, BLOCK_SIZE, BLOCK_SIZE)); + } + } + } + + void DrawSquares(Color[] colors, int borderWidth, int sideLength) + { + using IDrawingContextImpl ctxi = _bitmap.CreateDrawingContext(null); + using var ctx = new DrawingContext(ctxi, false); + + int squareWidth = (sideLength - (2 * borderWidth)) / colors.Length; + int squareHeight = squareWidth; + int x = 0; + int y = 0; + + foreach(Color color in colors) + { + ctx.FillRectangle(new SolidColorBrush(color), new Rect(x, y, squareWidth, squareHeight)); + x += squareWidth + (2 * borderWidth); + + if(x >= sideLength) + { + x = 0; + y += squareHeight + (2 * borderWidth); + } + } + } + + protected override void OnAttachedToLogicalTree(LogicalTreeAttachmentEventArgs e) + { + if(Width < 1 || + Height < 1 || + double.IsNaN(Width) || + double.IsNaN(Height)) + { + base.OnAttachedToLogicalTree(e); + + return; + } + + CreateBitmap(); + DrawGrid(); + + base.OnAttachedToLogicalTree(e); + } + + protected override void OnDetachedFromLogicalTree(LogicalTreeAttachmentEventArgs e) + { + _bitmap.Dispose(); + _bitmap = null; + base.OnDetachedFromLogicalTree(e); + } } \ No newline at end of file diff --git a/Aaru.Gui/Main.cs b/Aaru.Gui/Main.cs index 62a140668..f490441e1 100644 --- a/Aaru.Gui/Main.cs +++ b/Aaru.Gui/Main.cs @@ -31,19 +31,18 @@ using Avalonia; using Avalonia.Dialogs; using Avalonia.ReactiveUI; -namespace Aaru.Gui +namespace Aaru.Gui; + +public static class Main { - public static class Main + public static int Start(string[] args) { - public static int Start(string[] args) - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - return BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); - } - - // Avalonia configuration, don't remove; also used by visual designer. - public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure().UsePlatformDetect().LogToDebug(). - UseReactiveUI().UseManagedSystemDialogs(); + return BuildAvaloniaApp().StartWithClassicDesktopLifetime(args); } + + // Avalonia configuration, don't remove; also used by visual designer. + public static AppBuilder BuildAvaloniaApp() => AppBuilder.Configure().UsePlatformDetect().LogToDebug(). + UseReactiveUI().UseManagedSystemDialogs(); } \ No newline at end of file diff --git a/Aaru.Gui/Models/AssemblyModel.cs b/Aaru.Gui/Models/AssemblyModel.cs index 1f7e98251..952c74fa5 100644 --- a/Aaru.Gui/Models/AssemblyModel.cs +++ b/Aaru.Gui/Models/AssemblyModel.cs @@ -30,11 +30,10 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class AssemblyModel { - public sealed class AssemblyModel - { - public string Name { get; set; } - public string Version { get; set; } - } + public string Name { get; set; } + public string Version { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/ChecksumModel.cs b/Aaru.Gui/Models/ChecksumModel.cs index 155a6d0ad..92e193fb4 100644 --- a/Aaru.Gui/Models/ChecksumModel.cs +++ b/Aaru.Gui/Models/ChecksumModel.cs @@ -30,12 +30,11 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class ChecksumModel { - public sealed class ChecksumModel - { - public string Track { get; set; } - public string Algorithm { get; set; } - public string Hash { get; set; } - } + public string Track { get; set; } + public string Algorithm { get; set; } + public string Hash { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/DeviceModel.cs b/Aaru.Gui/Models/DeviceModel.cs index 1a96df566..5fb59f811 100644 --- a/Aaru.Gui/Models/DeviceModel.cs +++ b/Aaru.Gui/Models/DeviceModel.cs @@ -34,17 +34,16 @@ using System.Collections.ObjectModel; using Aaru.Gui.ViewModels.Panels; using Avalonia.Media.Imaging; -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class DeviceModel { - public sealed class DeviceModel - { - public DeviceModel() => Media = new ObservableCollection(); + public DeviceModel() => Media = new ObservableCollection(); - public Bitmap Icon { get; set; } - public string Name { get; set; } - public string Path { get; set; } - public DeviceInfoViewModel ViewModel { get; set; } + public Bitmap Icon { get; set; } + public string Name { get; set; } + public string Path { get; set; } + public DeviceInfoViewModel ViewModel { get; set; } - public ObservableCollection Media { get; } - } + public ObservableCollection Media { get; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/DeviceStatsModel.cs b/Aaru.Gui/Models/DeviceStatsModel.cs index 7c3d07c56..72ef9d828 100644 --- a/Aaru.Gui/Models/DeviceStatsModel.cs +++ b/Aaru.Gui/Models/DeviceStatsModel.cs @@ -30,13 +30,12 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class DeviceStatsModel { - public sealed class DeviceStatsModel - { - public string Model { get; set; } - public string Manufacturer { get; set; } - public string Revision { get; set; } - public string Bus { get; set; } - } + public string Model { get; set; } + public string Manufacturer { get; set; } + public string Revision { get; set; } + public string Bus { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/DevicesRootModel.cs b/Aaru.Gui/Models/DevicesRootModel.cs index 989e3b561..165faba61 100644 --- a/Aaru.Gui/Models/DevicesRootModel.cs +++ b/Aaru.Gui/Models/DevicesRootModel.cs @@ -32,12 +32,11 @@ using System.Collections.ObjectModel; -namespace Aaru.Gui.Models -{ - public sealed class DevicesRootModel : RootModel - { - public DevicesRootModel() => Devices = new ObservableCollection(); +namespace Aaru.Gui.Models; - public ObservableCollection Devices { get; } - } +public sealed class DevicesRootModel : RootModel +{ + public DevicesRootModel() => Devices = new ObservableCollection(); + + public ObservableCollection Devices { get; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/DumpHardwareModel.cs b/Aaru.Gui/Models/DumpHardwareModel.cs index 9a103521a..e987c413b 100644 --- a/Aaru.Gui/Models/DumpHardwareModel.cs +++ b/Aaru.Gui/Models/DumpHardwareModel.cs @@ -30,22 +30,21 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class DumpHardwareModel { - public sealed class DumpHardwareModel - { - public string Manufacturer { get; set; } + public string Manufacturer { get; set; } - public string Model { get; set; } + public string Model { get; set; } - public string Revision { get; set; } + public string Revision { get; set; } - public string Serial { get; set; } + public string Serial { get; set; } - public string SoftwareName { get; set; } - public string SoftwareVersion { get; set; } - public string OperatingSystem { get; set; } - public ulong Start { get; set; } - public ulong End { get; set; } - } + public string SoftwareName { get; set; } + public string SoftwareVersion { get; set; } + public string OperatingSystem { get; set; } + public ulong Start { get; set; } + public ulong End { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/EncodingModel.cs b/Aaru.Gui/Models/EncodingModel.cs index f54ab4c0c..03ab626e8 100644 --- a/Aaru.Gui/Models/EncodingModel.cs +++ b/Aaru.Gui/Models/EncodingModel.cs @@ -30,11 +30,10 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class EncodingModel { - public sealed class EncodingModel - { - public string Name { get; set; } - public string DisplayName { get; set; } - } + public string Name { get; set; } + public string DisplayName { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/FileModel.cs b/Aaru.Gui/Models/FileModel.cs index 8f7e6ae3e..a7ee20a07 100644 --- a/Aaru.Gui/Models/FileModel.cs +++ b/Aaru.Gui/Models/FileModel.cs @@ -34,35 +34,34 @@ using System; using Aaru.CommonTypes.Structs; using JetBrains.Annotations; -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class FileModel { - public sealed class FileModel - { - public string Name { get; set; } - [NotNull] - public string Size => $"{Stat.Length}"; - [NotNull] - public string CreationTime => Stat.CreationTime == default(DateTime) ? "" : $"{Stat.CreationTime:G}"; - [NotNull] - public string LastAccessTime => Stat.AccessTime == default(DateTime) ? "" : $"{Stat.AccessTime:G}"; - [NotNull] - public string ChangedTime => Stat.StatusChangeTime == default(DateTime) ? "" : $"{Stat.StatusChangeTime:G}"; - [NotNull] - public string LastBackupTime => Stat.BackupTime == default(DateTime) ? "" : $"{Stat.BackupTime:G}"; - [NotNull] - public string LastWriteTime => Stat.LastWriteTime == default(DateTime) ? "" : $"{Stat.LastWriteTime:G}"; - [NotNull] - public string Attributes => $"{Stat.Attributes}"; - [NotNull] - public string Gid => $"{Stat.GID}"; - [NotNull] - public string Uid => $"{Stat.UID}"; - [NotNull] - public string Inode => $"{Stat.Inode}"; - [NotNull] - public string Links => $"{Stat.Links}"; - [NotNull] - public string Mode => $"{Stat.Mode}"; - public FileEntryInfo Stat { get; set; } - } + public string Name { get; set; } + [NotNull] + public string Size => $"{Stat.Length}"; + [NotNull] + public string CreationTime => Stat.CreationTime == default(DateTime) ? "" : $"{Stat.CreationTime:G}"; + [NotNull] + public string LastAccessTime => Stat.AccessTime == default(DateTime) ? "" : $"{Stat.AccessTime:G}"; + [NotNull] + public string ChangedTime => Stat.StatusChangeTime == default(DateTime) ? "" : $"{Stat.StatusChangeTime:G}"; + [NotNull] + public string LastBackupTime => Stat.BackupTime == default(DateTime) ? "" : $"{Stat.BackupTime:G}"; + [NotNull] + public string LastWriteTime => Stat.LastWriteTime == default(DateTime) ? "" : $"{Stat.LastWriteTime:G}"; + [NotNull] + public string Attributes => $"{Stat.Attributes}"; + [NotNull] + public string Gid => $"{Stat.GID}"; + [NotNull] + public string Uid => $"{Stat.UID}"; + [NotNull] + public string Inode => $"{Stat.Inode}"; + [NotNull] + public string Links => $"{Stat.Links}"; + [NotNull] + public string Mode => $"{Stat.Mode}"; + public FileEntryInfo Stat { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/FileSystemModel.cs b/Aaru.Gui/Models/FileSystemModel.cs index 0c9638fa8..f3e636ee7 100644 --- a/Aaru.Gui/Models/FileSystemModel.cs +++ b/Aaru.Gui/Models/FileSystemModel.cs @@ -34,16 +34,15 @@ using System.Collections.ObjectModel; using Aaru.CommonTypes.Interfaces; using Aaru.Gui.ViewModels.Panels; -namespace Aaru.Gui.Models -{ - public sealed class FileSystemModel : RootModel - { - public FileSystemModel() => Roots = new ObservableCollection(); +namespace Aaru.Gui.Models; - public string VolumeName { get; set; } - public IFilesystem Filesystem { get; set; } - public IReadOnlyFilesystem ReadOnlyFilesystem { get; set; } - public FileSystemViewModel ViewModel { get; set; } - public ObservableCollection Roots { get; set; } - } +public sealed class FileSystemModel : RootModel +{ + public FileSystemModel() => Roots = new ObservableCollection(); + + public string VolumeName { get; set; } + public IFilesystem Filesystem { get; set; } + public IReadOnlyFilesystem ReadOnlyFilesystem { get; set; } + public FileSystemViewModel ViewModel { get; set; } + public ObservableCollection Roots { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/ImageModel.cs b/Aaru.Gui/Models/ImageModel.cs index 60178a7c9..86732d980 100644 --- a/Aaru.Gui/Models/ImageModel.cs +++ b/Aaru.Gui/Models/ImageModel.cs @@ -35,18 +35,17 @@ using Aaru.CommonTypes.Interfaces; using Aaru.Gui.ViewModels.Panels; using Avalonia.Media.Imaging; -namespace Aaru.Gui.Models -{ - public sealed class ImageModel - { - public ImageModel() => PartitionSchemesOrFileSystems = new ObservableCollection(); +namespace Aaru.Gui.Models; - public string Path { get; set; } - public string FileName { get; set; } - public Bitmap Icon { get; set; } - public ObservableCollection PartitionSchemesOrFileSystems { get; } - public IMediaImage Image { get; set; } - public ImageInfoViewModel ViewModel { get; set; } - public IFilter Filter { get; set; } - } +public sealed class ImageModel +{ + public ImageModel() => PartitionSchemesOrFileSystems = new ObservableCollection(); + + public string Path { get; set; } + public string FileName { get; set; } + public Bitmap Icon { get; set; } + public ObservableCollection PartitionSchemesOrFileSystems { get; } + public IMediaImage Image { get; set; } + public ImageInfoViewModel ViewModel { get; set; } + public IFilter Filter { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/ImagePluginModel.cs b/Aaru.Gui/Models/ImagePluginModel.cs index e665cfca3..200528554 100644 --- a/Aaru.Gui/Models/ImagePluginModel.cs +++ b/Aaru.Gui/Models/ImagePluginModel.cs @@ -32,11 +32,10 @@ using Aaru.CommonTypes.Interfaces; -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class ImagePluginModel { - public sealed class ImagePluginModel - { - public string Name => Plugin.Name; - public IWritableImage Plugin { get; set; } - } + public string Name => Plugin.Name; + public IWritableImage Plugin { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/ImagesRootModel.cs b/Aaru.Gui/Models/ImagesRootModel.cs index cf47f0709..bcddbd33e 100644 --- a/Aaru.Gui/Models/ImagesRootModel.cs +++ b/Aaru.Gui/Models/ImagesRootModel.cs @@ -32,12 +32,11 @@ using System.Collections.ObjectModel; -namespace Aaru.Gui.Models -{ - public sealed class ImagesRootModel : RootModel - { - public ImagesRootModel() => Images = new ObservableCollection(); +namespace Aaru.Gui.Models; - public ObservableCollection Images { get; } - } +public sealed class ImagesRootModel : RootModel +{ + public ImagesRootModel() => Images = new ObservableCollection(); + + public ObservableCollection Images { get; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/IsrcModel.cs b/Aaru.Gui/Models/IsrcModel.cs index 18d00888e..60ac1029c 100644 --- a/Aaru.Gui/Models/IsrcModel.cs +++ b/Aaru.Gui/Models/IsrcModel.cs @@ -30,11 +30,10 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class IsrcModel { - public sealed class IsrcModel - { - public string Track { get; set; } - public string Isrc { get; set; } - } + public string Track { get; set; } + public string Isrc { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/LbaModel.cs b/Aaru.Gui/Models/LbaModel.cs index 27f850ecd..e7a449cc7 100644 --- a/Aaru.Gui/Models/LbaModel.cs +++ b/Aaru.Gui/Models/LbaModel.cs @@ -30,10 +30,9 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class LbaModel { - public sealed class LbaModel - { - public string Lba { get; set; } - } + public string Lba { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/MediaModel.cs b/Aaru.Gui/Models/MediaModel.cs index 40e47c11e..c2fea59f1 100644 --- a/Aaru.Gui/Models/MediaModel.cs +++ b/Aaru.Gui/Models/MediaModel.cs @@ -33,15 +33,14 @@ using Aaru.Gui.ViewModels.Panels; using Avalonia.Media.Imaging; -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class MediaModel { - public sealed class MediaModel - { - public Bitmap Icon { get; set; } - public string Name { get; set; } - public string DevicePath { get; set; } - public bool NonRemovable { get; set; } - public bool NoMediaInserted { get; set; } - public MediaInfoViewModel ViewModel { get; set; } - } + public Bitmap Icon { get; set; } + public string Name { get; set; } + public string DevicePath { get; set; } + public bool NonRemovable { get; set; } + public bool NoMediaInserted { get; set; } + public MediaInfoViewModel ViewModel { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/MediaStatsModel.cs b/Aaru.Gui/Models/MediaStatsModel.cs index df51a0e84..9fdaf2ba8 100644 --- a/Aaru.Gui/Models/MediaStatsModel.cs +++ b/Aaru.Gui/Models/MediaStatsModel.cs @@ -30,12 +30,11 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class MediaStatsModel { - public sealed class MediaStatsModel - { - public string Name { get; set; } - public ulong Count { get; set; } - public string Type { get; set; } - } + public string Name { get; set; } + public ulong Count { get; set; } + public string Type { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/MediaTagModel.cs b/Aaru.Gui/Models/MediaTagModel.cs index 57c31bd7b..6edc6ca5a 100644 --- a/Aaru.Gui/Models/MediaTagModel.cs +++ b/Aaru.Gui/Models/MediaTagModel.cs @@ -33,14 +33,13 @@ using Aaru.CommonTypes.Enums; using JetBrains.Annotations; -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class MediaTagModel { - public sealed class MediaTagModel - { - public MediaTagType Tag { get; set; } - public byte[] Data { get; set; } - public string Decoded { get; set; } - [NotNull] - public string Name => Tag.ToString(); - } + public MediaTagType Tag { get; set; } + public byte[] Data { get; set; } + public string Decoded { get; set; } + [NotNull] + public string Name => Tag.ToString(); } \ No newline at end of file diff --git a/Aaru.Gui/Models/NameCountModel.cs b/Aaru.Gui/Models/NameCountModel.cs index 73e561a32..36691091e 100644 --- a/Aaru.Gui/Models/NameCountModel.cs +++ b/Aaru.Gui/Models/NameCountModel.cs @@ -30,11 +30,10 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class NameCountModel { - public sealed class NameCountModel - { - public string Name { get; set; } - public ulong Count { get; set; } - } + public string Name { get; set; } + public ulong Count { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/PartitionModel.cs b/Aaru.Gui/Models/PartitionModel.cs index 5af445f10..492809801 100644 --- a/Aaru.Gui/Models/PartitionModel.cs +++ b/Aaru.Gui/Models/PartitionModel.cs @@ -35,16 +35,15 @@ using Aaru.CommonTypes; using Aaru.Gui.ViewModels.Panels; using Avalonia.Media.Imaging; -namespace Aaru.Gui.Models -{ - public sealed class PartitionModel - { - public PartitionModel() => FileSystems = new ObservableCollection(); +namespace Aaru.Gui.Models; - public string Name { get; set; } - public Bitmap Icon { get; set; } - public ObservableCollection FileSystems { get; } - public Partition Partition { get; set; } - public PartitionViewModel ViewModel { get; set; } - } +public sealed class PartitionModel +{ + public PartitionModel() => FileSystems = new ObservableCollection(); + + public string Name { get; set; } + public Bitmap Icon { get; set; } + public ObservableCollection FileSystems { get; } + public Partition Partition { get; set; } + public PartitionViewModel ViewModel { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/PartitionSchemeModel.cs b/Aaru.Gui/Models/PartitionSchemeModel.cs index b374336e1..67a395471 100644 --- a/Aaru.Gui/Models/PartitionSchemeModel.cs +++ b/Aaru.Gui/Models/PartitionSchemeModel.cs @@ -33,13 +33,12 @@ using System.Collections.ObjectModel; using Avalonia.Media.Imaging; -namespace Aaru.Gui.Models -{ - public sealed class PartitionSchemeModel : RootModel - { - public PartitionSchemeModel() => Partitions = new ObservableCollection(); +namespace Aaru.Gui.Models; - public Bitmap Icon { get; set; } - public ObservableCollection Partitions { get; } - } +public sealed class PartitionSchemeModel : RootModel +{ + public PartitionSchemeModel() => Partitions = new ObservableCollection(); + + public Bitmap Icon { get; set; } + public ObservableCollection Partitions { get; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/PcmciaCisModel.cs b/Aaru.Gui/Models/PcmciaCisModel.cs index b5c4a823f..020b93bf4 100644 --- a/Aaru.Gui/Models/PcmciaCisModel.cs +++ b/Aaru.Gui/Models/PcmciaCisModel.cs @@ -30,11 +30,10 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class PcmciaCisModel { - public sealed class PcmciaCisModel - { - public string Code { get; set; } - public string Description { get; set; } - } + public string Code { get; set; } + public string Description { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/PluginModel.cs b/Aaru.Gui/Models/PluginModel.cs index c85827daa..8bcd2183e 100644 --- a/Aaru.Gui/Models/PluginModel.cs +++ b/Aaru.Gui/Models/PluginModel.cs @@ -32,13 +32,12 @@ using System; -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class PluginModel { - public sealed class PluginModel - { - public string Name { get; set; } - public Guid Uuid { get; set; } - public string Version { get; set; } - public string Author { get; set; } - } + public string Name { get; set; } + public Guid Uuid { get; set; } + public string Version { get; set; } + public string Author { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/RootModel.cs b/Aaru.Gui/Models/RootModel.cs index 920fefae0..32516ac3e 100644 --- a/Aaru.Gui/Models/RootModel.cs +++ b/Aaru.Gui/Models/RootModel.cs @@ -30,10 +30,9 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public class RootModel { - public class RootModel - { - public string Name { get; set; } - } + public string Name { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/ScsiPageModel.cs b/Aaru.Gui/Models/ScsiPageModel.cs index d3e9e2a1e..5193eb256 100644 --- a/Aaru.Gui/Models/ScsiPageModel.cs +++ b/Aaru.Gui/Models/ScsiPageModel.cs @@ -30,12 +30,11 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class ScsiPageModel { - public sealed class ScsiPageModel - { - public byte[] Data; - public string Page { get; set; } - public string Description { get; set; } - } + public byte[] Data; + public string Page { get; set; } + public string Description { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/SubdirectoryModel.cs b/Aaru.Gui/Models/SubdirectoryModel.cs index 85d357e96..2a086c656 100644 --- a/Aaru.Gui/Models/SubdirectoryModel.cs +++ b/Aaru.Gui/Models/SubdirectoryModel.cs @@ -33,16 +33,15 @@ using System.Collections.ObjectModel; using Aaru.CommonTypes.Interfaces; -namespace Aaru.Gui.Models -{ - public sealed class SubdirectoryModel - { - public SubdirectoryModel() => Subdirectories = new ObservableCollection(); +namespace Aaru.Gui.Models; - public string Name { get; set; } - public string Path { get; set; } - public ObservableCollection Subdirectories { get; set; } - public IReadOnlyFilesystem Plugin { get; set; } - public bool Listed { get; set; } - } +public sealed class SubdirectoryModel +{ + public SubdirectoryModel() => Subdirectories = new ObservableCollection(); + + public string Name { get; set; } + public string Path { get; set; } + public ObservableCollection Subdirectories { get; set; } + public IReadOnlyFilesystem Plugin { get; set; } + public bool Listed { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/Models/TrackEntropyModel.cs b/Aaru.Gui/Models/TrackEntropyModel.cs index 6665beda5..4a0872f30 100644 --- a/Aaru.Gui/Models/TrackEntropyModel.cs +++ b/Aaru.Gui/Models/TrackEntropyModel.cs @@ -30,12 +30,11 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.Gui.Models +namespace Aaru.Gui.Models; + +public sealed class TrackEntropyModel { - public sealed class TrackEntropyModel - { - public string Track { get; set; } - public string Entropy { get; set; } - public string UniqueSectors { get; set; } - } + public string Track { get; set; } + public string Entropy { get; set; } + public string UniqueSectors { get; set; } } \ No newline at end of file diff --git a/Aaru.Gui/ResourceHandler.cs b/Aaru.Gui/ResourceHandler.cs index af48c6a8c..f8d83c739 100644 --- a/Aaru.Gui/ResourceHandler.cs +++ b/Aaru.Gui/ResourceHandler.cs @@ -34,12 +34,11 @@ using System.IO; using System.Reflection; using JetBrains.Annotations; -namespace Aaru.Gui +namespace Aaru.Gui; + +internal static class ResourceHandler { - internal static class ResourceHandler - { - [CanBeNull] - internal static Stream GetResourceStream([NotNull] string resourcePath) => - Assembly.GetExecutingAssembly().GetManifestResourceStream(resourcePath); - } + [CanBeNull] + internal static Stream GetResourceStream([NotNull] string resourcePath) => + Assembly.GetExecutingAssembly().GetManifestResourceStream(resourcePath); } \ No newline at end of file diff --git a/Aaru.Gui/ViewLocator.cs b/Aaru.Gui/ViewLocator.cs index 6fc89b261..31d1fe79a 100644 --- a/Aaru.Gui/ViewLocator.cs +++ b/Aaru.Gui/ViewLocator.cs @@ -36,33 +36,32 @@ using Avalonia.Controls; using Avalonia.Controls.Templates; using JetBrains.Annotations; -namespace Aaru.Gui +namespace Aaru.Gui; + +public sealed class ViewLocator : IDataTemplate { - public sealed class ViewLocator : IDataTemplate + public bool SupportsRecycling => false; + + [CanBeNull] + public IControl Build([NotNull] object data) { - public bool SupportsRecycling => false; + string name = data.GetType().FullName?.Replace("ViewModel", "View"); - [CanBeNull] - public IControl Build([NotNull] object data) + if(name is null) + return null; + + var type = Type.GetType(name); + + if(type != null) { - string name = data.GetType().FullName?.Replace("ViewModel", "View"); - - if(name is null) - return null; - - var type = Type.GetType(name); - - if(type != null) - { - return (Control)Activator.CreateInstance(type); - } - - return new TextBlock - { - Text = "Not Found: " + name - }; + return (Control)Activator.CreateInstance(type); } - public bool Match(object data) => data is ViewModelBase; + return new TextBlock + { + Text = "Not Found: " + name + }; } + + public bool Match(object data) => data is ViewModelBase; } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Dialogs/AboutViewModel.cs b/Aaru.Gui/ViewModels/Dialogs/AboutViewModel.cs index ec8f40f13..0b56adb0b 100644 --- a/Aaru.Gui/ViewModels/Dialogs/AboutViewModel.cs +++ b/Aaru.Gui/ViewModels/Dialogs/AboutViewModel.cs @@ -43,76 +43,76 @@ using Aaru.Gui.Views.Dialogs; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Dialogs +namespace Aaru.Gui.ViewModels.Dialogs; + +public sealed class AboutViewModel : ViewModelBase { - public sealed class AboutViewModel : ViewModelBase + readonly About _view; + string _versionText; + + public AboutViewModel(About view) { - readonly About _view; - string _versionText; + _view = view; - public AboutViewModel(About view) + VersionText = + (Attribute.GetCustomAttribute(typeof(App).Assembly, typeof(AssemblyInformationalVersionAttribute)) as + AssemblyInformationalVersionAttribute)?.InformationalVersion; + + WebsiteCommand = ReactiveCommand.Create(ExecuteWebsiteCommand); + LicenseCommand = ReactiveCommand.Create(ExecuteLicenseCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + + Assemblies = new ObservableCollection(); + + Task.Run(() => { - _view = view; - - VersionText = - (Attribute.GetCustomAttribute(typeof(App).Assembly, typeof(AssemblyInformationalVersionAttribute)) as - AssemblyInformationalVersionAttribute)?.InformationalVersion; - - WebsiteCommand = ReactiveCommand.Create(ExecuteWebsiteCommand); - LicenseCommand = ReactiveCommand.Create(ExecuteLicenseCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - - Assemblies = new ObservableCollection(); - - Task.Run(() => + foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies().OrderBy(a => a.FullName)) { - foreach(Assembly assembly in AppDomain.CurrentDomain.GetAssemblies().OrderBy(a => a.FullName)) + string name = assembly.GetName().Name; + + string version = + (Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) as + AssemblyInformationalVersionAttribute)?.InformationalVersion; + + if(name is null || + version is null) + continue; + + Assemblies.Add(new AssemblyModel { - string name = assembly.GetName().Name; + Name = name, + Version = version + }); + } + }); + } - string version = - (Attribute.GetCustomAttribute(assembly, typeof(AssemblyInformationalVersionAttribute)) as - AssemblyInformationalVersionAttribute)?.InformationalVersion; - - if(name is null || - version is null) - continue; - - Assemblies.Add(new AssemblyModel - { - Name = name, - Version = version - }); - } - }); - } - - [NotNull] - public string AboutLabel => "About"; - [NotNull] - public string LibrariesLabel => "Libraries"; - [NotNull] - public string AuthorsLabel => "Authors"; - [NotNull] - public string Title => "About Aaru"; - [NotNull] - public string SoftwareName => "Aaru"; - [NotNull] - public string SuiteName => "Aaru Data Preservation Suite"; - [NotNull] - public string Copyright => "© 2011-2022 Natalia Portillo"; - [NotNull] - public string Website => "https://aaru.app"; - [NotNull] - public string License => "License: GNU General Public License Version 3"; - [NotNull] - public string CloseLabel => "Close"; - [NotNull] - public string AssembliesLibraryText => "Library"; - [NotNull] - public string AssembliesVersionText => "Version"; - [NotNull] - public string Authors => @"Developers: + [NotNull] + public string AboutLabel => "About"; + [NotNull] + public string LibrariesLabel => "Libraries"; + [NotNull] + public string AuthorsLabel => "Authors"; + [NotNull] + public string Title => "About Aaru"; + [NotNull] + public string SoftwareName => "Aaru"; + [NotNull] + public string SuiteName => "Aaru Data Preservation Suite"; + [NotNull] + public string Copyright => "© 2011-2022 Natalia Portillo"; + [NotNull] + public string Website => "https://aaru.app"; + [NotNull] + public string License => "License: GNU General Public License Version 3"; + [NotNull] + public string CloseLabel => "Close"; + [NotNull] + public string AssembliesLibraryText => "Library"; + [NotNull] + public string AssembliesVersionText => "Version"; + [NotNull] + public string Authors => @"Developers: Natalia Portillo Michael Drüing Rebecca Wallander @@ -125,51 +125,50 @@ Public relations: Logo and art: Juan Carlos Pastor Segura (Denymetanol)"; - public ReactiveCommand WebsiteCommand { get; } - public ReactiveCommand LicenseCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ObservableCollection Assemblies { get; } + public ReactiveCommand WebsiteCommand { get; } + public ReactiveCommand LicenseCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ObservableCollection Assemblies { get; } - public string VersionText - { - get => _versionText; - set => this.RaiseAndSetIfChanged(ref _versionText, value); - } - - void ExecuteWebsiteCommand() - { - var process = new Process - { - StartInfo = - { - UseShellExecute = false, - CreateNoWindow = true, - Arguments = "https://aaru.app" - } - }; - - if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - process.StartInfo.FileName = "cmd"; - process.StartInfo.Arguments = $"/c start {process.StartInfo.Arguments.Replace("&", "^&")}"; - } - else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) - process.StartInfo.FileName = "xdg-open"; - else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) - process.StartInfo.FileName = "open"; - else - return; - - process.Start(); - } - - void ExecuteLicenseCommand() - { - var dialog = new LicenseDialog(); - dialog.DataContext = new LicenseViewModel(dialog); - dialog.ShowDialog(_view); - } - - void ExecuteCloseCommand() => _view.Close(); + public string VersionText + { + get => _versionText; + set => this.RaiseAndSetIfChanged(ref _versionText, value); } + + void ExecuteWebsiteCommand() + { + var process = new Process + { + StartInfo = + { + UseShellExecute = false, + CreateNoWindow = true, + Arguments = "https://aaru.app" + } + }; + + if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + process.StartInfo.FileName = "cmd"; + process.StartInfo.Arguments = $"/c start {process.StartInfo.Arguments.Replace("&", "^&")}"; + } + else if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + process.StartInfo.FileName = "xdg-open"; + else if(RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + process.StartInfo.FileName = "open"; + else + return; + + process.Start(); + } + + void ExecuteLicenseCommand() + { + var dialog = new LicenseDialog(); + dialog.DataContext = new LicenseViewModel(dialog); + dialog.ShowDialog(_view); + } + + void ExecuteCloseCommand() => _view.Close(); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Dialogs/ConsoleViewModel.cs b/Aaru.Gui/ViewModels/Dialogs/ConsoleViewModel.cs index 14aba4120..598e3baad 100644 --- a/Aaru.Gui/ViewModels/Dialogs/ConsoleViewModel.cs +++ b/Aaru.Gui/ViewModels/Dialogs/ConsoleViewModel.cs @@ -46,116 +46,115 @@ using ReactiveUI; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; using Version = Aaru.CommonTypes.Interop.Version; -namespace Aaru.Gui.ViewModels.Dialogs +namespace Aaru.Gui.ViewModels.Dialogs; + +public sealed class ConsoleViewModel : ViewModelBase { - public sealed class ConsoleViewModel : ViewModelBase + readonly Views.Dialogs.Console _view; + bool _debugChecked; + + public ConsoleViewModel(Views.Dialogs.Console view) { - readonly Views.Dialogs.Console _view; - bool _debugChecked; - - public ConsoleViewModel(Views.Dialogs.Console view) - { - _view = view; - SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); - ClearCommand = ReactiveCommand.Create(ExecuteClearCommand); - } - - [NotNull] - public string Title => "Console"; - public ReactiveCommand ClearCommand { get; } - public ReactiveCommand SaveCommand { get; } - public ObservableCollection Entries => ConsoleHandler.Entries; - [NotNull] - public string DebugText => "Enable debug console"; - [NotNull] - public string SaveLabel => "Save"; - [NotNull] - public string ClearLabel => "Clear"; - - public bool DebugChecked - { - get => _debugChecked; - set - { - ConsoleHandler.Debug = value; - this.RaiseAndSetIfChanged(ref _debugChecked, value); - } - } - - async void ExecuteSaveCommand() - { - var dlgSave = new SaveFileDialog(); - - dlgSave.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "log" - }), - Name = "Log files" - }); - - string result = await dlgSave.ShowAsync(_view); - - if(result is null) - return; - - try - { - var logFs = new FileStream(result, FileMode.Create, FileAccess.ReadWrite); - var logSw = new StreamWriter(logFs); - - logSw.WriteLine("Log saved at {0}", DateTime.Now); - - PlatformID platId = DetectOS.GetRealPlatformID(); - string platVer = DetectOS.GetVersion(); - - var assemblyVersion = - Attribute.GetCustomAttribute(typeof(AaruConsole).Assembly, - typeof(AssemblyInformationalVersionAttribute)) as - AssemblyInformationalVersionAttribute; - - logSw.WriteLine("################# System information #################"); - - logSw.WriteLine("{0} {1} ({2}-bit)", DetectOS.GetPlatformName(platId, platVer), platVer, - Environment.Is64BitOperatingSystem ? 64 : 32); - - logSw.WriteLine(".NET Core {0}", Version.GetNetCoreVersion()); - - logSw.WriteLine(); - - logSw.WriteLine("################# Program information ################"); - logSw.WriteLine("Aaru {0}", assemblyVersion?.InformationalVersion); - logSw.WriteLine("Running in {0}-bit", Environment.Is64BitProcess ? 64 : 32); - #if DEBUG - logSw.WriteLine("DEBUG version"); - #endif - logSw.WriteLine("Command line: {0}", Environment.CommandLine); - logSw.WriteLine(); - - logSw.WriteLine("################# Console ################"); - - foreach(LogEntry entry in ConsoleHandler.Entries) - if(entry.Type != "Info") - logSw.WriteLine("{0}: ({1}) {2}", entry.Timestamp, entry.Type.ToLower(), entry.Message); - else - logSw.WriteLine("{0}: {1}", entry.Timestamp, entry.Message); - - logSw.Close(); - logFs.Close(); - } - catch(Exception exception) - { - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Exception {exception.Message} trying to save logfile, details has been sent to console.", - ButtonEnum.Ok, Icon.Error).ShowDialog(_view); - - AaruConsole.ErrorWriteLine("Console", exception.Message); - AaruConsole.ErrorWriteLine("Console", exception.StackTrace); - } - } - - void ExecuteClearCommand() => ConsoleHandler.Entries.Clear(); + _view = view; + SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); + ClearCommand = ReactiveCommand.Create(ExecuteClearCommand); } + + [NotNull] + public string Title => "Console"; + public ReactiveCommand ClearCommand { get; } + public ReactiveCommand SaveCommand { get; } + public ObservableCollection Entries => ConsoleHandler.Entries; + [NotNull] + public string DebugText => "Enable debug console"; + [NotNull] + public string SaveLabel => "Save"; + [NotNull] + public string ClearLabel => "Clear"; + + public bool DebugChecked + { + get => _debugChecked; + set + { + ConsoleHandler.Debug = value; + this.RaiseAndSetIfChanged(ref _debugChecked, value); + } + } + + async void ExecuteSaveCommand() + { + var dlgSave = new SaveFileDialog(); + + dlgSave.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "log" + }), + Name = "Log files" + }); + + string result = await dlgSave.ShowAsync(_view); + + if(result is null) + return; + + try + { + var logFs = new FileStream(result, FileMode.Create, FileAccess.ReadWrite); + var logSw = new StreamWriter(logFs); + + logSw.WriteLine("Log saved at {0}", DateTime.Now); + + PlatformID platId = DetectOS.GetRealPlatformID(); + string platVer = DetectOS.GetVersion(); + + var assemblyVersion = + Attribute.GetCustomAttribute(typeof(AaruConsole).Assembly, + typeof(AssemblyInformationalVersionAttribute)) as + AssemblyInformationalVersionAttribute; + + logSw.WriteLine("################# System information #################"); + + logSw.WriteLine("{0} {1} ({2}-bit)", DetectOS.GetPlatformName(platId, platVer), platVer, + Environment.Is64BitOperatingSystem ? 64 : 32); + + logSw.WriteLine(".NET Core {0}", Version.GetNetCoreVersion()); + + logSw.WriteLine(); + + logSw.WriteLine("################# Program information ################"); + logSw.WriteLine("Aaru {0}", assemblyVersion?.InformationalVersion); + logSw.WriteLine("Running in {0}-bit", Environment.Is64BitProcess ? 64 : 32); + #if DEBUG + logSw.WriteLine("DEBUG version"); + #endif + logSw.WriteLine("Command line: {0}", Environment.CommandLine); + logSw.WriteLine(); + + logSw.WriteLine("################# Console ################"); + + foreach(LogEntry entry in ConsoleHandler.Entries) + if(entry.Type != "Info") + logSw.WriteLine("{0}: ({1}) {2}", entry.Timestamp, entry.Type.ToLower(), entry.Message); + else + logSw.WriteLine("{0}: {1}", entry.Timestamp, entry.Message); + + logSw.Close(); + logFs.Close(); + } + catch(Exception exception) + { + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Exception {exception.Message} trying to save logfile, details has been sent to console.", + ButtonEnum.Ok, Icon.Error).ShowDialog(_view); + + AaruConsole.ErrorWriteLine("Console", exception.Message); + AaruConsole.ErrorWriteLine("Console", exception.StackTrace); + } + } + + void ExecuteClearCommand() => ConsoleHandler.Entries.Clear(); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Dialogs/EncodingsViewModel.cs b/Aaru.Gui/ViewModels/Dialogs/EncodingsViewModel.cs index 0aecbad50..d736d1f99 100644 --- a/Aaru.Gui/ViewModels/Dialogs/EncodingsViewModel.cs +++ b/Aaru.Gui/ViewModels/Dialogs/EncodingsViewModel.cs @@ -41,44 +41,43 @@ using Aaru.Gui.Views.Dialogs; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Dialogs +namespace Aaru.Gui.ViewModels.Dialogs; + +public sealed class EncodingsViewModel : ViewModelBase { - public sealed class EncodingsViewModel : ViewModelBase + readonly Encodings _view; + + public EncodingsViewModel(Encodings view) { - readonly Encodings _view; + _view = view; + Encodings = new ObservableCollection(); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - public EncodingsViewModel(Encodings view) + Task.Run(() => { - _view = view; - Encodings = new ObservableCollection(); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - - Task.Run(() => + List encodings = Encoding.GetEncodings().Select(info => new EncodingModel { - List encodings = Encoding.GetEncodings().Select(info => new EncodingModel - { - Name = info.Name, - DisplayName = info.GetEncoding().EncodingName - }).ToList(); + Name = info.Name, + DisplayName = info.GetEncoding().EncodingName + }).ToList(); - encodings.AddRange(Claunia.Encoding.Encoding.GetEncodings().Select(info => new EncodingModel - { - Name = info.Name, - DisplayName = info.DisplayName - })); + encodings.AddRange(Claunia.Encoding.Encoding.GetEncodings().Select(info => new EncodingModel + { + Name = info.Name, + DisplayName = info.DisplayName + })); - foreach(EncodingModel encoding in encodings.OrderBy(t => t.DisplayName)) - Encodings.Add(encoding); - }); - } - - [NotNull] - public string Title => "Encodings"; - [NotNull] - public string CloseLabel => "Close"; - public ReactiveCommand CloseCommand { get; } - public ObservableCollection Encodings { get; } - - void ExecuteCloseCommand() => _view.Close(); + foreach(EncodingModel encoding in encodings.OrderBy(t => t.DisplayName)) + Encodings.Add(encoding); + }); } + + [NotNull] + public string Title => "Encodings"; + [NotNull] + public string CloseLabel => "Close"; + public ReactiveCommand CloseCommand { get; } + public ObservableCollection Encodings { get; } + + void ExecuteCloseCommand() => _view.Close(); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Dialogs/LicenseViewModel.cs b/Aaru.Gui/ViewModels/Dialogs/LicenseViewModel.cs index 3a9c95bc7..d299df772 100644 --- a/Aaru.Gui/ViewModels/Dialogs/LicenseViewModel.cs +++ b/Aaru.Gui/ViewModels/Dialogs/LicenseViewModel.cs @@ -37,35 +37,34 @@ using Aaru.Gui.Views.Dialogs; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Dialogs +namespace Aaru.Gui.ViewModels.Dialogs; + +public sealed class LicenseViewModel : ViewModelBase { - public sealed class LicenseViewModel : ViewModelBase + readonly LicenseDialog _view; + string _versionText; + + public LicenseViewModel(LicenseDialog view) { - readonly LicenseDialog _view; - string _versionText; + _view = view; + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - public LicenseViewModel(LicenseDialog view) - { - _view = view; - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Aaru.Gui.LICENSE"); - using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("Aaru.Gui.LICENSE"); + if(stream == null) + return; - if(stream == null) - return; + using var reader = new StreamReader(stream); - using var reader = new StreamReader(stream); - - LicenseText = reader.ReadToEnd(); - } - - [NotNull] - public string Title => "Aaru's license"; - [NotNull] - public string CloseLabel => "Close"; - public string LicenseText { get; } - public ReactiveCommand CloseCommand { get; } - - void ExecuteCloseCommand() => _view.Close(); + LicenseText = reader.ReadToEnd(); } + + [NotNull] + public string Title => "Aaru's license"; + [NotNull] + public string CloseLabel => "Close"; + public string LicenseText { get; } + public ReactiveCommand CloseCommand { get; } + + void ExecuteCloseCommand() => _view.Close(); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Dialogs/PluginsViewModel.cs b/Aaru.Gui/ViewModels/Dialogs/PluginsViewModel.cs index d9ee15a8e..46cfe13e8 100644 --- a/Aaru.Gui/ViewModels/Dialogs/PluginsViewModel.cs +++ b/Aaru.Gui/ViewModels/Dialogs/PluginsViewModel.cs @@ -40,129 +40,128 @@ using Aaru.Gui.Views.Dialogs; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Dialogs +namespace Aaru.Gui.ViewModels.Dialogs; + +public sealed class PluginsViewModel : ViewModelBase { - public sealed class PluginsViewModel : ViewModelBase + readonly PluginsDialog _view; + + public PluginsViewModel(PluginsDialog view) { - readonly PluginsDialog _view; + _view = view; + Filters = new ObservableCollection(); + PartitionSchemes = new ObservableCollection(); + Filesystems = new ObservableCollection(); + ReadOnlyFilesystems = new ObservableCollection(); + Images = new ObservableCollection(); + WritableImages = new ObservableCollection(); + FloppyImages = new ObservableCollection(); + WritableFloppyImages = new ObservableCollection(); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - public PluginsViewModel(PluginsDialog view) - { - _view = view; - Filters = new ObservableCollection(); - PartitionSchemes = new ObservableCollection(); - Filesystems = new ObservableCollection(); - ReadOnlyFilesystems = new ObservableCollection(); - Images = new ObservableCollection(); - WritableImages = new ObservableCollection(); - FloppyImages = new ObservableCollection(); - WritableFloppyImages = new ObservableCollection(); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + // TODO: Takes too much time + foreach(IFilter filter in GetPluginBase.Instance.Filters.Values) + Filters.Add(new PluginModel + { + Name = filter.Name, + Uuid = filter.Id, + Version = Assembly.GetAssembly(filter.GetType())?.GetName().Version?.ToString(), + Author = filter.Author + }); - // TODO: Takes too much time - foreach(IFilter filter in GetPluginBase.Instance.Filters.Values) - Filters.Add(new PluginModel - { - Name = filter.Name, - Uuid = filter.Id, - Version = Assembly.GetAssembly(filter.GetType())?.GetName().Version?.ToString(), - Author = filter.Author - }); + foreach(IFloppyImage floppyImage in GetPluginBase.Instance.FloppyImages.Values) + FloppyImages.Add(new PluginModel + { + Name = floppyImage.Name, + Uuid = floppyImage.Id, + Version = Assembly.GetAssembly(floppyImage.GetType())?.GetName().Version?.ToString(), + Author = floppyImage.Author + }); - foreach(IFloppyImage floppyImage in GetPluginBase.Instance.FloppyImages.Values) - FloppyImages.Add(new PluginModel - { - Name = floppyImage.Name, - Uuid = floppyImage.Id, - Version = Assembly.GetAssembly(floppyImage.GetType())?.GetName().Version?.ToString(), - Author = floppyImage.Author - }); + foreach(IMediaImage mediaImage in GetPluginBase.Instance.ImagePluginsList.Values) + Images.Add(new PluginModel + { + Name = mediaImage.Name, + Uuid = mediaImage.Id, + Version = Assembly.GetAssembly(mediaImage.GetType())?.GetName().Version?.ToString(), + Author = mediaImage.Author + }); - foreach(IMediaImage mediaImage in GetPluginBase.Instance.ImagePluginsList.Values) - Images.Add(new PluginModel - { - Name = mediaImage.Name, - Uuid = mediaImage.Id, - Version = Assembly.GetAssembly(mediaImage.GetType())?.GetName().Version?.ToString(), - Author = mediaImage.Author - }); + foreach(IPartition partition in GetPluginBase.Instance.PartPluginsList.Values) + PartitionSchemes.Add(new PluginModel + { + Name = partition.Name, + Uuid = partition.Id, + Version = Assembly.GetAssembly(partition.GetType())?.GetName().Version?.ToString(), + Author = partition.Author + }); - foreach(IPartition partition in GetPluginBase.Instance.PartPluginsList.Values) - PartitionSchemes.Add(new PluginModel - { - Name = partition.Name, - Uuid = partition.Id, - Version = Assembly.GetAssembly(partition.GetType())?.GetName().Version?.ToString(), - Author = partition.Author - }); + foreach(IFilesystem filesystem in GetPluginBase.Instance.PluginsList.Values) + Filesystems.Add(new PluginModel + { + Name = filesystem.Name, + Uuid = filesystem.Id, + Version = Assembly.GetAssembly(filesystem.GetType())?.GetName().Version?.ToString(), + Author = filesystem.Author + }); - foreach(IFilesystem filesystem in GetPluginBase.Instance.PluginsList.Values) - Filesystems.Add(new PluginModel - { - Name = filesystem.Name, - Uuid = filesystem.Id, - Version = Assembly.GetAssembly(filesystem.GetType())?.GetName().Version?.ToString(), - Author = filesystem.Author - }); + foreach(IReadOnlyFilesystem readOnlyFilesystem in GetPluginBase.Instance.ReadOnlyFilesystems.Values) + ReadOnlyFilesystems.Add(new PluginModel + { + Name = readOnlyFilesystem.Name, + Uuid = readOnlyFilesystem.Id, + Version = Assembly.GetAssembly(readOnlyFilesystem.GetType())?.GetName().Version?.ToString(), + Author = readOnlyFilesystem.Author + }); - foreach(IReadOnlyFilesystem readOnlyFilesystem in GetPluginBase.Instance.ReadOnlyFilesystems.Values) - ReadOnlyFilesystems.Add(new PluginModel - { - Name = readOnlyFilesystem.Name, - Uuid = readOnlyFilesystem.Id, - Version = Assembly.GetAssembly(readOnlyFilesystem.GetType())?.GetName().Version?.ToString(), - Author = readOnlyFilesystem.Author - }); + foreach(IWritableFloppyImage writableFloppyImage in GetPluginBase.Instance.WritableFloppyImages.Values) + WritableFloppyImages.Add(new PluginModel + { + Name = writableFloppyImage.Name, + Uuid = writableFloppyImage.Id, + Version = Assembly.GetAssembly(writableFloppyImage.GetType())?.GetName().Version?.ToString(), + Author = writableFloppyImage.Author + }); - foreach(IWritableFloppyImage writableFloppyImage in GetPluginBase.Instance.WritableFloppyImages.Values) - WritableFloppyImages.Add(new PluginModel - { - Name = writableFloppyImage.Name, - Uuid = writableFloppyImage.Id, - Version = Assembly.GetAssembly(writableFloppyImage.GetType())?.GetName().Version?.ToString(), - Author = writableFloppyImage.Author - }); - - foreach(IWritableImage writableImage in GetPluginBase.Instance.WritableImages.Values) - WritableImages.Add(new PluginModel - { - Name = writableImage.Name, - Uuid = writableImage.Id, - Version = Assembly.GetAssembly(writableImage.GetType())?.GetName().Version?.ToString(), - Author = writableImage.Author - }); - } - - [NotNull] - public string Title => "Plugins"; - [NotNull] - public string FiltersLabel => "Filters"; - [NotNull] - public string PartitionsLabel => "Partitions"; - [NotNull] - public string FilesystemsLabel => "Filesystems"; - [NotNull] - public string IdentifyLabel => "Identify only:"; - [NotNull] - public string ImagesLabel => "Media images"; - [NotNull] - public string FloppyImagesLabel => "Floppy images"; - [NotNull] - public string ReadableLabel => "Readable:"; - [NotNull] - public string WritableLabel => "Writable:"; - [NotNull] - public string CloseLabel => "Close"; - public ReactiveCommand CloseCommand { get; } - public ObservableCollection Filters { get; } - public ObservableCollection PartitionSchemes { get; } - public ObservableCollection Filesystems { get; } - public ObservableCollection ReadOnlyFilesystems { get; } - public ObservableCollection Images { get; } - public ObservableCollection WritableImages { get; } - public ObservableCollection FloppyImages { get; } - public ObservableCollection WritableFloppyImages { get; } - - void ExecuteCloseCommand() => _view.Close(); + foreach(IWritableImage writableImage in GetPluginBase.Instance.WritableImages.Values) + WritableImages.Add(new PluginModel + { + Name = writableImage.Name, + Uuid = writableImage.Id, + Version = Assembly.GetAssembly(writableImage.GetType())?.GetName().Version?.ToString(), + Author = writableImage.Author + }); } + + [NotNull] + public string Title => "Plugins"; + [NotNull] + public string FiltersLabel => "Filters"; + [NotNull] + public string PartitionsLabel => "Partitions"; + [NotNull] + public string FilesystemsLabel => "Filesystems"; + [NotNull] + public string IdentifyLabel => "Identify only:"; + [NotNull] + public string ImagesLabel => "Media images"; + [NotNull] + public string FloppyImagesLabel => "Floppy images"; + [NotNull] + public string ReadableLabel => "Readable:"; + [NotNull] + public string WritableLabel => "Writable:"; + [NotNull] + public string CloseLabel => "Close"; + public ReactiveCommand CloseCommand { get; } + public ObservableCollection Filters { get; } + public ObservableCollection PartitionSchemes { get; } + public ObservableCollection Filesystems { get; } + public ObservableCollection ReadOnlyFilesystems { get; } + public ObservableCollection Images { get; } + public ObservableCollection WritableImages { get; } + public ObservableCollection FloppyImages { get; } + public ObservableCollection WritableFloppyImages { get; } + + void ExecuteCloseCommand() => _view.Close(); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Dialogs/SettingsViewModel.cs b/Aaru.Gui/ViewModels/Dialogs/SettingsViewModel.cs index 75f077992..d5ee44a1c 100644 --- a/Aaru.Gui/ViewModels/Dialogs/SettingsViewModel.cs +++ b/Aaru.Gui/ViewModels/Dialogs/SettingsViewModel.cs @@ -36,91 +36,91 @@ using Aaru.Settings; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Dialogs +namespace Aaru.Gui.ViewModels.Dialogs; + +public sealed class SettingsViewModel : ViewModelBase { - public sealed class SettingsViewModel : ViewModelBase + readonly SettingsDialog _view; + bool _commandStatsChecked; + bool _deviceStatsChecked; + bool _filesystemStatsChecked; + bool _filterStatsChecked; + bool _gdprVisible; + bool _mediaImageStatsChecked; + bool _mediaScanStatsChecked; + bool _mediaStatsChecked; + bool _partitionStatsChecked; + bool _saveReportsGloballyChecked; + bool _saveStatsChecked; + bool _shareReportsChecked; + bool _shareStatsChecked; + int _tabControlSelectedIndex; + bool _verifyStatsChecked; + + public SettingsViewModel(SettingsDialog view, bool gdprChange) { - readonly SettingsDialog _view; - bool _commandStatsChecked; - bool _deviceStatsChecked; - bool _filesystemStatsChecked; - bool _filterStatsChecked; - bool _gdprVisible; - bool _mediaImageStatsChecked; - bool _mediaScanStatsChecked; - bool _mediaStatsChecked; - bool _partitionStatsChecked; - bool _saveReportsGloballyChecked; - bool _saveStatsChecked; - bool _shareReportsChecked; - bool _shareStatsChecked; - int _tabControlSelectedIndex; - bool _verifyStatsChecked; + _view = view; + GdprVisible = gdprChange; + SaveReportsGloballyChecked = Settings.Settings.Current.SaveReportsGlobally; + ShareReportsChecked = Settings.Settings.Current.ShareReports; - public SettingsViewModel(SettingsDialog view, bool gdprChange) + if(Settings.Settings.Current.Stats != null) { - _view = view; - GdprVisible = gdprChange; - SaveReportsGloballyChecked = Settings.Settings.Current.SaveReportsGlobally; - ShareReportsChecked = Settings.Settings.Current.ShareReports; - - if(Settings.Settings.Current.Stats != null) - { - SaveStatsChecked = true; - ShareStatsChecked = Settings.Settings.Current.Stats.ShareStats; - CommandStatsChecked = Settings.Settings.Current.Stats.CommandStats; - DeviceStatsChecked = Settings.Settings.Current.Stats.DeviceStats; - FilesystemStatsChecked = Settings.Settings.Current.Stats.FilesystemStats; - FilterStatsChecked = Settings.Settings.Current.Stats.FilterStats; - MediaImageStatsChecked = Settings.Settings.Current.Stats.MediaImageStats; - MediaScanStatsChecked = Settings.Settings.Current.Stats.MediaScanStats; - PartitionStatsChecked = Settings.Settings.Current.Stats.PartitionStats; - MediaStatsChecked = Settings.Settings.Current.Stats.MediaStats; - VerifyStatsChecked = Settings.Settings.Current.Stats.VerifyStats; - } - else - SaveStatsChecked = false; - - CancelCommand = ReactiveCommand.Create(ExecuteCancelCommand); - SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); - - if(!_gdprVisible) - _tabControlSelectedIndex = 1; + SaveStatsChecked = true; + ShareStatsChecked = Settings.Settings.Current.Stats.ShareStats; + CommandStatsChecked = Settings.Settings.Current.Stats.CommandStats; + DeviceStatsChecked = Settings.Settings.Current.Stats.DeviceStats; + FilesystemStatsChecked = Settings.Settings.Current.Stats.FilesystemStats; + FilterStatsChecked = Settings.Settings.Current.Stats.FilterStats; + MediaImageStatsChecked = Settings.Settings.Current.Stats.MediaImageStats; + MediaScanStatsChecked = Settings.Settings.Current.Stats.MediaScanStats; + PartitionStatsChecked = Settings.Settings.Current.Stats.PartitionStats; + MediaStatsChecked = Settings.Settings.Current.Stats.MediaStats; + VerifyStatsChecked = Settings.Settings.Current.Stats.VerifyStats; } + else + SaveStatsChecked = false; - // TODO: Show Preferences in macOS - [NotNull] - public string Title => "Settings"; - [NotNull] - public string GdprLabel => "GDPR"; - [NotNull] - public string ReportsLabel => "Reports"; - [NotNull] - public string StatisticsLabel => "Statistics"; - [NotNull] - public string SaveLabel => "Save"; - [NotNull] - public string CancelLabel => "Cancel"; - [NotNull] - public string GdprText1 => - @"In compliance with the European Union General Data Protection Regulation 2016/679 (GDPR), + CancelCommand = ReactiveCommand.Create(ExecuteCancelCommand); + SaveCommand = ReactiveCommand.Create(ExecuteSaveCommand); + + if(!_gdprVisible) + _tabControlSelectedIndex = 1; + } + + // TODO: Show Preferences in macOS + [NotNull] + public string Title => "Settings"; + [NotNull] + public string GdprLabel => "GDPR"; + [NotNull] + public string ReportsLabel => "Reports"; + [NotNull] + public string StatisticsLabel => "Statistics"; + [NotNull] + public string SaveLabel => "Save"; + [NotNull] + public string CancelLabel => "Cancel"; + [NotNull] + public string GdprText1 => + @"In compliance with the European Union General Data Protection Regulation 2016/679 (GDPR), we must give you the following information about Aaru and ask if you want to opt-in in some information sharing."; - [NotNull] - public string GdprText2 => - @"Disclaimer: Because Aaru is an open source software this information, and therefore, + [NotNull] + public string GdprText2 => + @"Disclaimer: Because Aaru is an open source software this information, and therefore, compliance with GDPR only holds true if you obtained a certificated copy from its original authors. In case of doubt, close Aaru now and ask in our IRC support channel."; - [NotNull] - public string GdprText3 => - @"For any information sharing your IP address may be stored in our server, in a way that is not + [NotNull] + public string GdprText3 => + @"For any information sharing your IP address may be stored in our server, in a way that is not possible for any person, manual, or automated process, to link with your identity, unless specified otherwise."; - [NotNull] - public string ReportsGloballyText => - @"With the 'device-report' command, Aaru creates a report of a device, that includes its + [NotNull] + public string ReportsGloballyText => + @"With the 'device-report' command, Aaru creates a report of a device, that includes its manufacturer, model, firmware revision and/or version, attached bus, size, and supported commands. The serial number of the device is not stored in the report. If used with the debug parameter, extra information about the device will be stored in the report. This information is known to contain @@ -128,169 +128,168 @@ the device serial number in non-standard places that prevent the automatic remov of devices. A human-readable copy of the report in XML format is always created in the same directory where Aaru is being run from."; - [NotNull] - public string SaveReportsGloballyText => "Save device reports in shared folder of your computer?"; + [NotNull] + public string SaveReportsGloballyText => "Save device reports in shared folder of your computer?"; - [NotNull] - public string ReportsText => - @"Sharing a report with us will send it to our server, that's in the european union territory, where it + [NotNull] + public string ReportsText => + @"Sharing a report with us will send it to our server, that's in the european union territory, where it will be manually analyzed by an european union citizen to remove any trace of personal identification from it. Once that is done, it will be shared in our stats website, https://www.aaru.app These report will be used to improve Aaru support, and in some cases, to provide emulation of the devices to other open-source projects. In any case, no information linking the report to you will be stored."; - [NotNull] - public string ShareReportsText => "Share your device reports with us?"; - [NotNull] - public string StatisticsText => - @"Aaru can store some usage statistics. These statistics are limited to the number of times a + [NotNull] + public string ShareReportsText => "Share your device reports with us?"; + [NotNull] + public string StatisticsText => + @"Aaru can store some usage statistics. These statistics are limited to the number of times a command is executed, a filesystem, partition, or device is used, the operating system version, and other. In no case, any information besides pure statistical usage numbers is stored, and they're just joint to the pool with no way of using them to identify you."; - [NotNull] - public string SaveStatsText => "Save stats about your Aaru usage?"; - [NotNull] - public string ShareStatsText => "Share your stats (anonymously)?"; - [NotNull] - public string CommandStatsText => "Gather statistics about command usage?"; - [NotNull] - public string DeviceStatsText => "Gather statistics about found devices?"; - [NotNull] - public string FilesystemStatsText => "Gather statistics about found filesystems?"; - [NotNull] - public string FilterStatsText => "Gather statistics about found file filters?"; - [NotNull] - public string MediaImageStatsText => "Gather statistics about found media image formats?"; - [NotNull] - public string MediaScanStatsText => "Gather statistics about scanned media?"; - [NotNull] - public string PartitionStatsText => "Gather statistics about found partitioning schemes?"; - [NotNull] - public string MediaStatsText => "Gather statistics about media types?"; - [NotNull] - public string VerifyStatsText => "Gather statistics about media image verifications?"; + [NotNull] + public string SaveStatsText => "Save stats about your Aaru usage?"; + [NotNull] + public string ShareStatsText => "Share your stats (anonymously)?"; + [NotNull] + public string CommandStatsText => "Gather statistics about command usage?"; + [NotNull] + public string DeviceStatsText => "Gather statistics about found devices?"; + [NotNull] + public string FilesystemStatsText => "Gather statistics about found filesystems?"; + [NotNull] + public string FilterStatsText => "Gather statistics about found file filters?"; + [NotNull] + public string MediaImageStatsText => "Gather statistics about found media image formats?"; + [NotNull] + public string MediaScanStatsText => "Gather statistics about scanned media?"; + [NotNull] + public string PartitionStatsText => "Gather statistics about found partitioning schemes?"; + [NotNull] + public string MediaStatsText => "Gather statistics about media types?"; + [NotNull] + public string VerifyStatsText => "Gather statistics about media image verifications?"; - public ReactiveCommand CancelCommand { get; } - public ReactiveCommand SaveCommand { get; } + public ReactiveCommand CancelCommand { get; } + public ReactiveCommand SaveCommand { get; } - public bool GdprVisible - { - get => _gdprVisible; - set => this.RaiseAndSetIfChanged(ref _gdprVisible, value); - } - - public bool SaveReportsGloballyChecked - { - get => _saveReportsGloballyChecked; - set => this.RaiseAndSetIfChanged(ref _saveReportsGloballyChecked, value); - } - - public bool ShareReportsChecked - { - get => _shareReportsChecked; - set => this.RaiseAndSetIfChanged(ref _shareReportsChecked, value); - } - - public bool SaveStatsChecked - { - get => _saveStatsChecked; - set => this.RaiseAndSetIfChanged(ref _saveStatsChecked, value); - } - - public bool ShareStatsChecked - { - get => _shareStatsChecked; - set => this.RaiseAndSetIfChanged(ref _shareStatsChecked, value); - } - - public bool CommandStatsChecked - { - get => _commandStatsChecked; - set => this.RaiseAndSetIfChanged(ref _commandStatsChecked, value); - } - - public bool DeviceStatsChecked - { - get => _deviceStatsChecked; - set => this.RaiseAndSetIfChanged(ref _deviceStatsChecked, value); - } - - public bool FilesystemStatsChecked - { - get => _filesystemStatsChecked; - set => this.RaiseAndSetIfChanged(ref _filesystemStatsChecked, value); - } - - public bool FilterStatsChecked - { - get => _filterStatsChecked; - set => this.RaiseAndSetIfChanged(ref _filterStatsChecked, value); - } - - public bool MediaImageStatsChecked - { - get => _mediaImageStatsChecked; - set => this.RaiseAndSetIfChanged(ref _mediaImageStatsChecked, value); - } - - public bool MediaScanStatsChecked - { - get => _mediaScanStatsChecked; - set => this.RaiseAndSetIfChanged(ref _mediaScanStatsChecked, value); - } - - public bool PartitionStatsChecked - { - get => _partitionStatsChecked; - set => this.RaiseAndSetIfChanged(ref _partitionStatsChecked, value); - } - - public bool MediaStatsChecked - { - get => _mediaStatsChecked; - set => this.RaiseAndSetIfChanged(ref _mediaStatsChecked, value); - } - - public bool VerifyStatsChecked - { - get => _verifyStatsChecked; - set => this.RaiseAndSetIfChanged(ref _verifyStatsChecked, value); - } - - public int TabControlSelectedIndex - { - get => _tabControlSelectedIndex; - set => this.RaiseAndSetIfChanged(ref _tabControlSelectedIndex, value); - } - - void ExecuteSaveCommand() - { - Settings.Settings.Current.SaveReportsGlobally = SaveReportsGloballyChecked; - Settings.Settings.Current.ShareReports = ShareReportsChecked; - - if(SaveStatsChecked) - Settings.Settings.Current.Stats = new StatsSettings - { - ShareStats = ShareStatsChecked, - CommandStats = CommandStatsChecked, - DeviceStats = DeviceStatsChecked, - FilesystemStats = FilesystemStatsChecked, - FilterStats = FilterStatsChecked, - MediaImageStats = MediaImageStatsChecked, - MediaScanStats = MediaScanStatsChecked, - PartitionStats = PartitionStatsChecked, - MediaStats = MediaStatsChecked, - VerifyStats = VerifyStatsChecked - }; - else - Settings.Settings.Current.Stats = null; - - Settings.Settings.Current.GdprCompliance = DicSettings.GDPR_LEVEL; - Settings.Settings.SaveSettings(); - _view.Close(); - } - - void ExecuteCancelCommand() => _view.Close(); + public bool GdprVisible + { + get => _gdprVisible; + set => this.RaiseAndSetIfChanged(ref _gdprVisible, value); } + + public bool SaveReportsGloballyChecked + { + get => _saveReportsGloballyChecked; + set => this.RaiseAndSetIfChanged(ref _saveReportsGloballyChecked, value); + } + + public bool ShareReportsChecked + { + get => _shareReportsChecked; + set => this.RaiseAndSetIfChanged(ref _shareReportsChecked, value); + } + + public bool SaveStatsChecked + { + get => _saveStatsChecked; + set => this.RaiseAndSetIfChanged(ref _saveStatsChecked, value); + } + + public bool ShareStatsChecked + { + get => _shareStatsChecked; + set => this.RaiseAndSetIfChanged(ref _shareStatsChecked, value); + } + + public bool CommandStatsChecked + { + get => _commandStatsChecked; + set => this.RaiseAndSetIfChanged(ref _commandStatsChecked, value); + } + + public bool DeviceStatsChecked + { + get => _deviceStatsChecked; + set => this.RaiseAndSetIfChanged(ref _deviceStatsChecked, value); + } + + public bool FilesystemStatsChecked + { + get => _filesystemStatsChecked; + set => this.RaiseAndSetIfChanged(ref _filesystemStatsChecked, value); + } + + public bool FilterStatsChecked + { + get => _filterStatsChecked; + set => this.RaiseAndSetIfChanged(ref _filterStatsChecked, value); + } + + public bool MediaImageStatsChecked + { + get => _mediaImageStatsChecked; + set => this.RaiseAndSetIfChanged(ref _mediaImageStatsChecked, value); + } + + public bool MediaScanStatsChecked + { + get => _mediaScanStatsChecked; + set => this.RaiseAndSetIfChanged(ref _mediaScanStatsChecked, value); + } + + public bool PartitionStatsChecked + { + get => _partitionStatsChecked; + set => this.RaiseAndSetIfChanged(ref _partitionStatsChecked, value); + } + + public bool MediaStatsChecked + { + get => _mediaStatsChecked; + set => this.RaiseAndSetIfChanged(ref _mediaStatsChecked, value); + } + + public bool VerifyStatsChecked + { + get => _verifyStatsChecked; + set => this.RaiseAndSetIfChanged(ref _verifyStatsChecked, value); + } + + public int TabControlSelectedIndex + { + get => _tabControlSelectedIndex; + set => this.RaiseAndSetIfChanged(ref _tabControlSelectedIndex, value); + } + + void ExecuteSaveCommand() + { + Settings.Settings.Current.SaveReportsGlobally = SaveReportsGloballyChecked; + Settings.Settings.Current.ShareReports = ShareReportsChecked; + + if(SaveStatsChecked) + Settings.Settings.Current.Stats = new StatsSettings + { + ShareStats = ShareStatsChecked, + CommandStats = CommandStatsChecked, + DeviceStats = DeviceStatsChecked, + FilesystemStats = FilesystemStatsChecked, + FilterStats = FilterStatsChecked, + MediaImageStats = MediaImageStatsChecked, + MediaScanStats = MediaScanStatsChecked, + PartitionStats = PartitionStatsChecked, + MediaStats = MediaStatsChecked, + VerifyStats = VerifyStatsChecked + }; + else + Settings.Settings.Current.Stats = null; + + Settings.Settings.Current.GdprCompliance = DicSettings.GDPR_LEVEL; + Settings.Settings.SaveSettings(); + _view.Close(); + } + + void ExecuteCancelCommand() => _view.Close(); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Dialogs/StatisticsViewModel.cs b/Aaru.Gui/ViewModels/Dialogs/StatisticsViewModel.cs index 49c9d1f39..32707bf89 100644 --- a/Aaru.Gui/ViewModels/Dialogs/StatisticsViewModel.cs +++ b/Aaru.Gui/ViewModels/Dialogs/StatisticsViewModel.cs @@ -41,685 +41,684 @@ using JetBrains.Annotations; using ReactiveUI; using NameCountModel = Aaru.Gui.Models.NameCountModel; -namespace Aaru.Gui.ViewModels.Dialogs +namespace Aaru.Gui.ViewModels.Dialogs; + +public sealed class StatisticsViewModel : ViewModelBase { - public sealed class StatisticsViewModel : ViewModelBase + readonly StatisticsDialog _view; + string _checksumText; + bool _checksumVisible; + bool _commandsVisible; + string _compareText; + bool _compareVisible; + string _convertImageText; + bool _convertImageVisible; + string _createSidecarText; + bool _createSidecarVisible; + string _decodeText; + bool _decodeVisible; + string _deviceInfoText; + bool _deviceInfoVisible; + string _deviceReportText; + bool _deviceReportVisible; + bool _devicesVisible; + string _dumpMediaText; + bool _dumpMediaVisible; + string _entropyText; + bool _entropyVisible; + bool _filesystemsVisible; + bool _filtersVisible; + bool _formatsCommandVisible; + string _formatsText; + bool _formatsVisible; + + string _fsinfoText; + bool _fsinfoVisible; + string _imageInfoText; + bool _imageInfoVisible; + string _mediaInfoText; + bool _mediaInfoVisible; + string _mediaScanText; + bool _mediaScanVisible; + bool _mediasVisible; + bool _partitionsVisible; + string _printHexText; + bool _printHexVisible; + string _verifyText; + bool _verifyVisible; + + public StatisticsViewModel(StatisticsDialog view) { - readonly StatisticsDialog _view; - string _checksumText; - bool _checksumVisible; - bool _commandsVisible; - string _compareText; - bool _compareVisible; - string _convertImageText; - bool _convertImageVisible; - string _createSidecarText; - bool _createSidecarVisible; - string _decodeText; - bool _decodeVisible; - string _deviceInfoText; - bool _deviceInfoVisible; - string _deviceReportText; - bool _deviceReportVisible; - bool _devicesVisible; - string _dumpMediaText; - bool _dumpMediaVisible; - string _entropyText; - bool _entropyVisible; - bool _filesystemsVisible; - bool _filtersVisible; - bool _formatsCommandVisible; - string _formatsText; - bool _formatsVisible; + _view = view; + Filters = new ObservableCollection(); + Formats = new ObservableCollection(); + Partitions = new ObservableCollection(); + Filesystems = new ObservableCollection(); + Devices = new ObservableCollection(); + Medias = new ObservableCollection(); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - string _fsinfoText; - bool _fsinfoVisible; - string _imageInfoText; - bool _imageInfoVisible; - string _mediaInfoText; - bool _mediaInfoVisible; - string _mediaScanText; - bool _mediaScanVisible; - bool _mediasVisible; - bool _partitionsVisible; - string _printHexText; - bool _printHexVisible; - string _verifyText; - bool _verifyVisible; - - public StatisticsViewModel(StatisticsDialog view) + if(ctx.Commands.Any()) { - _view = view; - Filters = new ObservableCollection(); - Formats = new ObservableCollection(); - Partitions = new ObservableCollection(); - Filesystems = new ObservableCollection(); - Devices = new ObservableCollection(); - Medias = new ObservableCollection(); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - if(ctx.Commands.Any()) + if(ctx.Commands.Any(c => c.Name == "analyze")) { - if(ctx.Commands.Any(c => c.Name == "analyze")) + foreach(Command oldAnalyze in ctx.Commands.Where(c => c.Name == "analyze")) { - foreach(Command oldAnalyze in ctx.Commands.Where(c => c.Name == "analyze")) - { - oldAnalyze.Name = "fs-info"; - ctx.Commands.Update(oldAnalyze); - } - - ulong count = 0; - - foreach(Command fsInfo in ctx.Commands.Where(c => c.Name == "fs-info" && c.Synchronized)) - { - count += fsInfo.Count; - ctx.Remove(fsInfo); - } - - if(count > 0) - ctx.Commands.Add(new Command - { - Count = count, - Name = "fs-info", - Synchronized = true - }); - - ctx.SaveChanges(); + oldAnalyze.Name = "fs-info"; + ctx.Commands.Update(oldAnalyze); } - if(ctx.Commands.Any(c => c.Name == "fs-info")) + ulong count = 0; + + foreach(Command fsInfo in ctx.Commands.Where(c => c.Name == "fs-info" && c.Synchronized)) { - ulong count = ctx.Commands.Where(c => c.Name == "fs-info" && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "fs-info" && !c.Synchronized); - - FsInfoVisible = true; - FsInfoText = $"You have called the Filesystem Info command {count} times"; + count += fsInfo.Count; + ctx.Remove(fsInfo); } - if(ctx.Commands.Any(c => c.Name == "checksum")) - { - ulong count = ctx.Commands.Where(c => c.Name == "checksum" && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "checksum" && !c.Synchronized); - - ChecksumVisible = true; - ChecksumText = $"You have called the Checksum command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "compare")) - { - ulong count = ctx.Commands.Where(c => c.Name == "compare" && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "compare" && !c.Synchronized); - - CompareVisible = true; - CompareText = $"You have called the Compare command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "convert-image")) - { - ulong count = ctx.Commands.Where(c => c.Name == "convert-image" && c.Synchronized). - Select(c => c.Count).FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "convert-image" && !c.Synchronized); - - ConvertImageVisible = true; - ConvertImageText = $"You have called the Convert-Image command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "create-sidecar")) - { - ulong count = ctx.Commands.Where(c => c.Name == "create-sidecar" && c.Synchronized). - Select(c => c.Count).FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "create-sidecar" && !c.Synchronized); - - CreateSidecarVisible = true; - CreateSidecarText = $"You have called the Create-Sidecar command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "decode")) - { - ulong count = ctx.Commands.Where(c => c.Name == "decode" && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "decode" && !c.Synchronized); - - DecodeVisible = true; - DecodeText = $"You have called the Decode command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "device-info")) - { - ulong count = ctx.Commands.Where(c => c.Name == "device-info" && c.Synchronized). - Select(c => c.Count).FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "device-info" && !c.Synchronized); - - DeviceInfoVisible = true; - DeviceInfoText = $"You have called the Device-Info command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "device-report")) - { - ulong count = ctx.Commands.Where(c => c.Name == "device-report" && c.Synchronized). - Select(c => c.Count).FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "device-report" && !c.Synchronized); - - DeviceReportVisible = true; - DeviceReportText = $"You have called the Device-Report command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "dump-media")) - { - ulong count = ctx.Commands.Where(c => c.Name == "dump-media" && c.Synchronized). - Select(c => c.Count).FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "dump-media" && !c.Synchronized); - - DumpMediaVisible = true; - DumpMediaText = $"You have called the Dump-Media command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "entropy")) - { - ulong count = ctx.Commands.Where(c => c.Name == "entropy" && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "entropy" && !c.Synchronized); - - EntropyVisible = true; - EntropyText = $"You have called the Entropy command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "formats")) - { - ulong count = ctx.Commands.Where(c => c.Name == "formats" && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "formats" && !c.Synchronized); - - FormatsCommandVisible = true; - FormatsCommandText = $"You have called the Formats command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "image-info")) - { - ulong count = ctx.Commands.Where(c => c.Name == "image-info" && c.Synchronized). - Select(c => c.Count).FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "image-info" && !c.Synchronized); - - ImageInfoVisible = true; - ImageInfoText = $"You have called the Image-Info command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "media-info")) - { - ulong count = ctx.Commands.Where(c => c.Name == "media-info" && c.Synchronized). - Select(c => c.Count).FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "media-info" && !c.Synchronized); - - MediaInfoVisible = true; - MediaInfoText = $"You have called the Media-Info command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "media-scan")) - { - ulong count = ctx.Commands.Where(c => c.Name == "media-scan" && c.Synchronized). - Select(c => c.Count).FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "media-scan" && !c.Synchronized); - - MediaScanVisible = true; - MediaScanText = $"You have called the Media-Scan command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "printhex")) - { - ulong count = ctx.Commands.Where(c => c.Name == "printhex" && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "printhex" && !c.Synchronized); - - PrintHexVisible = true; - PrintHexText = $"You have called the Print-Hex command {count} times"; - } - - if(ctx.Commands.Any(c => c.Name == "verify")) - { - ulong count = ctx.Commands.Where(c => c.Name == "verify" && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Commands.LongCount(c => c.Name == "verify" && !c.Synchronized); - - VerifyVisible = true; - VerifyText = $"You have called the Verify command {count} times"; - } - - CommandsVisible = FsInfoVisible || ChecksumVisible || CompareVisible || ConvertImageVisible || - CreateSidecarVisible || DecodeVisible || DeviceInfoVisible || DeviceReportVisible || - DumpMediaVisible || EntropyVisible || FormatsCommandVisible || ImageInfoVisible || - MediaInfoVisible || MediaScanVisible || PrintHexVisible || VerifyVisible; - } - - if(ctx.Filters.Any()) - { - FiltersVisible = true; - - foreach(string nvs in ctx.Filters.Select(n => n.Name).Distinct()) - { - ulong count = ctx.Filters.Where(c => c.Name == nvs && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Filters.LongCount(c => c.Name == nvs && !c.Synchronized); - - Filters.Add(new NameCountModel - { - Name = nvs, - Count = count - }); - } - } - - if(ctx.MediaFormats.Any()) - { - FormatsVisible = true; - - foreach(string nvs in ctx.MediaFormats.Select(n => n.Name).Distinct()) - { - ulong count = ctx.MediaFormats.Where(c => c.Name == nvs && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.MediaFormats.LongCount(c => c.Name == nvs && !c.Synchronized); - - Formats.Add(new NameCountModel - { - Name = nvs, - Count = count - }); - } - } - - if(ctx.Partitions.Any()) - { - PartitionsVisible = true; - - foreach(string nvs in ctx.Partitions.Select(n => n.Name).Distinct()) - { - ulong count = ctx.Partitions.Where(c => c.Name == nvs && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Partitions.LongCount(c => c.Name == nvs && !c.Synchronized); - - Partitions.Add(new NameCountModel - { - Name = nvs, - Count = count - }); - } - } - - if(ctx.Filesystems.Any()) - { - FilesystemsVisible = true; - - foreach(string nvs in ctx.Filesystems.Select(n => n.Name).Distinct()) - { - ulong count = ctx.Filesystems.Where(c => c.Name == nvs && c.Synchronized).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Filesystems.LongCount(c => c.Name == nvs && !c.Synchronized); - - Filesystems.Add(new NameCountModel - { - Name = nvs, - Count = count - }); - } - } - - if(ctx.SeenDevices.Any()) - { - DevicesVisible = true; - - foreach(DeviceStat ds in ctx.SeenDevices.OrderBy(n => n.Manufacturer).ThenBy(n => n.Manufacturer). - ThenBy(n => n.Revision).ThenBy(n => n.Bus)) - Devices.Add(new DeviceStatsModel - { - Model = ds.Model, - Manufacturer = ds.Manufacturer, - Revision = ds.Revision, - Bus = ds.Bus - }); - } - - if(!ctx.Medias.Any()) - return; - - MediasVisible = true; - - foreach(string media in ctx.Medias.OrderBy(ms => ms.Type).Select(ms => ms.Type).Distinct()) - { - ulong count = ctx.Medias.Where(c => c.Type == media && c.Synchronized && c.Real).Select(c => c.Count). - FirstOrDefault(); - - count += (ulong)ctx.Medias.LongCount(c => c.Type == media && !c.Synchronized && c.Real); - if(count > 0) - Medias.Add(new MediaStatsModel + ctx.Commands.Add(new Command { - Name = media, - Count = count, - Type = "real" + Count = count, + Name = "fs-info", + Synchronized = true }); - count = ctx.Medias.Where(c => c.Type == media && c.Synchronized && !c.Real).Select(c => c.Count). - FirstOrDefault(); + ctx.SaveChanges(); + } - count += (ulong)ctx.Medias.LongCount(c => c.Type == media && !c.Synchronized && !c.Real); + if(ctx.Commands.Any(c => c.Name == "fs-info")) + { + ulong count = ctx.Commands.Where(c => c.Name == "fs-info" && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); - if(count == 0) - continue; + count += (ulong)ctx.Commands.LongCount(c => c.Name == "fs-info" && !c.Synchronized); - Medias.Add(new MediaStatsModel + FsInfoVisible = true; + FsInfoText = $"You have called the Filesystem Info command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "checksum")) + { + ulong count = ctx.Commands.Where(c => c.Name == "checksum" && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "checksum" && !c.Synchronized); + + ChecksumVisible = true; + ChecksumText = $"You have called the Checksum command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "compare")) + { + ulong count = ctx.Commands.Where(c => c.Name == "compare" && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "compare" && !c.Synchronized); + + CompareVisible = true; + CompareText = $"You have called the Compare command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "convert-image")) + { + ulong count = ctx.Commands.Where(c => c.Name == "convert-image" && c.Synchronized). + Select(c => c.Count).FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "convert-image" && !c.Synchronized); + + ConvertImageVisible = true; + ConvertImageText = $"You have called the Convert-Image command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "create-sidecar")) + { + ulong count = ctx.Commands.Where(c => c.Name == "create-sidecar" && c.Synchronized). + Select(c => c.Count).FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "create-sidecar" && !c.Synchronized); + + CreateSidecarVisible = true; + CreateSidecarText = $"You have called the Create-Sidecar command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "decode")) + { + ulong count = ctx.Commands.Where(c => c.Name == "decode" && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "decode" && !c.Synchronized); + + DecodeVisible = true; + DecodeText = $"You have called the Decode command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "device-info")) + { + ulong count = ctx.Commands.Where(c => c.Name == "device-info" && c.Synchronized). + Select(c => c.Count).FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "device-info" && !c.Synchronized); + + DeviceInfoVisible = true; + DeviceInfoText = $"You have called the Device-Info command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "device-report")) + { + ulong count = ctx.Commands.Where(c => c.Name == "device-report" && c.Synchronized). + Select(c => c.Count).FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "device-report" && !c.Synchronized); + + DeviceReportVisible = true; + DeviceReportText = $"You have called the Device-Report command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "dump-media")) + { + ulong count = ctx.Commands.Where(c => c.Name == "dump-media" && c.Synchronized). + Select(c => c.Count).FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "dump-media" && !c.Synchronized); + + DumpMediaVisible = true; + DumpMediaText = $"You have called the Dump-Media command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "entropy")) + { + ulong count = ctx.Commands.Where(c => c.Name == "entropy" && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "entropy" && !c.Synchronized); + + EntropyVisible = true; + EntropyText = $"You have called the Entropy command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "formats")) + { + ulong count = ctx.Commands.Where(c => c.Name == "formats" && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "formats" && !c.Synchronized); + + FormatsCommandVisible = true; + FormatsCommandText = $"You have called the Formats command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "image-info")) + { + ulong count = ctx.Commands.Where(c => c.Name == "image-info" && c.Synchronized). + Select(c => c.Count).FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "image-info" && !c.Synchronized); + + ImageInfoVisible = true; + ImageInfoText = $"You have called the Image-Info command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "media-info")) + { + ulong count = ctx.Commands.Where(c => c.Name == "media-info" && c.Synchronized). + Select(c => c.Count).FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "media-info" && !c.Synchronized); + + MediaInfoVisible = true; + MediaInfoText = $"You have called the Media-Info command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "media-scan")) + { + ulong count = ctx.Commands.Where(c => c.Name == "media-scan" && c.Synchronized). + Select(c => c.Count).FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "media-scan" && !c.Synchronized); + + MediaScanVisible = true; + MediaScanText = $"You have called the Media-Scan command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "printhex")) + { + ulong count = ctx.Commands.Where(c => c.Name == "printhex" && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "printhex" && !c.Synchronized); + + PrintHexVisible = true; + PrintHexText = $"You have called the Print-Hex command {count} times"; + } + + if(ctx.Commands.Any(c => c.Name == "verify")) + { + ulong count = ctx.Commands.Where(c => c.Name == "verify" && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Commands.LongCount(c => c.Name == "verify" && !c.Synchronized); + + VerifyVisible = true; + VerifyText = $"You have called the Verify command {count} times"; + } + + CommandsVisible = FsInfoVisible || ChecksumVisible || CompareVisible || ConvertImageVisible || + CreateSidecarVisible || DecodeVisible || DeviceInfoVisible || DeviceReportVisible || + DumpMediaVisible || EntropyVisible || FormatsCommandVisible || ImageInfoVisible || + MediaInfoVisible || MediaScanVisible || PrintHexVisible || VerifyVisible; + } + + if(ctx.Filters.Any()) + { + FiltersVisible = true; + + foreach(string nvs in ctx.Filters.Select(n => n.Name).Distinct()) + { + ulong count = ctx.Filters.Where(c => c.Name == nvs && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Filters.LongCount(c => c.Name == nvs && !c.Synchronized); + + Filters.Add(new NameCountModel { - Name = media, - Count = count, - Type = "image" + Name = nvs, + Count = count }); } } - public string FsInfoText + if(ctx.MediaFormats.Any()) { - get => _fsinfoText; - set => this.RaiseAndSetIfChanged(ref _fsinfoText, value); + FormatsVisible = true; + + foreach(string nvs in ctx.MediaFormats.Select(n => n.Name).Distinct()) + { + ulong count = ctx.MediaFormats.Where(c => c.Name == nvs && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.MediaFormats.LongCount(c => c.Name == nvs && !c.Synchronized); + + Formats.Add(new NameCountModel + { + Name = nvs, + Count = count + }); + } } - public bool FsInfoVisible + if(ctx.Partitions.Any()) { - get => _fsinfoVisible; - set => this.RaiseAndSetIfChanged(ref _fsinfoVisible, value); + PartitionsVisible = true; + + foreach(string nvs in ctx.Partitions.Select(n => n.Name).Distinct()) + { + ulong count = ctx.Partitions.Where(c => c.Name == nvs && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Partitions.LongCount(c => c.Name == nvs && !c.Synchronized); + + Partitions.Add(new NameCountModel + { + Name = nvs, + Count = count + }); + } } - public string ChecksumText + if(ctx.Filesystems.Any()) { - get => _checksumText; - set => this.RaiseAndSetIfChanged(ref _checksumText, value); + FilesystemsVisible = true; + + foreach(string nvs in ctx.Filesystems.Select(n => n.Name).Distinct()) + { + ulong count = ctx.Filesystems.Where(c => c.Name == nvs && c.Synchronized).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Filesystems.LongCount(c => c.Name == nvs && !c.Synchronized); + + Filesystems.Add(new NameCountModel + { + Name = nvs, + Count = count + }); + } } - public bool ChecksumVisible + if(ctx.SeenDevices.Any()) { - get => _checksumVisible; - set => this.RaiseAndSetIfChanged(ref _checksumVisible, value); + DevicesVisible = true; + + foreach(DeviceStat ds in ctx.SeenDevices.OrderBy(n => n.Manufacturer).ThenBy(n => n.Manufacturer). + ThenBy(n => n.Revision).ThenBy(n => n.Bus)) + Devices.Add(new DeviceStatsModel + { + Model = ds.Model, + Manufacturer = ds.Manufacturer, + Revision = ds.Revision, + Bus = ds.Bus + }); } - public string CompareText + if(!ctx.Medias.Any()) + return; + + MediasVisible = true; + + foreach(string media in ctx.Medias.OrderBy(ms => ms.Type).Select(ms => ms.Type).Distinct()) { - get => _compareText; - set => this.RaiseAndSetIfChanged(ref _compareText, value); + ulong count = ctx.Medias.Where(c => c.Type == media && c.Synchronized && c.Real).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Medias.LongCount(c => c.Type == media && !c.Synchronized && c.Real); + + if(count > 0) + Medias.Add(new MediaStatsModel + { + Name = media, + Count = count, + Type = "real" + }); + + count = ctx.Medias.Where(c => c.Type == media && c.Synchronized && !c.Real).Select(c => c.Count). + FirstOrDefault(); + + count += (ulong)ctx.Medias.LongCount(c => c.Type == media && !c.Synchronized && !c.Real); + + if(count == 0) + continue; + + Medias.Add(new MediaStatsModel + { + Name = media, + Count = count, + Type = "image" + }); } - - public bool CompareVisible - { - get => _compareVisible; - set => this.RaiseAndSetIfChanged(ref _compareVisible, value); - } - - public string ConvertImageText - { - get => _convertImageText; - set => this.RaiseAndSetIfChanged(ref _convertImageText, value); - } - - public bool ConvertImageVisible - { - get => _convertImageVisible; - set => this.RaiseAndSetIfChanged(ref _convertImageVisible, value); - } - - public string CreateSidecarText - { - get => _createSidecarText; - set => this.RaiseAndSetIfChanged(ref _createSidecarText, value); - } - - public bool CreateSidecarVisible - { - get => _createSidecarVisible; - set => this.RaiseAndSetIfChanged(ref _createSidecarVisible, value); - } - - public string DecodeText - { - get => _decodeText; - set => this.RaiseAndSetIfChanged(ref _decodeText, value); - } - - public bool DecodeVisible - { - get => _decodeVisible; - set => this.RaiseAndSetIfChanged(ref _decodeVisible, value); - } - - public string DeviceInfoText - { - get => _deviceInfoText; - set => this.RaiseAndSetIfChanged(ref _deviceInfoText, value); - } - - public bool DeviceInfoVisible - { - get => _deviceInfoVisible; - set => this.RaiseAndSetIfChanged(ref _deviceInfoVisible, value); - } - - public string DeviceReportText - { - get => _deviceReportText; - set => this.RaiseAndSetIfChanged(ref _deviceReportText, value); - } - - public bool DeviceReportVisible - { - get => _deviceReportVisible; - set => this.RaiseAndSetIfChanged(ref _deviceReportVisible, value); - } - - public string DumpMediaText - { - get => _dumpMediaText; - set => this.RaiseAndSetIfChanged(ref _dumpMediaText, value); - } - - public bool DumpMediaVisible - { - get => _dumpMediaVisible; - set => this.RaiseAndSetIfChanged(ref _dumpMediaVisible, value); - } - - public string EntropyText - { - get => _entropyText; - set => this.RaiseAndSetIfChanged(ref _entropyText, value); - } - - public bool EntropyVisible - { - get => _entropyVisible; - set => this.RaiseAndSetIfChanged(ref _entropyVisible, value); - } - - public string FormatsCommandText - { - get => _formatsText; - set => this.RaiseAndSetIfChanged(ref _formatsText, value); - } - - public bool FormatsCommandVisible - { - get => _formatsCommandVisible; - set => this.RaiseAndSetIfChanged(ref _formatsCommandVisible, value); - } - - public string ImageInfoText - { - get => _imageInfoText; - set => this.RaiseAndSetIfChanged(ref _imageInfoText, value); - } - - public bool ImageInfoVisible - { - get => _imageInfoVisible; - set => this.RaiseAndSetIfChanged(ref _imageInfoVisible, value); - } - - public string MediaInfoText - { - get => _mediaInfoText; - set => this.RaiseAndSetIfChanged(ref _mediaInfoText, value); - } - - public bool MediaInfoVisible - { - get => _mediaInfoVisible; - set => this.RaiseAndSetIfChanged(ref _mediaInfoVisible, value); - } - - public string MediaScanText - { - get => _mediaScanText; - set => this.RaiseAndSetIfChanged(ref _mediaScanText, value); - } - - public bool MediaScanVisible - { - get => _mediaScanVisible; - set => this.RaiseAndSetIfChanged(ref _mediaScanVisible, value); - } - - public string PrintHexText - { - get => _printHexText; - set => this.RaiseAndSetIfChanged(ref _printHexText, value); - } - - public bool PrintHexVisible - { - get => _printHexVisible; - set => this.RaiseAndSetIfChanged(ref _printHexVisible, value); - } - - public string VerifyText - { - get => _verifyText; - set => this.RaiseAndSetIfChanged(ref _verifyText, value); - } - - public bool VerifyVisible - { - get => _verifyVisible; - set => this.RaiseAndSetIfChanged(ref _verifyVisible, value); - } - - public bool CommandsVisible - { - get => _commandsVisible; - set => this.RaiseAndSetIfChanged(ref _commandsVisible, value); - } - - public bool FiltersVisible - { - get => _filtersVisible; - set => this.RaiseAndSetIfChanged(ref _filtersVisible, value); - } - - public bool PartitionsVisible - { - get => _partitionsVisible; - set => this.RaiseAndSetIfChanged(ref _partitionsVisible, value); - } - - public bool FormatsVisible - { - get => _formatsVisible; - set => this.RaiseAndSetIfChanged(ref _formatsVisible, value); - } - - public bool FilesystemsVisible - { - get => _filesystemsVisible; - set => this.RaiseAndSetIfChanged(ref _filesystemsVisible, value); - } - - public bool DevicesVisible - { - get => _devicesVisible; - set => this.RaiseAndSetIfChanged(ref _devicesVisible, value); - } - - public bool MediasVisible - { - get => _mediasVisible; - set => this.RaiseAndSetIfChanged(ref _mediasVisible, value); - } - - [NotNull] - public string CommandsLabel => "Commands"; - [NotNull] - public string FilterLabel => "Filter"; - [NotNull] - public string PartitionLabel => "Partition"; - [NotNull] - public string PartitionsLabel => "Partitions"; - [NotNull] - public string FiltersLabel => "Filters"; - [NotNull] - public string FormatsLabel => "Formats"; - [NotNull] - public string FormatLabel => "Format"; - [NotNull] - public string FilesystemsLabel => "Filesystems"; - [NotNull] - public string FilesystemLabel => "Filesystem"; - [NotNull] - public string TimesFoundLabel => "Times found"; - [NotNull] - public string DevicesLabel => "Devices"; - [NotNull] - public string DeviceLabel => "Device"; - [NotNull] - public string ManufacturerLabel => "Manufacturer"; - [NotNull] - public string RevisionLabel => "Revision"; - [NotNull] - public string BusLabel => "Bus"; - [NotNull] - public string MediasLabel => "Medias"; - [NotNull] - public string MediaLabel => "Media"; - [NotNull] - public string TypeLabel => "Type"; - [NotNull] - public string Title => "Encodings"; - [NotNull] - public string CloseLabel => "Close"; - public ReactiveCommand CloseCommand { get; } - public ObservableCollection Filters { get; } - public ObservableCollection Formats { get; } - public ObservableCollection Partitions { get; } - public ObservableCollection Filesystems { get; } - public ObservableCollection Devices { get; } - public ObservableCollection Medias { get; } - - void ExecuteCloseCommand() => _view.Close(); } + + public string FsInfoText + { + get => _fsinfoText; + set => this.RaiseAndSetIfChanged(ref _fsinfoText, value); + } + + public bool FsInfoVisible + { + get => _fsinfoVisible; + set => this.RaiseAndSetIfChanged(ref _fsinfoVisible, value); + } + + public string ChecksumText + { + get => _checksumText; + set => this.RaiseAndSetIfChanged(ref _checksumText, value); + } + + public bool ChecksumVisible + { + get => _checksumVisible; + set => this.RaiseAndSetIfChanged(ref _checksumVisible, value); + } + + public string CompareText + { + get => _compareText; + set => this.RaiseAndSetIfChanged(ref _compareText, value); + } + + public bool CompareVisible + { + get => _compareVisible; + set => this.RaiseAndSetIfChanged(ref _compareVisible, value); + } + + public string ConvertImageText + { + get => _convertImageText; + set => this.RaiseAndSetIfChanged(ref _convertImageText, value); + } + + public bool ConvertImageVisible + { + get => _convertImageVisible; + set => this.RaiseAndSetIfChanged(ref _convertImageVisible, value); + } + + public string CreateSidecarText + { + get => _createSidecarText; + set => this.RaiseAndSetIfChanged(ref _createSidecarText, value); + } + + public bool CreateSidecarVisible + { + get => _createSidecarVisible; + set => this.RaiseAndSetIfChanged(ref _createSidecarVisible, value); + } + + public string DecodeText + { + get => _decodeText; + set => this.RaiseAndSetIfChanged(ref _decodeText, value); + } + + public bool DecodeVisible + { + get => _decodeVisible; + set => this.RaiseAndSetIfChanged(ref _decodeVisible, value); + } + + public string DeviceInfoText + { + get => _deviceInfoText; + set => this.RaiseAndSetIfChanged(ref _deviceInfoText, value); + } + + public bool DeviceInfoVisible + { + get => _deviceInfoVisible; + set => this.RaiseAndSetIfChanged(ref _deviceInfoVisible, value); + } + + public string DeviceReportText + { + get => _deviceReportText; + set => this.RaiseAndSetIfChanged(ref _deviceReportText, value); + } + + public bool DeviceReportVisible + { + get => _deviceReportVisible; + set => this.RaiseAndSetIfChanged(ref _deviceReportVisible, value); + } + + public string DumpMediaText + { + get => _dumpMediaText; + set => this.RaiseAndSetIfChanged(ref _dumpMediaText, value); + } + + public bool DumpMediaVisible + { + get => _dumpMediaVisible; + set => this.RaiseAndSetIfChanged(ref _dumpMediaVisible, value); + } + + public string EntropyText + { + get => _entropyText; + set => this.RaiseAndSetIfChanged(ref _entropyText, value); + } + + public bool EntropyVisible + { + get => _entropyVisible; + set => this.RaiseAndSetIfChanged(ref _entropyVisible, value); + } + + public string FormatsCommandText + { + get => _formatsText; + set => this.RaiseAndSetIfChanged(ref _formatsText, value); + } + + public bool FormatsCommandVisible + { + get => _formatsCommandVisible; + set => this.RaiseAndSetIfChanged(ref _formatsCommandVisible, value); + } + + public string ImageInfoText + { + get => _imageInfoText; + set => this.RaiseAndSetIfChanged(ref _imageInfoText, value); + } + + public bool ImageInfoVisible + { + get => _imageInfoVisible; + set => this.RaiseAndSetIfChanged(ref _imageInfoVisible, value); + } + + public string MediaInfoText + { + get => _mediaInfoText; + set => this.RaiseAndSetIfChanged(ref _mediaInfoText, value); + } + + public bool MediaInfoVisible + { + get => _mediaInfoVisible; + set => this.RaiseAndSetIfChanged(ref _mediaInfoVisible, value); + } + + public string MediaScanText + { + get => _mediaScanText; + set => this.RaiseAndSetIfChanged(ref _mediaScanText, value); + } + + public bool MediaScanVisible + { + get => _mediaScanVisible; + set => this.RaiseAndSetIfChanged(ref _mediaScanVisible, value); + } + + public string PrintHexText + { + get => _printHexText; + set => this.RaiseAndSetIfChanged(ref _printHexText, value); + } + + public bool PrintHexVisible + { + get => _printHexVisible; + set => this.RaiseAndSetIfChanged(ref _printHexVisible, value); + } + + public string VerifyText + { + get => _verifyText; + set => this.RaiseAndSetIfChanged(ref _verifyText, value); + } + + public bool VerifyVisible + { + get => _verifyVisible; + set => this.RaiseAndSetIfChanged(ref _verifyVisible, value); + } + + public bool CommandsVisible + { + get => _commandsVisible; + set => this.RaiseAndSetIfChanged(ref _commandsVisible, value); + } + + public bool FiltersVisible + { + get => _filtersVisible; + set => this.RaiseAndSetIfChanged(ref _filtersVisible, value); + } + + public bool PartitionsVisible + { + get => _partitionsVisible; + set => this.RaiseAndSetIfChanged(ref _partitionsVisible, value); + } + + public bool FormatsVisible + { + get => _formatsVisible; + set => this.RaiseAndSetIfChanged(ref _formatsVisible, value); + } + + public bool FilesystemsVisible + { + get => _filesystemsVisible; + set => this.RaiseAndSetIfChanged(ref _filesystemsVisible, value); + } + + public bool DevicesVisible + { + get => _devicesVisible; + set => this.RaiseAndSetIfChanged(ref _devicesVisible, value); + } + + public bool MediasVisible + { + get => _mediasVisible; + set => this.RaiseAndSetIfChanged(ref _mediasVisible, value); + } + + [NotNull] + public string CommandsLabel => "Commands"; + [NotNull] + public string FilterLabel => "Filter"; + [NotNull] + public string PartitionLabel => "Partition"; + [NotNull] + public string PartitionsLabel => "Partitions"; + [NotNull] + public string FiltersLabel => "Filters"; + [NotNull] + public string FormatsLabel => "Formats"; + [NotNull] + public string FormatLabel => "Format"; + [NotNull] + public string FilesystemsLabel => "Filesystems"; + [NotNull] + public string FilesystemLabel => "Filesystem"; + [NotNull] + public string TimesFoundLabel => "Times found"; + [NotNull] + public string DevicesLabel => "Devices"; + [NotNull] + public string DeviceLabel => "Device"; + [NotNull] + public string ManufacturerLabel => "Manufacturer"; + [NotNull] + public string RevisionLabel => "Revision"; + [NotNull] + public string BusLabel => "Bus"; + [NotNull] + public string MediasLabel => "Medias"; + [NotNull] + public string MediaLabel => "Media"; + [NotNull] + public string TypeLabel => "Type"; + [NotNull] + public string Title => "Encodings"; + [NotNull] + public string CloseLabel => "Close"; + public ReactiveCommand CloseCommand { get; } + public ObservableCollection Filters { get; } + public ObservableCollection Formats { get; } + public ObservableCollection Partitions { get; } + public ObservableCollection Filesystems { get; } + public ObservableCollection Devices { get; } + public ObservableCollection Medias { get; } + + void ExecuteCloseCommand() => _view.Close(); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Panels/DeviceInfoViewModel.cs b/Aaru.Gui/ViewModels/Panels/DeviceInfoViewModel.cs index 081689b51..0fcf6712f 100644 --- a/Aaru.Gui/ViewModels/Panels/DeviceInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Panels/DeviceInfoViewModel.cs @@ -42,914 +42,913 @@ using Avalonia.Controls; using ReactiveUI; using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo; -namespace Aaru.Gui.ViewModels.Panels +namespace Aaru.Gui.ViewModels.Panels; + +public sealed class DeviceInfoViewModel : ViewModelBase { - public sealed class DeviceInfoViewModel : ViewModelBase + readonly DeviceInfo _devInfo; + readonly Window _view; + AtaInfo _ataInfo; + string _blockLimits; + string _blockSizeGranularity; + string _cid; + string _csd; + string _densities; + string _deviceType; + string _extendedCsd; + string _firewireGuid; + string _firewireManufacturer; + string _firewireModel; + string _firewireModelId; + string _firewireVendorId; + bool _firewireVisible; + bool _kreon; + bool _kreonChallengeResponse; + bool _kreonChallengeResponse360; + bool _kreonDecryptSs; + bool _kreonDecryptSs360; + bool _kreonErrorSkipping; + bool _kreonLock; + bool _kreonWxripperUnlock; + bool _kreonWxripperUnlock360; + bool _kreonXtremeUnlock; + bool _kreonXtremeUnlock360; + string _manufacturer; + string _maxBlockSize; + string _mediumDensity; + string _mediumTypes; + string _minBlockSize; + string _model; + string _ocr; + PcmciaInfo _pcmciaInfo; + bool _plextorBitSetting; + bool _plextorBitSettingDl; + string _plextorCdReadTime; + string _plextorCdWriteTime; + string _plextorDiscs; + string _plextorDvd; + bool _plextorDvdPlusWriteTest; + string _plextorDvdReadTime; + bool _plextorDvdTimesVisible; + string _plextorDvdWriteTime; + bool _plextorEepromVisible; + bool _plextorGigaRec; + bool _plextorHidesRecordables; + bool _plextorHidesSessions; + bool _plextorHiding; + bool _plextorPoweRec; + bool _plextorPoweRecEnabled; + string _plextorPoweRecLast; + bool _plextorPoweRecLastVisible; + string _plextorPoweRecMax; + bool _plextorPoweRecMaxVisible; + string _plextorPoweRecRecommended; + bool _plextorPoweRecRecommendedVisible; + string _plextorPoweRecSelected; + bool _plextorPoweRecSelectedVisible; + bool _plextorSecuRec; + bool _plextorSilentMode; + string _plextorSilentModeAccessTime; + string _plextorSilentModeCdReadSpeedLimit; + string _plextorSilentModeCdWriteSpeedLimit; + string _plextorSilentModeDvdReadSpeedLimit; + bool _plextorSilentModeDvdReadSpeedLimitVisible; + bool _plextorSilentModeEnabled; + bool _plextorSpeedEnabled; + bool _plextorSpeedRead; + bool _plextorVariRec; + bool _plextorVariRecDvd; + bool _plextorVisible; + bool _removable; + string _revision; + bool _saveUsbDescriptorsEnabled; + string _scr; + ScsiInfo _scsiInfo; + string _scsiType; + string _sdMm; + SdMmcInfo _sdMmcInfo; + string _secureDigital; + string _serial; + bool _ssc; + string _usbConnected; + string _usbManufacturer; + string _usbProduct; + string _usbProductId; + string _usbSerial; + string _usbVendorId; + bool _usbVisible; + + public DeviceInfoViewModel(DeviceInfo devInfo, Window view) { - readonly DeviceInfo _devInfo; - readonly Window _view; - AtaInfo _ataInfo; - string _blockLimits; - string _blockSizeGranularity; - string _cid; - string _csd; - string _densities; - string _deviceType; - string _extendedCsd; - string _firewireGuid; - string _firewireManufacturer; - string _firewireModel; - string _firewireModelId; - string _firewireVendorId; - bool _firewireVisible; - bool _kreon; - bool _kreonChallengeResponse; - bool _kreonChallengeResponse360; - bool _kreonDecryptSs; - bool _kreonDecryptSs360; - bool _kreonErrorSkipping; - bool _kreonLock; - bool _kreonWxripperUnlock; - bool _kreonWxripperUnlock360; - bool _kreonXtremeUnlock; - bool _kreonXtremeUnlock360; - string _manufacturer; - string _maxBlockSize; - string _mediumDensity; - string _mediumTypes; - string _minBlockSize; - string _model; - string _ocr; - PcmciaInfo _pcmciaInfo; - bool _plextorBitSetting; - bool _plextorBitSettingDl; - string _plextorCdReadTime; - string _plextorCdWriteTime; - string _plextorDiscs; - string _plextorDvd; - bool _plextorDvdPlusWriteTest; - string _plextorDvdReadTime; - bool _plextorDvdTimesVisible; - string _plextorDvdWriteTime; - bool _plextorEepromVisible; - bool _plextorGigaRec; - bool _plextorHidesRecordables; - bool _plextorHidesSessions; - bool _plextorHiding; - bool _plextorPoweRec; - bool _plextorPoweRecEnabled; - string _plextorPoweRecLast; - bool _plextorPoweRecLastVisible; - string _plextorPoweRecMax; - bool _plextorPoweRecMaxVisible; - string _plextorPoweRecRecommended; - bool _plextorPoweRecRecommendedVisible; - string _plextorPoweRecSelected; - bool _plextorPoweRecSelectedVisible; - bool _plextorSecuRec; - bool _plextorSilentMode; - string _plextorSilentModeAccessTime; - string _plextorSilentModeCdReadSpeedLimit; - string _plextorSilentModeCdWriteSpeedLimit; - string _plextorSilentModeDvdReadSpeedLimit; - bool _plextorSilentModeDvdReadSpeedLimitVisible; - bool _plextorSilentModeEnabled; - bool _plextorSpeedEnabled; - bool _plextorSpeedRead; - bool _plextorVariRec; - bool _plextorVariRecDvd; - bool _plextorVisible; - bool _removable; - string _revision; - bool _saveUsbDescriptorsEnabled; - string _scr; - ScsiInfo _scsiInfo; - string _scsiType; - string _sdMm; - SdMmcInfo _sdMmcInfo; - string _secureDigital; - string _serial; - bool _ssc; - string _usbConnected; - string _usbManufacturer; - string _usbProduct; - string _usbProductId; - string _usbSerial; - string _usbVendorId; - bool _usbVisible; + SaveUsbDescriptorsCommand = ReactiveCommand.Create(ExecuteSaveUsbDescriptorsCommand); + _view = view; + _devInfo = devInfo; - public DeviceInfoViewModel(DeviceInfo devInfo, Window view) + DeviceType = devInfo.Type.ToString(); + Manufacturer = devInfo.Manufacturer; + Model = devInfo.Model; + Revision = devInfo.FirmwareRevision; + Serial = devInfo.Serial; + ScsiType = devInfo.ScsiType.ToString(); + Removable = devInfo.IsRemovable; + UsbVisible = devInfo.IsUsb; + + if(devInfo.IsUsb) { - SaveUsbDescriptorsCommand = ReactiveCommand.Create(ExecuteSaveUsbDescriptorsCommand); - _view = view; - _devInfo = devInfo; + UsbVisible = true; + SaveUsbDescriptorsEnabled = devInfo.UsbDescriptors != null; + UsbVendorId = $"{devInfo.UsbVendorId:X4}"; + UsbProductId = $"{devInfo.UsbProductId:X4}"; + UsbManufacturer = devInfo.UsbManufacturerString; + UsbProduct = devInfo.UsbProductString; + UsbSerial = devInfo.UsbSerialString; + } - DeviceType = devInfo.Type.ToString(); - Manufacturer = devInfo.Manufacturer; - Model = devInfo.Model; - Revision = devInfo.FirmwareRevision; - Serial = devInfo.Serial; - ScsiType = devInfo.ScsiType.ToString(); - Removable = devInfo.IsRemovable; - UsbVisible = devInfo.IsUsb; + if(devInfo.IsFireWire) + { + FirewireVisible = true; + FirewireVendorId = $"{devInfo.FireWireVendor:X4}"; + FirewireModelId = $"{devInfo.FireWireModel:X4}"; + FirewireManufacturer = devInfo.FireWireVendorName; + FirewireModel = devInfo.FireWireModelName; + FirewireGuid = $"{devInfo.FireWireGuid:X16}"; + } - if(devInfo.IsUsb) + if(devInfo.IsPcmcia) + { + PcmciaInfo = new PcmciaInfo { - UsbVisible = true; - SaveUsbDescriptorsEnabled = devInfo.UsbDescriptors != null; - UsbVendorId = $"{devInfo.UsbVendorId:X4}"; - UsbProductId = $"{devInfo.UsbProductId:X4}"; - UsbManufacturer = devInfo.UsbManufacturerString; - UsbProduct = devInfo.UsbProductString; - UsbSerial = devInfo.UsbSerialString; - } - - if(devInfo.IsFireWire) - { - FirewireVisible = true; - FirewireVendorId = $"{devInfo.FireWireVendor:X4}"; - FirewireModelId = $"{devInfo.FireWireModel:X4}"; - FirewireManufacturer = devInfo.FireWireVendorName; - FirewireModel = devInfo.FireWireModelName; - FirewireGuid = $"{devInfo.FireWireGuid:X16}"; - } - - if(devInfo.IsPcmcia) - { - PcmciaInfo = new PcmciaInfo - { - DataContext = new PcmciaInfoViewModel(devInfo.Cis, _view) - }; - } - - if(devInfo.AtaIdentify != null || - devInfo.AtapiIdentify != null) - { - AtaInfo = new AtaInfo - { - DataContext = - new AtaInfoViewModel(devInfo.AtaIdentify, devInfo.AtapiIdentify, devInfo.AtaMcptError, _view) - }; - } - - if(devInfo.ScsiInquiryData != null) - { - ScsiInfo = new ScsiInfo - { - DataContext = new ScsiInfoViewModel(devInfo.ScsiInquiryData, devInfo.ScsiInquiry, - devInfo.ScsiEvpdPages, devInfo.ScsiMode, devInfo.ScsiType, - devInfo.ScsiModeSense6, devInfo.ScsiModeSense10, - devInfo.MmcConfiguration, _view) - }; - - if(devInfo.PlextorFeatures != null) - { - PlextorVisible = true; - - if(devInfo.PlextorFeatures.Eeprom != null) - { - PlextorEepromVisible = true; - PlextorDiscs = $"{devInfo.PlextorFeatures.Discs}"; - PlextorCdReadTime = TimeSpan.FromSeconds(devInfo.PlextorFeatures.CdReadTime).ToString(); - - PlextorCdWriteTime = TimeSpan.FromSeconds(devInfo.PlextorFeatures.CdWriteTime).ToString(); - - if(devInfo.PlextorFeatures.IsDvd) - { - PlextorDvdTimesVisible = true; - - PlextorDvdReadTime = TimeSpan.FromSeconds(devInfo.PlextorFeatures.DvdReadTime).ToString(); - - PlextorDvdWriteTime = TimeSpan.FromSeconds(devInfo.PlextorFeatures.DvdWriteTime).ToString(); - } - } - - PlextorPoweRec = devInfo.PlextorFeatures.PoweRec; - - if(devInfo.PlextorFeatures.PoweRec) - { - PlextorPoweRecEnabled = devInfo.PlextorFeatures.PoweRecEnabled; - - if(devInfo.PlextorFeatures.PoweRecEnabled) - { - PlextorPoweRecEnabled = true; - - if(devInfo.PlextorFeatures.PoweRecRecommendedSpeed > 0) - { - PlextorPoweRecRecommendedVisible = true; - - PlextorPoweRecRecommended = - $"{devInfo.PlextorFeatures.PoweRecRecommendedSpeed} Kb/sec."; - } - - if(devInfo.PlextorFeatures.PoweRecSelected > 0) - { - PlextorPoweRecSelectedVisible = true; - - PlextorPoweRecSelected = $"{devInfo.PlextorFeatures.PoweRecSelected} Kb/sec."; - } - - if(devInfo.PlextorFeatures.PoweRecMax > 0) - { - PlextorPoweRecMaxVisible = true; - PlextorPoweRecMax = $"{devInfo.PlextorFeatures.PoweRecMax} Kb/sec."; - } - - if(devInfo.PlextorFeatures.PoweRecLast > 0) - { - PlextorPoweRecLastVisible = true; - PlextorPoweRecLast = $"{devInfo.PlextorFeatures.PoweRecLast} Kb/sec."; - } - } - } - - PlextorSilentMode = devInfo.PlextorFeatures.SilentMode; - - if(devInfo.PlextorFeatures.SilentMode) - { - PlextorSilentModeEnabled = devInfo.PlextorFeatures.SilentModeEnabled; - - if(devInfo.PlextorFeatures.SilentModeEnabled) - { - PlextorSilentModeAccessTime = devInfo.PlextorFeatures.AccessTimeLimit == 2 - ? "\tAccess time is slow" : "\tAccess time is fast"; - - PlextorSilentModeCdReadSpeedLimit = - devInfo.PlextorFeatures.CdReadSpeedLimit > 0 - ? $"{devInfo.PlextorFeatures.CdReadSpeedLimit}x" : "unlimited"; - - PlextorSilentModeCdWriteSpeedLimit = - devInfo.PlextorFeatures.CdWriteSpeedLimit > 0 - ? $"{devInfo.PlextorFeatures.CdReadSpeedLimit}x" : "unlimited"; - - if(devInfo.PlextorFeatures.IsDvd) - { - PlextorSilentModeDvdReadSpeedLimitVisible = true; - - PlextorSilentModeDvdReadSpeedLimit = - devInfo.PlextorFeatures.DvdReadSpeedLimit > 0 - ? $"{devInfo.PlextorFeatures.DvdReadSpeedLimit}x" : "unlimited"; - } - } - } - - PlextorGigaRec = devInfo.PlextorFeatures.GigaRec; - PlextorSecuRec = devInfo.PlextorFeatures.SecuRec; - PlextorSpeedRead = devInfo.PlextorFeatures.SpeedRead; - - if(devInfo.PlextorFeatures.SpeedRead) - { - PlextorSpeedEnabled = devInfo.PlextorFeatures.SpeedReadEnabled; - } - - PlextorHiding = devInfo.PlextorFeatures.Hiding; - - if(devInfo.PlextorFeatures.Hiding) - { - PlextorHidesRecordables = devInfo.PlextorFeatures.HidesRecordables; - PlextorHidesSessions = devInfo.PlextorFeatures.HidesSessions; - } - - PlextorVariRec = devInfo.PlextorFeatures.VariRec; - - if(devInfo.PlextorFeatures.IsDvd) - { - PlextorVariRecDvd = devInfo.PlextorFeatures.VariRecDvd; - PlextorBitSetting = devInfo.PlextorFeatures.BitSetting; - PlextorBitSettingDl = devInfo.PlextorFeatures.BitSettingDl; - PlextorDvdPlusWriteTest = devInfo.PlextorFeatures.DvdPlusWriteTest; - } - } - - if(devInfo.ScsiInquiry?.KreonPresent == true) - { - Kreon = true; - KreonChallengeResponse = devInfo.KreonFeatures.HasFlag(KreonFeatures.ChallengeResponse); - KreonDecryptSs = devInfo.KreonFeatures.HasFlag(KreonFeatures.DecryptSs); - KreonXtremeUnlock = devInfo.KreonFeatures.HasFlag(KreonFeatures.XtremeUnlock); - KreonWxripperUnlock = devInfo.KreonFeatures.HasFlag(KreonFeatures.WxripperUnlock); - - KreonChallengeResponse360 = devInfo.KreonFeatures.HasFlag(KreonFeatures.ChallengeResponse360); - - KreonDecryptSs360 = devInfo.KreonFeatures.HasFlag(KreonFeatures.DecryptSs360); - KreonXtremeUnlock360 = devInfo.KreonFeatures.HasFlag(KreonFeatures.XtremeUnlock360); - KreonWxripperUnlock360 = devInfo.KreonFeatures.HasFlag(KreonFeatures.WxripperUnlock360); - KreonLock = devInfo.KreonFeatures.HasFlag(KreonFeatures.Lock); - KreonErrorSkipping = devInfo.KreonFeatures.HasFlag(KreonFeatures.ErrorSkipping); - } - - if(devInfo.BlockLimits != null) - { - BlockLimits.BlockLimitsData? - blockLimits = Decoders.SCSI.SSC.BlockLimits.Decode(devInfo.BlockLimits); - - if(blockLimits.HasValue) - { - Ssc = true; - - if(blockLimits.Value.minBlockLen == blockLimits.Value.maxBlockLen) - { - MinBlockSize = $"Device's block size is fixed at {blockLimits.Value.minBlockLen} bytes"; - } - else - { - MaxBlockSize = blockLimits.Value.maxBlockLen > 0 - ? $"Device's maximum block size is {blockLimits.Value.maxBlockLen} bytes" - : "Device does not specify a maximum block size"; - - MinBlockSize = $"Device's minimum block size is {blockLimits.Value.minBlockLen} bytes"; - - if(blockLimits.Value.granularity > 0) - { - BlockSizeGranularity = - $"Device's needs a block size granularity of 2^{blockLimits.Value.granularity} ({Math.Pow(2, blockLimits.Value.granularity)}) bytes"; - } - } - } - } - - if(devInfo.DensitySupport != null) - if(devInfo.DensitySupportHeader.HasValue) - { - Densities = DensitySupport.PrettifyDensity(devInfo.DensitySupportHeader); - } - - if(devInfo.MediumDensitySupport != null) - { - if(devInfo.MediaTypeSupportHeader.HasValue) - { - MediumTypes = DensitySupport.PrettifyMediumType(devInfo.MediaTypeSupportHeader); - } - - MediumDensity = DensitySupport.PrettifyMediumType(devInfo.MediumDensitySupport); - } - } - - SdMmcInfo = new SdMmcInfo - { - DataContext = new SdMmcInfoViewModel(devInfo.Type, devInfo.CID, devInfo.CSD, devInfo.OCR, - devInfo.ExtendedCSD, devInfo.SCR) + DataContext = new PcmciaInfoViewModel(devInfo.Cis, _view) }; } - public ReactiveCommand SaveUsbDescriptorsCommand { get; } - - public string DeviceType + if(devInfo.AtaIdentify != null || + devInfo.AtapiIdentify != null) { - get => _deviceType; - set => this.RaiseAndSetIfChanged(ref _deviceType, value); - } - - public string Manufacturer - { - get => _manufacturer; - set => this.RaiseAndSetIfChanged(ref _manufacturer, value); - } - - public string Model - { - get => _model; - set => this.RaiseAndSetIfChanged(ref _model, value); - } - - public string Revision - { - get => _revision; - set => this.RaiseAndSetIfChanged(ref _revision, value); - } - - public string Serial - { - get => _serial; - set => this.RaiseAndSetIfChanged(ref _serial, value); - } - - public string ScsiType - { - get => _scsiType; - set => this.RaiseAndSetIfChanged(ref _scsiType, value); - } - - public bool Removable - { - get => _removable; - set => this.RaiseAndSetIfChanged(ref _removable, value); - } - - public string UsbConnected - { - get => _usbConnected; - set => this.RaiseAndSetIfChanged(ref _usbConnected, value); - } - - public bool UsbVisible - { - get => _usbVisible; - set => this.RaiseAndSetIfChanged(ref _usbVisible, value); - } - - public string UsbVendorId - { - get => _usbVendorId; - set => this.RaiseAndSetIfChanged(ref _usbVendorId, value); - } - - public string UsbProductId - { - get => _usbProductId; - set => this.RaiseAndSetIfChanged(ref _usbProductId, value); - } - - public string UsbManufacturer - { - get => _usbManufacturer; - set => this.RaiseAndSetIfChanged(ref _usbManufacturer, value); - } - - public string UsbProduct - { - get => _usbProduct; - set => this.RaiseAndSetIfChanged(ref _usbProduct, value); - } - - public string UsbSerial - { - get => _usbSerial; - set => this.RaiseAndSetIfChanged(ref _usbSerial, value); - } - - public bool SaveUsbDescriptorsEnabled - { - get => _saveUsbDescriptorsEnabled; - set => this.RaiseAndSetIfChanged(ref _saveUsbDescriptorsEnabled, value); - } - - public bool FirewireVisible - { - get => _firewireVisible; - set => this.RaiseAndSetIfChanged(ref _firewireVisible, value); - } - - public string FirewireVendorId - { - get => _firewireVendorId; - set => this.RaiseAndSetIfChanged(ref _firewireVendorId, value); - } - - public string FirewireModelId - { - get => _firewireModelId; - set => this.RaiseAndSetIfChanged(ref _firewireModelId, value); - } - - public string FirewireManufacturer - { - get => _firewireManufacturer; - set => this.RaiseAndSetIfChanged(ref _firewireManufacturer, value); - } - - public string FirewireModel - { - get => _firewireModel; - set => this.RaiseAndSetIfChanged(ref _firewireModel, value); - } - - public string FirewireGuid - { - get => _firewireGuid; - set => this.RaiseAndSetIfChanged(ref _firewireGuid, value); - } - - public bool PlextorVisible - { - get => _plextorVisible; - set => this.RaiseAndSetIfChanged(ref _plextorVisible, value); - } - - public bool PlextorEepromVisible - { - get => _plextorEepromVisible; - set => this.RaiseAndSetIfChanged(ref _plextorEepromVisible, value); - } - - public string PlextorDiscs - { - get => _plextorDiscs; - set => this.RaiseAndSetIfChanged(ref _plextorDiscs, value); - } - - public string PlextorCdReadTime - { - get => _plextorCdReadTime; - set => this.RaiseAndSetIfChanged(ref _plextorCdReadTime, value); - } - - public string PlextorCdWriteTime - { - get => _plextorCdWriteTime; - set => this.RaiseAndSetIfChanged(ref _plextorCdWriteTime, value); - } - - public bool PlextorDvdTimesVisible - { - get => _plextorDvdTimesVisible; - set => this.RaiseAndSetIfChanged(ref _plextorDvdTimesVisible, value); - } - - public string PlextorDvdReadTime - { - get => _plextorDvdReadTime; - set => this.RaiseAndSetIfChanged(ref _plextorDvdReadTime, value); - } - - public string PlextorDvdWriteTime - { - get => _plextorDvdWriteTime; - set => this.RaiseAndSetIfChanged(ref _plextorDvdWriteTime, value); - } - - public bool PlextorPoweRec - { - get => _plextorPoweRec; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRec, value); - } - - public bool PlextorPoweRecEnabled - { - get => _plextorPoweRecEnabled; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecEnabled, value); - } - - public bool PlextorPoweRecRecommendedVisible - { - get => _plextorPoweRecRecommendedVisible; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecRecommendedVisible, value); - } - - public string PlextorPoweRecRecommended - { - get => _plextorPoweRecRecommended; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecRecommended, value); - } - - public bool PlextorPoweRecSelectedVisible - { - get => _plextorPoweRecSelectedVisible; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecSelectedVisible, value); - } - - public string PlextorPoweRecSelected - { - get => _plextorPoweRecSelected; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecSelected, value); - } - - public bool PlextorPoweRecMaxVisible - { - get => _plextorPoweRecMaxVisible; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecMaxVisible, value); - } - - public string PlextorPoweRecMax - { - get => _plextorPoweRecMax; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecMax, value); - } - - public bool PlextorPoweRecLastVisible - { - get => _plextorPoweRecLastVisible; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecLastVisible, value); - } - - public string PlextorPoweRecLast - { - get => _plextorPoweRecLast; - set => this.RaiseAndSetIfChanged(ref _plextorPoweRecLast, value); - } - - public bool PlextorSilentMode - { - get => _plextorSilentMode; - set => this.RaiseAndSetIfChanged(ref _plextorSilentMode, value); - } - - public bool PlextorSilentModeEnabled - { - get => _plextorSilentModeEnabled; - set => this.RaiseAndSetIfChanged(ref _plextorSilentModeEnabled, value); - } - - public string PlextorSilentModeAccessTime - { - get => _plextorSilentModeAccessTime; - set => this.RaiseAndSetIfChanged(ref _plextorSilentModeAccessTime, value); - } - - public string PlextorSilentModeCdReadSpeedLimit - { - get => _plextorSilentModeCdReadSpeedLimit; - set => this.RaiseAndSetIfChanged(ref _plextorSilentModeCdReadSpeedLimit, value); - } - - public string PlextorSilentModeCdWriteSpeedLimit - { - get => _plextorSilentModeCdWriteSpeedLimit; - set => this.RaiseAndSetIfChanged(ref _plextorSilentModeCdWriteSpeedLimit, value); - } - - public bool PlextorSilentModeDvdReadSpeedLimitVisible - { - get => _plextorSilentModeDvdReadSpeedLimitVisible; - set => this.RaiseAndSetIfChanged(ref _plextorSilentModeDvdReadSpeedLimitVisible, value); - } - - public string PlextorSilentModeDvdReadSpeedLimit - { - get => _plextorSilentModeDvdReadSpeedLimit; - set => this.RaiseAndSetIfChanged(ref _plextorSilentModeDvdReadSpeedLimit, value); - } - - public bool PlextorGigaRec - { - get => _plextorGigaRec; - set => this.RaiseAndSetIfChanged(ref _plextorGigaRec, value); - } - - public bool PlextorSecuRec - { - get => _plextorSecuRec; - set => this.RaiseAndSetIfChanged(ref _plextorSecuRec, value); - } - - public bool PlextorSpeedRead - { - get => _plextorSpeedRead; - set => this.RaiseAndSetIfChanged(ref _plextorSpeedRead, value); - } - - public bool PlextorSpeedEnabled - { - get => _plextorSpeedEnabled; - set => this.RaiseAndSetIfChanged(ref _plextorSpeedEnabled, value); - } - - public bool PlextorHiding - { - get => _plextorHiding; - set => this.RaiseAndSetIfChanged(ref _plextorHiding, value); - } - - public bool PlextorHidesRecordables - { - get => _plextorHidesRecordables; - set => this.RaiseAndSetIfChanged(ref _plextorHidesRecordables, value); - } - - public bool PlextorHidesSessions - { - get => _plextorHidesSessions; - set => this.RaiseAndSetIfChanged(ref _plextorHidesSessions, value); - } - - public bool PlextorVariRec - { - get => _plextorVariRec; - set => this.RaiseAndSetIfChanged(ref _plextorVariRec, value); - } - - public string PlextorDvd - { - get => _plextorDvd; - set => this.RaiseAndSetIfChanged(ref _plextorDvd, value); - } - - public bool PlextorVariRecDvd - { - get => _plextorVariRecDvd; - set => this.RaiseAndSetIfChanged(ref _plextorVariRecDvd, value); - } - - public bool PlextorBitSetting - { - get => _plextorBitSetting; - set => this.RaiseAndSetIfChanged(ref _plextorBitSetting, value); - } - - public bool PlextorBitSettingDl - { - get => _plextorBitSettingDl; - set => this.RaiseAndSetIfChanged(ref _plextorBitSettingDl, value); - } - - public bool PlextorDvdPlusWriteTest - { - get => _plextorDvdPlusWriteTest; - set => this.RaiseAndSetIfChanged(ref _plextorDvdPlusWriteTest, value); - } - - public bool Kreon - { - get => _kreon; - set => this.RaiseAndSetIfChanged(ref _kreon, value); - } - - public bool KreonChallengeResponse - { - get => _kreonChallengeResponse; - set => this.RaiseAndSetIfChanged(ref _kreonChallengeResponse, value); - } - - public bool KreonDecryptSs - { - get => _kreonDecryptSs; - set => this.RaiseAndSetIfChanged(ref _kreonDecryptSs, value); - } - - public bool KreonXtremeUnlock - { - get => _kreonXtremeUnlock; - set => this.RaiseAndSetIfChanged(ref _kreonXtremeUnlock, value); - } - - public bool KreonWxripperUnlock - { - get => _kreonWxripperUnlock; - set => this.RaiseAndSetIfChanged(ref _kreonWxripperUnlock, value); - } - - public bool KreonChallengeResponse360 - { - get => _kreonChallengeResponse360; - set => this.RaiseAndSetIfChanged(ref _kreonChallengeResponse360, value); - } - - public bool KreonDecryptSs360 - { - get => _kreonDecryptSs360; - set => this.RaiseAndSetIfChanged(ref _kreonDecryptSs360, value); - } - - public bool KreonXtremeUnlock360 - { - get => _kreonXtremeUnlock360; - set => this.RaiseAndSetIfChanged(ref _kreonXtremeUnlock360, value); - } - - public bool KreonWxripperUnlock360 - { - get => _kreonWxripperUnlock360; - set => this.RaiseAndSetIfChanged(ref _kreonWxripperUnlock360, value); - } - - public bool KreonLock - { - get => _kreonLock; - set => this.RaiseAndSetIfChanged(ref _kreonLock, value); - } - - public bool KreonErrorSkipping - { - get => _kreonErrorSkipping; - set => this.RaiseAndSetIfChanged(ref _kreonErrorSkipping, value); - } - - public bool Ssc - { - get => _ssc; - set => this.RaiseAndSetIfChanged(ref _ssc, value); - } - - public string BlockLimits - { - get => _blockLimits; - set => this.RaiseAndSetIfChanged(ref _blockLimits, value); - } - - public string MinBlockSize - { - get => _minBlockSize; - set => this.RaiseAndSetIfChanged(ref _minBlockSize, value); - } - - public string MaxBlockSize - { - get => _maxBlockSize; - set => this.RaiseAndSetIfChanged(ref _maxBlockSize, value); - } - - public string BlockSizeGranularity - { - get => _blockSizeGranularity; - set => this.RaiseAndSetIfChanged(ref _blockSizeGranularity, value); - } - - public string Densities - { - get => _densities; - set => this.RaiseAndSetIfChanged(ref _densities, value); - } - - public string MediumTypes - { - get => _mediumTypes; - set => this.RaiseAndSetIfChanged(ref _mediumTypes, value); - } - - public string MediumDensity - { - get => _mediumDensity; - set => this.RaiseAndSetIfChanged(ref _mediumDensity, value); - } - - public string SecureDigital - { - get => _secureDigital; - set => this.RaiseAndSetIfChanged(ref _secureDigital, value); - } - - public string SdMm - { - get => _sdMm; - set => this.RaiseAndSetIfChanged(ref _sdMm, value); - } - - public string Cid - { - get => _cid; - set => this.RaiseAndSetIfChanged(ref _cid, value); - } - - public string Csd - { - get => _csd; - set => this.RaiseAndSetIfChanged(ref _csd, value); - } - - public string Ocr - { - get => _ocr; - set => this.RaiseAndSetIfChanged(ref _ocr, value); - } - - public string ExtendedCsd - { - get => _extendedCsd; - set => this.RaiseAndSetIfChanged(ref _extendedCsd, value); - } - - public string Scr - { - get => _scr; - set => this.RaiseAndSetIfChanged(ref _scr, value); - } - - public PcmciaInfo PcmciaInfo - { - get => _pcmciaInfo; - set => this.RaiseAndSetIfChanged(ref _pcmciaInfo, value); - } - - public ScsiInfo ScsiInfo - { - get => _scsiInfo; - set => this.RaiseAndSetIfChanged(ref _scsiInfo, value); - } - - public AtaInfo AtaInfo - { - get => _ataInfo; - set => this.RaiseAndSetIfChanged(ref _ataInfo, value); - } - - public SdMmcInfo SdMmcInfo - { - get => _sdMmcInfo; - set => this.RaiseAndSetIfChanged(ref _sdMmcInfo, value); - } - - async void ExecuteSaveUsbDescriptorsCommand() - { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter + AtaInfo = new AtaInfo { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); - - string result = await dlgSaveBinary.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_devInfo.UsbDescriptors, 0, _devInfo.UsbDescriptors.Length); - - saveFs.Close(); + DataContext = + new AtaInfoViewModel(devInfo.AtaIdentify, devInfo.AtapiIdentify, devInfo.AtaMcptError, _view) + }; } + + if(devInfo.ScsiInquiryData != null) + { + ScsiInfo = new ScsiInfo + { + DataContext = new ScsiInfoViewModel(devInfo.ScsiInquiryData, devInfo.ScsiInquiry, + devInfo.ScsiEvpdPages, devInfo.ScsiMode, devInfo.ScsiType, + devInfo.ScsiModeSense6, devInfo.ScsiModeSense10, + devInfo.MmcConfiguration, _view) + }; + + if(devInfo.PlextorFeatures != null) + { + PlextorVisible = true; + + if(devInfo.PlextorFeatures.Eeprom != null) + { + PlextorEepromVisible = true; + PlextorDiscs = $"{devInfo.PlextorFeatures.Discs}"; + PlextorCdReadTime = TimeSpan.FromSeconds(devInfo.PlextorFeatures.CdReadTime).ToString(); + + PlextorCdWriteTime = TimeSpan.FromSeconds(devInfo.PlextorFeatures.CdWriteTime).ToString(); + + if(devInfo.PlextorFeatures.IsDvd) + { + PlextorDvdTimesVisible = true; + + PlextorDvdReadTime = TimeSpan.FromSeconds(devInfo.PlextorFeatures.DvdReadTime).ToString(); + + PlextorDvdWriteTime = TimeSpan.FromSeconds(devInfo.PlextorFeatures.DvdWriteTime).ToString(); + } + } + + PlextorPoweRec = devInfo.PlextorFeatures.PoweRec; + + if(devInfo.PlextorFeatures.PoweRec) + { + PlextorPoweRecEnabled = devInfo.PlextorFeatures.PoweRecEnabled; + + if(devInfo.PlextorFeatures.PoweRecEnabled) + { + PlextorPoweRecEnabled = true; + + if(devInfo.PlextorFeatures.PoweRecRecommendedSpeed > 0) + { + PlextorPoweRecRecommendedVisible = true; + + PlextorPoweRecRecommended = + $"{devInfo.PlextorFeatures.PoweRecRecommendedSpeed} Kb/sec."; + } + + if(devInfo.PlextorFeatures.PoweRecSelected > 0) + { + PlextorPoweRecSelectedVisible = true; + + PlextorPoweRecSelected = $"{devInfo.PlextorFeatures.PoweRecSelected} Kb/sec."; + } + + if(devInfo.PlextorFeatures.PoweRecMax > 0) + { + PlextorPoweRecMaxVisible = true; + PlextorPoweRecMax = $"{devInfo.PlextorFeatures.PoweRecMax} Kb/sec."; + } + + if(devInfo.PlextorFeatures.PoweRecLast > 0) + { + PlextorPoweRecLastVisible = true; + PlextorPoweRecLast = $"{devInfo.PlextorFeatures.PoweRecLast} Kb/sec."; + } + } + } + + PlextorSilentMode = devInfo.PlextorFeatures.SilentMode; + + if(devInfo.PlextorFeatures.SilentMode) + { + PlextorSilentModeEnabled = devInfo.PlextorFeatures.SilentModeEnabled; + + if(devInfo.PlextorFeatures.SilentModeEnabled) + { + PlextorSilentModeAccessTime = devInfo.PlextorFeatures.AccessTimeLimit == 2 + ? "\tAccess time is slow" : "\tAccess time is fast"; + + PlextorSilentModeCdReadSpeedLimit = + devInfo.PlextorFeatures.CdReadSpeedLimit > 0 + ? $"{devInfo.PlextorFeatures.CdReadSpeedLimit}x" : "unlimited"; + + PlextorSilentModeCdWriteSpeedLimit = + devInfo.PlextorFeatures.CdWriteSpeedLimit > 0 + ? $"{devInfo.PlextorFeatures.CdReadSpeedLimit}x" : "unlimited"; + + if(devInfo.PlextorFeatures.IsDvd) + { + PlextorSilentModeDvdReadSpeedLimitVisible = true; + + PlextorSilentModeDvdReadSpeedLimit = + devInfo.PlextorFeatures.DvdReadSpeedLimit > 0 + ? $"{devInfo.PlextorFeatures.DvdReadSpeedLimit}x" : "unlimited"; + } + } + } + + PlextorGigaRec = devInfo.PlextorFeatures.GigaRec; + PlextorSecuRec = devInfo.PlextorFeatures.SecuRec; + PlextorSpeedRead = devInfo.PlextorFeatures.SpeedRead; + + if(devInfo.PlextorFeatures.SpeedRead) + { + PlextorSpeedEnabled = devInfo.PlextorFeatures.SpeedReadEnabled; + } + + PlextorHiding = devInfo.PlextorFeatures.Hiding; + + if(devInfo.PlextorFeatures.Hiding) + { + PlextorHidesRecordables = devInfo.PlextorFeatures.HidesRecordables; + PlextorHidesSessions = devInfo.PlextorFeatures.HidesSessions; + } + + PlextorVariRec = devInfo.PlextorFeatures.VariRec; + + if(devInfo.PlextorFeatures.IsDvd) + { + PlextorVariRecDvd = devInfo.PlextorFeatures.VariRecDvd; + PlextorBitSetting = devInfo.PlextorFeatures.BitSetting; + PlextorBitSettingDl = devInfo.PlextorFeatures.BitSettingDl; + PlextorDvdPlusWriteTest = devInfo.PlextorFeatures.DvdPlusWriteTest; + } + } + + if(devInfo.ScsiInquiry?.KreonPresent == true) + { + Kreon = true; + KreonChallengeResponse = devInfo.KreonFeatures.HasFlag(KreonFeatures.ChallengeResponse); + KreonDecryptSs = devInfo.KreonFeatures.HasFlag(KreonFeatures.DecryptSs); + KreonXtremeUnlock = devInfo.KreonFeatures.HasFlag(KreonFeatures.XtremeUnlock); + KreonWxripperUnlock = devInfo.KreonFeatures.HasFlag(KreonFeatures.WxripperUnlock); + + KreonChallengeResponse360 = devInfo.KreonFeatures.HasFlag(KreonFeatures.ChallengeResponse360); + + KreonDecryptSs360 = devInfo.KreonFeatures.HasFlag(KreonFeatures.DecryptSs360); + KreonXtremeUnlock360 = devInfo.KreonFeatures.HasFlag(KreonFeatures.XtremeUnlock360); + KreonWxripperUnlock360 = devInfo.KreonFeatures.HasFlag(KreonFeatures.WxripperUnlock360); + KreonLock = devInfo.KreonFeatures.HasFlag(KreonFeatures.Lock); + KreonErrorSkipping = devInfo.KreonFeatures.HasFlag(KreonFeatures.ErrorSkipping); + } + + if(devInfo.BlockLimits != null) + { + BlockLimits.BlockLimitsData? + blockLimits = Decoders.SCSI.SSC.BlockLimits.Decode(devInfo.BlockLimits); + + if(blockLimits.HasValue) + { + Ssc = true; + + if(blockLimits.Value.minBlockLen == blockLimits.Value.maxBlockLen) + { + MinBlockSize = $"Device's block size is fixed at {blockLimits.Value.minBlockLen} bytes"; + } + else + { + MaxBlockSize = blockLimits.Value.maxBlockLen > 0 + ? $"Device's maximum block size is {blockLimits.Value.maxBlockLen} bytes" + : "Device does not specify a maximum block size"; + + MinBlockSize = $"Device's minimum block size is {blockLimits.Value.minBlockLen} bytes"; + + if(blockLimits.Value.granularity > 0) + { + BlockSizeGranularity = + $"Device's needs a block size granularity of 2^{blockLimits.Value.granularity} ({Math.Pow(2, blockLimits.Value.granularity)}) bytes"; + } + } + } + } + + if(devInfo.DensitySupport != null) + if(devInfo.DensitySupportHeader.HasValue) + { + Densities = DensitySupport.PrettifyDensity(devInfo.DensitySupportHeader); + } + + if(devInfo.MediumDensitySupport != null) + { + if(devInfo.MediaTypeSupportHeader.HasValue) + { + MediumTypes = DensitySupport.PrettifyMediumType(devInfo.MediaTypeSupportHeader); + } + + MediumDensity = DensitySupport.PrettifyMediumType(devInfo.MediumDensitySupport); + } + } + + SdMmcInfo = new SdMmcInfo + { + DataContext = new SdMmcInfoViewModel(devInfo.Type, devInfo.CID, devInfo.CSD, devInfo.OCR, + devInfo.ExtendedCSD, devInfo.SCR) + }; + } + + public ReactiveCommand SaveUsbDescriptorsCommand { get; } + + public string DeviceType + { + get => _deviceType; + set => this.RaiseAndSetIfChanged(ref _deviceType, value); + } + + public string Manufacturer + { + get => _manufacturer; + set => this.RaiseAndSetIfChanged(ref _manufacturer, value); + } + + public string Model + { + get => _model; + set => this.RaiseAndSetIfChanged(ref _model, value); + } + + public string Revision + { + get => _revision; + set => this.RaiseAndSetIfChanged(ref _revision, value); + } + + public string Serial + { + get => _serial; + set => this.RaiseAndSetIfChanged(ref _serial, value); + } + + public string ScsiType + { + get => _scsiType; + set => this.RaiseAndSetIfChanged(ref _scsiType, value); + } + + public bool Removable + { + get => _removable; + set => this.RaiseAndSetIfChanged(ref _removable, value); + } + + public string UsbConnected + { + get => _usbConnected; + set => this.RaiseAndSetIfChanged(ref _usbConnected, value); + } + + public bool UsbVisible + { + get => _usbVisible; + set => this.RaiseAndSetIfChanged(ref _usbVisible, value); + } + + public string UsbVendorId + { + get => _usbVendorId; + set => this.RaiseAndSetIfChanged(ref _usbVendorId, value); + } + + public string UsbProductId + { + get => _usbProductId; + set => this.RaiseAndSetIfChanged(ref _usbProductId, value); + } + + public string UsbManufacturer + { + get => _usbManufacturer; + set => this.RaiseAndSetIfChanged(ref _usbManufacturer, value); + } + + public string UsbProduct + { + get => _usbProduct; + set => this.RaiseAndSetIfChanged(ref _usbProduct, value); + } + + public string UsbSerial + { + get => _usbSerial; + set => this.RaiseAndSetIfChanged(ref _usbSerial, value); + } + + public bool SaveUsbDescriptorsEnabled + { + get => _saveUsbDescriptorsEnabled; + set => this.RaiseAndSetIfChanged(ref _saveUsbDescriptorsEnabled, value); + } + + public bool FirewireVisible + { + get => _firewireVisible; + set => this.RaiseAndSetIfChanged(ref _firewireVisible, value); + } + + public string FirewireVendorId + { + get => _firewireVendorId; + set => this.RaiseAndSetIfChanged(ref _firewireVendorId, value); + } + + public string FirewireModelId + { + get => _firewireModelId; + set => this.RaiseAndSetIfChanged(ref _firewireModelId, value); + } + + public string FirewireManufacturer + { + get => _firewireManufacturer; + set => this.RaiseAndSetIfChanged(ref _firewireManufacturer, value); + } + + public string FirewireModel + { + get => _firewireModel; + set => this.RaiseAndSetIfChanged(ref _firewireModel, value); + } + + public string FirewireGuid + { + get => _firewireGuid; + set => this.RaiseAndSetIfChanged(ref _firewireGuid, value); + } + + public bool PlextorVisible + { + get => _plextorVisible; + set => this.RaiseAndSetIfChanged(ref _plextorVisible, value); + } + + public bool PlextorEepromVisible + { + get => _plextorEepromVisible; + set => this.RaiseAndSetIfChanged(ref _plextorEepromVisible, value); + } + + public string PlextorDiscs + { + get => _plextorDiscs; + set => this.RaiseAndSetIfChanged(ref _plextorDiscs, value); + } + + public string PlextorCdReadTime + { + get => _plextorCdReadTime; + set => this.RaiseAndSetIfChanged(ref _plextorCdReadTime, value); + } + + public string PlextorCdWriteTime + { + get => _plextorCdWriteTime; + set => this.RaiseAndSetIfChanged(ref _plextorCdWriteTime, value); + } + + public bool PlextorDvdTimesVisible + { + get => _plextorDvdTimesVisible; + set => this.RaiseAndSetIfChanged(ref _plextorDvdTimesVisible, value); + } + + public string PlextorDvdReadTime + { + get => _plextorDvdReadTime; + set => this.RaiseAndSetIfChanged(ref _plextorDvdReadTime, value); + } + + public string PlextorDvdWriteTime + { + get => _plextorDvdWriteTime; + set => this.RaiseAndSetIfChanged(ref _plextorDvdWriteTime, value); + } + + public bool PlextorPoweRec + { + get => _plextorPoweRec; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRec, value); + } + + public bool PlextorPoweRecEnabled + { + get => _plextorPoweRecEnabled; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecEnabled, value); + } + + public bool PlextorPoweRecRecommendedVisible + { + get => _plextorPoweRecRecommendedVisible; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecRecommendedVisible, value); + } + + public string PlextorPoweRecRecommended + { + get => _plextorPoweRecRecommended; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecRecommended, value); + } + + public bool PlextorPoweRecSelectedVisible + { + get => _plextorPoweRecSelectedVisible; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecSelectedVisible, value); + } + + public string PlextorPoweRecSelected + { + get => _plextorPoweRecSelected; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecSelected, value); + } + + public bool PlextorPoweRecMaxVisible + { + get => _plextorPoweRecMaxVisible; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecMaxVisible, value); + } + + public string PlextorPoweRecMax + { + get => _plextorPoweRecMax; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecMax, value); + } + + public bool PlextorPoweRecLastVisible + { + get => _plextorPoweRecLastVisible; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecLastVisible, value); + } + + public string PlextorPoweRecLast + { + get => _plextorPoweRecLast; + set => this.RaiseAndSetIfChanged(ref _plextorPoweRecLast, value); + } + + public bool PlextorSilentMode + { + get => _plextorSilentMode; + set => this.RaiseAndSetIfChanged(ref _plextorSilentMode, value); + } + + public bool PlextorSilentModeEnabled + { + get => _plextorSilentModeEnabled; + set => this.RaiseAndSetIfChanged(ref _plextorSilentModeEnabled, value); + } + + public string PlextorSilentModeAccessTime + { + get => _plextorSilentModeAccessTime; + set => this.RaiseAndSetIfChanged(ref _plextorSilentModeAccessTime, value); + } + + public string PlextorSilentModeCdReadSpeedLimit + { + get => _plextorSilentModeCdReadSpeedLimit; + set => this.RaiseAndSetIfChanged(ref _plextorSilentModeCdReadSpeedLimit, value); + } + + public string PlextorSilentModeCdWriteSpeedLimit + { + get => _plextorSilentModeCdWriteSpeedLimit; + set => this.RaiseAndSetIfChanged(ref _plextorSilentModeCdWriteSpeedLimit, value); + } + + public bool PlextorSilentModeDvdReadSpeedLimitVisible + { + get => _plextorSilentModeDvdReadSpeedLimitVisible; + set => this.RaiseAndSetIfChanged(ref _plextorSilentModeDvdReadSpeedLimitVisible, value); + } + + public string PlextorSilentModeDvdReadSpeedLimit + { + get => _plextorSilentModeDvdReadSpeedLimit; + set => this.RaiseAndSetIfChanged(ref _plextorSilentModeDvdReadSpeedLimit, value); + } + + public bool PlextorGigaRec + { + get => _plextorGigaRec; + set => this.RaiseAndSetIfChanged(ref _plextorGigaRec, value); + } + + public bool PlextorSecuRec + { + get => _plextorSecuRec; + set => this.RaiseAndSetIfChanged(ref _plextorSecuRec, value); + } + + public bool PlextorSpeedRead + { + get => _plextorSpeedRead; + set => this.RaiseAndSetIfChanged(ref _plextorSpeedRead, value); + } + + public bool PlextorSpeedEnabled + { + get => _plextorSpeedEnabled; + set => this.RaiseAndSetIfChanged(ref _plextorSpeedEnabled, value); + } + + public bool PlextorHiding + { + get => _plextorHiding; + set => this.RaiseAndSetIfChanged(ref _plextorHiding, value); + } + + public bool PlextorHidesRecordables + { + get => _plextorHidesRecordables; + set => this.RaiseAndSetIfChanged(ref _plextorHidesRecordables, value); + } + + public bool PlextorHidesSessions + { + get => _plextorHidesSessions; + set => this.RaiseAndSetIfChanged(ref _plextorHidesSessions, value); + } + + public bool PlextorVariRec + { + get => _plextorVariRec; + set => this.RaiseAndSetIfChanged(ref _plextorVariRec, value); + } + + public string PlextorDvd + { + get => _plextorDvd; + set => this.RaiseAndSetIfChanged(ref _plextorDvd, value); + } + + public bool PlextorVariRecDvd + { + get => _plextorVariRecDvd; + set => this.RaiseAndSetIfChanged(ref _plextorVariRecDvd, value); + } + + public bool PlextorBitSetting + { + get => _plextorBitSetting; + set => this.RaiseAndSetIfChanged(ref _plextorBitSetting, value); + } + + public bool PlextorBitSettingDl + { + get => _plextorBitSettingDl; + set => this.RaiseAndSetIfChanged(ref _plextorBitSettingDl, value); + } + + public bool PlextorDvdPlusWriteTest + { + get => _plextorDvdPlusWriteTest; + set => this.RaiseAndSetIfChanged(ref _plextorDvdPlusWriteTest, value); + } + + public bool Kreon + { + get => _kreon; + set => this.RaiseAndSetIfChanged(ref _kreon, value); + } + + public bool KreonChallengeResponse + { + get => _kreonChallengeResponse; + set => this.RaiseAndSetIfChanged(ref _kreonChallengeResponse, value); + } + + public bool KreonDecryptSs + { + get => _kreonDecryptSs; + set => this.RaiseAndSetIfChanged(ref _kreonDecryptSs, value); + } + + public bool KreonXtremeUnlock + { + get => _kreonXtremeUnlock; + set => this.RaiseAndSetIfChanged(ref _kreonXtremeUnlock, value); + } + + public bool KreonWxripperUnlock + { + get => _kreonWxripperUnlock; + set => this.RaiseAndSetIfChanged(ref _kreonWxripperUnlock, value); + } + + public bool KreonChallengeResponse360 + { + get => _kreonChallengeResponse360; + set => this.RaiseAndSetIfChanged(ref _kreonChallengeResponse360, value); + } + + public bool KreonDecryptSs360 + { + get => _kreonDecryptSs360; + set => this.RaiseAndSetIfChanged(ref _kreonDecryptSs360, value); + } + + public bool KreonXtremeUnlock360 + { + get => _kreonXtremeUnlock360; + set => this.RaiseAndSetIfChanged(ref _kreonXtremeUnlock360, value); + } + + public bool KreonWxripperUnlock360 + { + get => _kreonWxripperUnlock360; + set => this.RaiseAndSetIfChanged(ref _kreonWxripperUnlock360, value); + } + + public bool KreonLock + { + get => _kreonLock; + set => this.RaiseAndSetIfChanged(ref _kreonLock, value); + } + + public bool KreonErrorSkipping + { + get => _kreonErrorSkipping; + set => this.RaiseAndSetIfChanged(ref _kreonErrorSkipping, value); + } + + public bool Ssc + { + get => _ssc; + set => this.RaiseAndSetIfChanged(ref _ssc, value); + } + + public string BlockLimits + { + get => _blockLimits; + set => this.RaiseAndSetIfChanged(ref _blockLimits, value); + } + + public string MinBlockSize + { + get => _minBlockSize; + set => this.RaiseAndSetIfChanged(ref _minBlockSize, value); + } + + public string MaxBlockSize + { + get => _maxBlockSize; + set => this.RaiseAndSetIfChanged(ref _maxBlockSize, value); + } + + public string BlockSizeGranularity + { + get => _blockSizeGranularity; + set => this.RaiseAndSetIfChanged(ref _blockSizeGranularity, value); + } + + public string Densities + { + get => _densities; + set => this.RaiseAndSetIfChanged(ref _densities, value); + } + + public string MediumTypes + { + get => _mediumTypes; + set => this.RaiseAndSetIfChanged(ref _mediumTypes, value); + } + + public string MediumDensity + { + get => _mediumDensity; + set => this.RaiseAndSetIfChanged(ref _mediumDensity, value); + } + + public string SecureDigital + { + get => _secureDigital; + set => this.RaiseAndSetIfChanged(ref _secureDigital, value); + } + + public string SdMm + { + get => _sdMm; + set => this.RaiseAndSetIfChanged(ref _sdMm, value); + } + + public string Cid + { + get => _cid; + set => this.RaiseAndSetIfChanged(ref _cid, value); + } + + public string Csd + { + get => _csd; + set => this.RaiseAndSetIfChanged(ref _csd, value); + } + + public string Ocr + { + get => _ocr; + set => this.RaiseAndSetIfChanged(ref _ocr, value); + } + + public string ExtendedCsd + { + get => _extendedCsd; + set => this.RaiseAndSetIfChanged(ref _extendedCsd, value); + } + + public string Scr + { + get => _scr; + set => this.RaiseAndSetIfChanged(ref _scr, value); + } + + public PcmciaInfo PcmciaInfo + { + get => _pcmciaInfo; + set => this.RaiseAndSetIfChanged(ref _pcmciaInfo, value); + } + + public ScsiInfo ScsiInfo + { + get => _scsiInfo; + set => this.RaiseAndSetIfChanged(ref _scsiInfo, value); + } + + public AtaInfo AtaInfo + { + get => _ataInfo; + set => this.RaiseAndSetIfChanged(ref _ataInfo, value); + } + + public SdMmcInfo SdMmcInfo + { + get => _sdMmcInfo; + set => this.RaiseAndSetIfChanged(ref _sdMmcInfo, value); + } + + async void ExecuteSaveUsbDescriptorsCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_devInfo.UsbDescriptors, 0, _devInfo.UsbDescriptors.Length); + + saveFs.Close(); } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Panels/FileSystemViewModel.cs b/Aaru.Gui/ViewModels/Panels/FileSystemViewModel.cs index dddb30513..32e9c4112 100644 --- a/Aaru.Gui/ViewModels/Panels/FileSystemViewModel.cs +++ b/Aaru.Gui/ViewModels/Panels/FileSystemViewModel.cs @@ -33,71 +33,70 @@ using JetBrains.Annotations; using Schemas; -namespace Aaru.Gui.ViewModels.Panels +namespace Aaru.Gui.ViewModels.Panels; + +public sealed class FileSystemViewModel { - public sealed class FileSystemViewModel + public FileSystemViewModel([NotNull] FileSystemType xmlFsType, string information) { - public FileSystemViewModel([NotNull] FileSystemType xmlFsType, string information) - { - TypeText = $"Filesystem type: {xmlFsType.Type}"; - VolumeNameText = $"Volume name: {xmlFsType.VolumeName}"; - SerialNumberText = $"Serial number: {xmlFsType.VolumeSerial}"; - ApplicationIdentifierText = $"Application identifier: {xmlFsType.ApplicationIdentifier}"; - SystemIdentifierText = $"System identifier: {xmlFsType.SystemIdentifier}"; - VolumeSetIdentifierText = $"Volume set identifier: {xmlFsType.VolumeSetIdentifier}"; - DataPreparerIdentifierText = $"Data preparer identifier: {xmlFsType.DataPreparerIdentifier}"; - PublisherIdentifierText = $"Publisher identifier: {xmlFsType.PublisherIdentifier}"; - CreationDateText = $"Volume created on {xmlFsType.CreationDate:F}"; - EffectiveDateText = $"Volume effective from {xmlFsType.EffectiveDate:F}"; - ModificationDateText = $"Volume last modified on {xmlFsType.ModificationDate:F}"; - ExpirationDateText = $"Volume expired on {xmlFsType.ExpirationDate:F}"; - BackupDateText = $"Volume last backed up on {xmlFsType.BackupDate:F}"; + TypeText = $"Filesystem type: {xmlFsType.Type}"; + VolumeNameText = $"Volume name: {xmlFsType.VolumeName}"; + SerialNumberText = $"Serial number: {xmlFsType.VolumeSerial}"; + ApplicationIdentifierText = $"Application identifier: {xmlFsType.ApplicationIdentifier}"; + SystemIdentifierText = $"System identifier: {xmlFsType.SystemIdentifier}"; + VolumeSetIdentifierText = $"Volume set identifier: {xmlFsType.VolumeSetIdentifier}"; + DataPreparerIdentifierText = $"Data preparer identifier: {xmlFsType.DataPreparerIdentifier}"; + PublisherIdentifierText = $"Publisher identifier: {xmlFsType.PublisherIdentifier}"; + CreationDateText = $"Volume created on {xmlFsType.CreationDate:F}"; + EffectiveDateText = $"Volume effective from {xmlFsType.EffectiveDate:F}"; + ModificationDateText = $"Volume last modified on {xmlFsType.ModificationDate:F}"; + ExpirationDateText = $"Volume expired on {xmlFsType.ExpirationDate:F}"; + BackupDateText = $"Volume last backed up on {xmlFsType.BackupDate:F}"; - ClustersText = - $"Volume has {xmlFsType.Clusters} clusters of {xmlFsType.ClusterSize} bytes each (total of {xmlFsType.Clusters * xmlFsType.ClusterSize} bytes)"; + ClustersText = + $"Volume has {xmlFsType.Clusters} clusters of {xmlFsType.ClusterSize} bytes each (total of {xmlFsType.Clusters * xmlFsType.ClusterSize} bytes)"; - FreeClustersText = - $"Volume has {xmlFsType.FreeClusters} clusters free ({xmlFsType.FreeClusters / xmlFsType.Clusters:P})"; + FreeClustersText = + $"Volume has {xmlFsType.FreeClusters} clusters free ({xmlFsType.FreeClusters / xmlFsType.Clusters:P})"; - FilesText = $"Volume contains {xmlFsType.Files} files"; - BootableChecked = xmlFsType.Bootable; - DirtyChecked = xmlFsType.Dirty; - InformationText = information; + FilesText = $"Volume contains {xmlFsType.Files} files"; + BootableChecked = xmlFsType.Bootable; + DirtyChecked = xmlFsType.Dirty; + InformationText = information; - CreationDateVisible = xmlFsType.CreationDateSpecified; - EffectiveDateVisible = xmlFsType.EffectiveDateSpecified; - ModificationDateVisible = xmlFsType.ModificationDateSpecified; - ExpirationDateVisible = xmlFsType.ExpirationDateSpecified; - BackupDateVisible = xmlFsType.BackupDateSpecified; - FreeClustersVisible = xmlFsType.FreeClustersSpecified; - FilesVisible = xmlFsType.FilesSpecified; - } - - public string TypeText { get; } - public string VolumeNameText { get; } - public string SerialNumberText { get; } - public string ApplicationIdentifierText { get; } - public string SystemIdentifierText { get; } - public string VolumeSetIdentifierText { get; } - public string DataPreparerIdentifierText { get; } - public string PublisherIdentifierText { get; } - public string CreationDateText { get; } - public string EffectiveDateText { get; } - public string ModificationDateText { get; } - public string ExpirationDateText { get; } - public string BackupDateText { get; } - public string ClustersText { get; } - public string FreeClustersText { get; } - public string FilesText { get; } - public bool BootableChecked { get; } - public bool DirtyChecked { get; } - public string InformationText { get; } - public bool CreationDateVisible { get; } - public bool EffectiveDateVisible { get; } - public bool ModificationDateVisible { get; } - public bool ExpirationDateVisible { get; } - public bool BackupDateVisible { get; } - public bool FreeClustersVisible { get; } - public bool FilesVisible { get; } + CreationDateVisible = xmlFsType.CreationDateSpecified; + EffectiveDateVisible = xmlFsType.EffectiveDateSpecified; + ModificationDateVisible = xmlFsType.ModificationDateSpecified; + ExpirationDateVisible = xmlFsType.ExpirationDateSpecified; + BackupDateVisible = xmlFsType.BackupDateSpecified; + FreeClustersVisible = xmlFsType.FreeClustersSpecified; + FilesVisible = xmlFsType.FilesSpecified; } + + public string TypeText { get; } + public string VolumeNameText { get; } + public string SerialNumberText { get; } + public string ApplicationIdentifierText { get; } + public string SystemIdentifierText { get; } + public string VolumeSetIdentifierText { get; } + public string DataPreparerIdentifierText { get; } + public string PublisherIdentifierText { get; } + public string CreationDateText { get; } + public string EffectiveDateText { get; } + public string ModificationDateText { get; } + public string ExpirationDateText { get; } + public string BackupDateText { get; } + public string ClustersText { get; } + public string FreeClustersText { get; } + public string FilesText { get; } + public bool BootableChecked { get; } + public bool DirtyChecked { get; } + public string InformationText { get; } + public bool CreationDateVisible { get; } + public bool EffectiveDateVisible { get; } + public bool ModificationDateVisible { get; } + public bool ExpirationDateVisible { get; } + public bool BackupDateVisible { get; } + public bool FreeClustersVisible { get; } + public bool FilesVisible { get; } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Panels/ImageInfoViewModel.cs b/Aaru.Gui/ViewModels/Panels/ImageInfoViewModel.cs index 050c71bc2..74e12497d 100644 --- a/Aaru.Gui/ViewModels/Panels/ImageInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Panels/ImageInfoViewModel.cs @@ -58,796 +58,795 @@ using Schemas; using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry; using Session = Aaru.CommonTypes.Structs.Session; -namespace Aaru.Gui.ViewModels.Panels +namespace Aaru.Gui.ViewModels.Panels; + +public sealed class ImageInfoViewModel : ViewModelBase { - public sealed class ImageInfoViewModel : ViewModelBase + readonly IFilter _filter; + readonly IMediaImage _imageFormat; + readonly string _imagePath; + DecodeMediaTags _decodeMediaTags; + ImageChecksum _imageChecksum; + ImageConvert _imageConvert; + ImageEntropy _imageEntropy; + ImageSidecar _imageSidecar; + ImageVerify _imageVerify; + ViewSector _viewSector; + + public ImageInfoViewModel(string imagePath, IFilter filter, IMediaImage imageFormat, Window view) + { - readonly IFilter _filter; - readonly IMediaImage _imageFormat; - readonly string _imagePath; - DecodeMediaTags _decodeMediaTags; - ImageChecksum _imageChecksum; - ImageConvert _imageConvert; - ImageEntropy _imageEntropy; - ImageSidecar _imageSidecar; - ImageVerify _imageVerify; - ViewSector _viewSector; + _imagePath = imagePath; + _filter = filter; + _imageFormat = imageFormat; + IAssetLoader assets = AvaloniaLocator.Current.GetService(); + MediaTagsList = new ObservableCollection(); + SectorTagsList = new ObservableCollection(); + Sessions = new ObservableCollection(); + Tracks = new ObservableCollection(); + DumpHardwareList = new ObservableCollection(); + EntropyCommand = ReactiveCommand.Create(ExecuteEntropyCommand); + VerifyCommand = ReactiveCommand.Create(ExecuteVerifyCommand); + ChecksumCommand = ReactiveCommand.Create(ExecuteChecksumCommand); + ConvertCommand = ReactiveCommand.Create(ExecuteConvertCommand); + CreateSidecarCommand = ReactiveCommand.Create(ExecuteCreateSidecarCommand); + ViewSectorsCommand = ReactiveCommand.Create(ExecuteViewSectorsCommand); + DecodeMediaTagCommand = ReactiveCommand.Create(ExecuteDecodeMediaTagCommand); - public ImageInfoViewModel(string imagePath, IFilter filter, IMediaImage imageFormat, Window view) + var genericHddIcon = + new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png"))); + var genericOpticalIcon = + new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-optical.png"))); + + var genericFolderIcon = + new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png"))); + + var mediaResource = new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{imageFormat.Info.MediaType}.png"); + + MediaLogo = assets.Exists(mediaResource) + ? new Bitmap(assets.Open(mediaResource)) + : imageFormat.Info.XmlMediaType == XmlMediaType.BlockMedia + ? genericHddIcon + : imageFormat.Info.XmlMediaType == XmlMediaType.OpticalDisc + ? genericOpticalIcon + : genericFolderIcon; + + ImagePathText = $"Path: {imagePath}"; + FilterText = $"Filter: {filter.Name}"; + ImageIdentifiedText = $"Image format identified by {imageFormat.Name} ({imageFormat.Id})."; + + ImageFormatText = !string.IsNullOrWhiteSpace(imageFormat.Info.Version) + ? $"Format: {imageFormat.Format} version {imageFormat.Info.Version}" + : $"Format: {imageFormat.Format}"; + + ImageSizeText = $"Image without headers is {imageFormat.Info.ImageSize} bytes long"; + + SectorsText = + $"Contains a media of {imageFormat.Info.Sectors} sectors with a maximum sector size of {imageFormat.Info.SectorSize} bytes (if all sectors are of the same size this would be {imageFormat.Info.Sectors * imageFormat.Info.SectorSize} bytes)"; + + MediaTypeText = + $"Contains a media of type {imageFormat.Info.MediaType} and XML type {imageFormat.Info.XmlMediaType}"; + + HasPartitionsText = $"{(imageFormat.Info.HasPartitions ? "Has" : "Doesn't have")} partitions"; + HasSessionsText = $"{(imageFormat.Info.HasSessions ? "Has" : "Doesn't have")} sessions"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.Application)) + ApplicationText = !string.IsNullOrWhiteSpace(imageFormat.Info.ApplicationVersion) + ? $"Was created with {imageFormat.Info.Application} version {imageFormat.Info.ApplicationVersion}" + : $"Was created with {imageFormat.Info.Application}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.Creator)) + CreatorText = $"Created by: {imageFormat.Info.Creator}"; + + if(imageFormat.Info.CreationTime != DateTime.MinValue) + CreationTimeText = $"Created on {imageFormat.Info.CreationTime}"; + + if(imageFormat.Info.LastModificationTime != DateTime.MinValue) + LastModificationTimeText = $"Last modified on {imageFormat.Info.LastModificationTime}"; + + if(imageFormat.Info.MediaSequence != 0 && + imageFormat.Info.LastMediaSequence != 0) + MediaSequenceText = + $"Media is number {imageFormat.Info.MediaSequence} on a set of {imageFormat.Info.LastMediaSequence} medias"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaTitle)) + MediaTitleText = $"Media title: {imageFormat.Info.MediaTitle}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaManufacturer)) + MediaManufacturerText = $"Media manufacturer: {imageFormat.Info.MediaManufacturer}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaModel)) + MediaModelText = $"Media model: {imageFormat.Info.MediaModel}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaSerialNumber)) + MediaSerialNumberText = $"Media serial number: {imageFormat.Info.MediaSerialNumber}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaBarcode)) + MediaBarcodeText = $"Media barcode: {imageFormat.Info.MediaBarcode}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaPartNumber)) + MediaPartNumberText = $"Media part number: {imageFormat.Info.MediaPartNumber}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveManufacturer)) + DriveManufacturerText = $"Drive manufacturer: {imageFormat.Info.DriveManufacturer}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveModel)) + DriveModelText = $"Drive model: {imageFormat.Info.DriveModel}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveSerialNumber)) + DriveSerialNumberText = $"Drive serial number: {imageFormat.Info.DriveSerialNumber}"; + + if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveFirmwareRevision)) + DriveFirmwareRevisionText = $"Drive firmware info: {imageFormat.Info.DriveFirmwareRevision}"; + + if(imageFormat.Info.Cylinders > 0 && + imageFormat.Info.Heads > 0 && + imageFormat.Info.SectorsPerTrack > 0 && + imageFormat.Info.XmlMediaType != XmlMediaType.OpticalDisc && + (!(imageFormat is ITapeImage tapeImage) || !tapeImage.IsTape)) + MediaGeometryText = + $"Media geometry: {imageFormat.Info.Cylinders} cylinders, {imageFormat.Info.Heads} heads, {imageFormat.Info.SectorsPerTrack} sectors per track"; + + if(imageFormat.Info.ReadableMediaTags != null && + imageFormat.Info.ReadableMediaTags.Count > 0) + foreach(MediaTagType tag in imageFormat.Info.ReadableMediaTags.OrderBy(t => t)) + MediaTagsList.Add(tag.ToString()); + + if(imageFormat.Info.ReadableSectorTags != null && + imageFormat.Info.ReadableSectorTags.Count > 0) + foreach(SectorTagType tag in imageFormat.Info.ReadableSectorTags.OrderBy(t => t)) + SectorTagsList.Add(tag.ToString()); + + PeripheralDeviceTypes scsiDeviceType = PeripheralDeviceTypes.DirectAccess; + byte[] scsiInquiryData = null; + Inquiry? scsiInquiry = null; + Modes.DecodedMode? scsiMode = null; + byte[] scsiModeSense6 = null; + byte[] scsiModeSense10 = null; + ErrorNumber errno; + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_INQUIRY) == true) { - _imagePath = imagePath; - _filter = filter; - _imageFormat = imageFormat; - IAssetLoader assets = AvaloniaLocator.Current.GetService(); - MediaTagsList = new ObservableCollection(); - SectorTagsList = new ObservableCollection(); - Sessions = new ObservableCollection(); - Tracks = new ObservableCollection(); - DumpHardwareList = new ObservableCollection(); - EntropyCommand = ReactiveCommand.Create(ExecuteEntropyCommand); - VerifyCommand = ReactiveCommand.Create(ExecuteVerifyCommand); - ChecksumCommand = ReactiveCommand.Create(ExecuteChecksumCommand); - ConvertCommand = ReactiveCommand.Create(ExecuteConvertCommand); - CreateSidecarCommand = ReactiveCommand.Create(ExecuteCreateSidecarCommand); - ViewSectorsCommand = ReactiveCommand.Create(ExecuteViewSectorsCommand); - DecodeMediaTagCommand = ReactiveCommand.Create(ExecuteDecodeMediaTagCommand); - - var genericHddIcon = - new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png"))); - - var genericOpticalIcon = - new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-optical.png"))); - - var genericFolderIcon = - new Bitmap(assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png"))); - - var mediaResource = new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{imageFormat.Info.MediaType}.png"); - - MediaLogo = assets.Exists(mediaResource) - ? new Bitmap(assets.Open(mediaResource)) - : imageFormat.Info.XmlMediaType == XmlMediaType.BlockMedia - ? genericHddIcon - : imageFormat.Info.XmlMediaType == XmlMediaType.OpticalDisc - ? genericOpticalIcon - : genericFolderIcon; - - ImagePathText = $"Path: {imagePath}"; - FilterText = $"Filter: {filter.Name}"; - ImageIdentifiedText = $"Image format identified by {imageFormat.Name} ({imageFormat.Id})."; - - ImageFormatText = !string.IsNullOrWhiteSpace(imageFormat.Info.Version) - ? $"Format: {imageFormat.Format} version {imageFormat.Info.Version}" - : $"Format: {imageFormat.Format}"; - - ImageSizeText = $"Image without headers is {imageFormat.Info.ImageSize} bytes long"; - - SectorsText = - $"Contains a media of {imageFormat.Info.Sectors} sectors with a maximum sector size of {imageFormat.Info.SectorSize} bytes (if all sectors are of the same size this would be {imageFormat.Info.Sectors * imageFormat.Info.SectorSize} bytes)"; - - MediaTypeText = - $"Contains a media of type {imageFormat.Info.MediaType} and XML type {imageFormat.Info.XmlMediaType}"; - - HasPartitionsText = $"{(imageFormat.Info.HasPartitions ? "Has" : "Doesn't have")} partitions"; - HasSessionsText = $"{(imageFormat.Info.HasSessions ? "Has" : "Doesn't have")} sessions"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.Application)) - ApplicationText = !string.IsNullOrWhiteSpace(imageFormat.Info.ApplicationVersion) - ? $"Was created with {imageFormat.Info.Application} version {imageFormat.Info.ApplicationVersion}" - : $"Was created with {imageFormat.Info.Application}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.Creator)) - CreatorText = $"Created by: {imageFormat.Info.Creator}"; - - if(imageFormat.Info.CreationTime != DateTime.MinValue) - CreationTimeText = $"Created on {imageFormat.Info.CreationTime}"; - - if(imageFormat.Info.LastModificationTime != DateTime.MinValue) - LastModificationTimeText = $"Last modified on {imageFormat.Info.LastModificationTime}"; - - if(imageFormat.Info.MediaSequence != 0 && - imageFormat.Info.LastMediaSequence != 0) - MediaSequenceText = - $"Media is number {imageFormat.Info.MediaSequence} on a set of {imageFormat.Info.LastMediaSequence} medias"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaTitle)) - MediaTitleText = $"Media title: {imageFormat.Info.MediaTitle}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaManufacturer)) - MediaManufacturerText = $"Media manufacturer: {imageFormat.Info.MediaManufacturer}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaModel)) - MediaModelText = $"Media model: {imageFormat.Info.MediaModel}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaSerialNumber)) - MediaSerialNumberText = $"Media serial number: {imageFormat.Info.MediaSerialNumber}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaBarcode)) - MediaBarcodeText = $"Media barcode: {imageFormat.Info.MediaBarcode}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.MediaPartNumber)) - MediaPartNumberText = $"Media part number: {imageFormat.Info.MediaPartNumber}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveManufacturer)) - DriveManufacturerText = $"Drive manufacturer: {imageFormat.Info.DriveManufacturer}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveModel)) - DriveModelText = $"Drive model: {imageFormat.Info.DriveModel}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveSerialNumber)) - DriveSerialNumberText = $"Drive serial number: {imageFormat.Info.DriveSerialNumber}"; - - if(!string.IsNullOrWhiteSpace(imageFormat.Info.DriveFirmwareRevision)) - DriveFirmwareRevisionText = $"Drive firmware info: {imageFormat.Info.DriveFirmwareRevision}"; - - if(imageFormat.Info.Cylinders > 0 && - imageFormat.Info.Heads > 0 && - imageFormat.Info.SectorsPerTrack > 0 && - imageFormat.Info.XmlMediaType != XmlMediaType.OpticalDisc && - (!(imageFormat is ITapeImage tapeImage) || !tapeImage.IsTape)) - MediaGeometryText = - $"Media geometry: {imageFormat.Info.Cylinders} cylinders, {imageFormat.Info.Heads} heads, {imageFormat.Info.SectorsPerTrack} sectors per track"; - - if(imageFormat.Info.ReadableMediaTags != null && - imageFormat.Info.ReadableMediaTags.Count > 0) - foreach(MediaTagType tag in imageFormat.Info.ReadableMediaTags.OrderBy(t => t)) - MediaTagsList.Add(tag.ToString()); - - if(imageFormat.Info.ReadableSectorTags != null && - imageFormat.Info.ReadableSectorTags.Count > 0) - foreach(SectorTagType tag in imageFormat.Info.ReadableSectorTags.OrderBy(t => t)) - SectorTagsList.Add(tag.ToString()); - - PeripheralDeviceTypes scsiDeviceType = PeripheralDeviceTypes.DirectAccess; - byte[] scsiInquiryData = null; - Inquiry? scsiInquiry = null; - Modes.DecodedMode? scsiMode = null; - byte[] scsiModeSense6 = null; - byte[] scsiModeSense10 = null; - ErrorNumber errno; - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_INQUIRY) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_INQUIRY, out scsiInquiryData); - - if(errno == ErrorNumber.NoError) - { - scsiDeviceType = (PeripheralDeviceTypes)(scsiInquiryData[0] & 0x1F); - - scsiInquiry = Inquiry.Decode(scsiInquiryData); - } - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_MODESENSE_6) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_MODESENSE_6, out scsiModeSense6); - - if(errno == ErrorNumber.NoError) - scsiMode = Modes.DecodeMode6(scsiModeSense6, scsiDeviceType); - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_MODESENSE_10) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_MODESENSE_10, out scsiModeSense10); - - if(errno == ErrorNumber.NoError) - scsiMode = Modes.DecodeMode10(scsiModeSense10, scsiDeviceType); - } - - ScsiInfo = new ScsiInfo - { - DataContext = new ScsiInfoViewModel(scsiInquiryData, scsiInquiry, null, scsiMode, scsiDeviceType, - scsiModeSense6, scsiModeSense10, null, view) - }; - - byte[] ataIdentify = null; - byte[] atapiIdentify = null; - errno = ErrorNumber.NoData; - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.ATA_IDENTIFY) == true) - errno = imageFormat.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out ataIdentify); - - else if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.ATAPI_IDENTIFY) == true) - errno = imageFormat.ReadMediaTag(MediaTagType.ATAPI_IDENTIFY, out atapiIdentify); + errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_INQUIRY, out scsiInquiryData); if(errno == ErrorNumber.NoError) - AtaInfo = new AtaInfo - { - DataContext = new AtaInfoViewModel(ataIdentify, atapiIdentify, null, view) - }; - - byte[] toc = null; - TOC.CDTOC? decodedToc = null; - byte[] fullToc = null; - FullTOC.CDFullTOC? decodedFullToc = null; - byte[] pma = null; - byte[] atip = null; - ATIP.CDATIP decodedAtip = null; - byte[] cdtext = null; - CDTextOnLeadIn.CDText? decodedCdText = null; - string mediaCatalogueNumber = null; - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_TOC) == true) { - errno = imageFormat.ReadMediaTag(MediaTagType.CD_TOC, out toc); + scsiDeviceType = (PeripheralDeviceTypes)(scsiInquiryData[0] & 0x1F); - if(errno == ErrorNumber.NoError && - toc.Length > 0) - { - ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(toc, 0)); - - if(dataLen + 2 != toc.Length) - { - byte[] tmp = new byte[toc.Length + 2]; - Array.Copy(toc, 0, tmp, 2, toc.Length); - tmp[0] = (byte)((toc.Length & 0xFF00) >> 8); - tmp[1] = (byte)(toc.Length & 0xFF); - toc = tmp; - } - - decodedToc = TOC.Decode(toc); - } + scsiInquiry = Inquiry.Decode(scsiInquiryData); } + } - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_FullTOC) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.CD_FullTOC, out fullToc); - - if(errno == ErrorNumber.NoError && - fullToc.Length > 0) - { - ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(fullToc, 0)); - - if(dataLen + 2 != fullToc.Length) - { - byte[] tmp = new byte[fullToc.Length + 2]; - Array.Copy(fullToc, 0, tmp, 2, fullToc.Length); - tmp[0] = (byte)((fullToc.Length & 0xFF00) >> 8); - tmp[1] = (byte)(fullToc.Length & 0xFF); - fullToc = tmp; - } - - decodedFullToc = FullTOC.Decode(fullToc); - } - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_PMA) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.CD_PMA, out pma); - - if(errno == ErrorNumber.NoError && - pma.Length > 0) - { - ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(pma, 0)); - - if(dataLen + 2 != pma.Length) - { - byte[] tmp = new byte[pma.Length + 2]; - Array.Copy(pma, 0, tmp, 2, pma.Length); - tmp[0] = (byte)((pma.Length & 0xFF00) >> 8); - tmp[1] = (byte)(pma.Length & 0xFF); - pma = tmp; - } - } - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_ATIP) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.CD_ATIP, out atip); - - if(errno == ErrorNumber.NoError) - { - uint dataLen = Swapping.Swap(BitConverter.ToUInt32(atip, 0)); - - if(dataLen + 4 != atip.Length) - { - byte[] tmp = new byte[atip.Length + 4]; - Array.Copy(atip, 0, tmp, 4, atip.Length); - tmp[0] = (byte)((atip.Length & 0xFF000000) >> 24); - tmp[1] = (byte)((atip.Length & 0xFF0000) >> 16); - tmp[2] = (byte)((atip.Length & 0xFF00) >> 8); - tmp[3] = (byte)(atip.Length & 0xFF); - atip = tmp; - } - - decodedAtip = ATIP.Decode(atip); - } - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_TEXT) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.CD_TEXT, out cdtext); - - if(errno == ErrorNumber.NoError) - { - uint dataLen = Swapping.Swap(BitConverter.ToUInt32(cdtext, 0)); - - if(dataLen + 4 != cdtext.Length) - { - byte[] tmp = new byte[cdtext.Length + 4]; - Array.Copy(cdtext, 0, tmp, 4, cdtext.Length); - tmp[0] = (byte)((cdtext.Length & 0xFF000000) >> 24); - tmp[1] = (byte)((cdtext.Length & 0xFF0000) >> 16); - tmp[2] = (byte)((cdtext.Length & 0xFF00) >> 8); - tmp[3] = (byte)(cdtext.Length & 0xFF); - cdtext = tmp; - } - - decodedCdText = CDTextOnLeadIn.Decode(cdtext); - } - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_MCN) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.CD_MCN, out byte[] mcn); - - if(errno == ErrorNumber.NoError) - mediaCatalogueNumber = Encoding.UTF8.GetString(mcn); - } - - CompactDiscInfo = new CompactDiscInfo - { - DataContext = new CompactDiscInfoViewModel(toc, atip, null, null, fullToc, pma, cdtext, decodedToc, - decodedAtip, null, decodedFullToc, decodedCdText, null, - mediaCatalogueNumber, null, view) - }; - - byte[] dvdPfi = null; - byte[] dvdDmi = null; - byte[] dvdCmi = null; - byte[] hddvdCopyrightInformation = null; - byte[] dvdBca = null; - PFI.PhysicalFormatInformation? decodedPfi = null; - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_PFI) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.DVD_PFI, out dvdPfi); - - if(errno == ErrorNumber.NoError) - decodedPfi = PFI.Decode(dvdPfi, imageFormat.Info.MediaType); - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_DMI) == true) - imageFormat.ReadMediaTag(MediaTagType.DVD_DMI, out dvdDmi); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_CMI) == true) - imageFormat.ReadMediaTag(MediaTagType.DVD_CMI, out dvdCmi); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.HDDVD_CPI) == true) - imageFormat.ReadMediaTag(MediaTagType.HDDVD_CPI, out hddvdCopyrightInformation); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_BCA) == true) - imageFormat.ReadMediaTag(MediaTagType.DVD_BCA, out dvdBca); - - DvdInfo = new DvdInfo - { - DataContext = new DvdInfoViewModel(imageFormat.Info.MediaType, dvdPfi, dvdDmi, dvdCmi, - hddvdCopyrightInformation, dvdBca, null, decodedPfi, view) - }; - - byte[] dvdRamDds = null; - byte[] dvdRamCartridgeStatus = null; - byte[] dvdRamSpareArea = null; - byte[] lastBorderOutRmd = null; - byte[] dvdPreRecordedInfo = null; - byte[] dvdrMediaIdentifier = null; - byte[] dvdrPhysicalInformation = null; - byte[] hddvdrMediumStatus = null; - byte[] dvdrLayerCapacity = null; - byte[] dvdrDlMiddleZoneStart = null; - byte[] dvdrDlJumpIntervalSize = null; - byte[] dvdrDlManualLayerJumpStartLba = null; - byte[] dvdPlusAdip = null; - byte[] dvdPlusDcb = null; - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_DDS) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDRAM_DDS, out dvdRamDds); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_MediumStatus) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDRAM_MediumStatus, out dvdRamCartridgeStatus); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_SpareArea) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDRAM_SpareArea, out dvdRamSpareArea); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_RMD) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDR_RMD, out lastBorderOutRmd); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_PreRecordedInfo) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDR_PreRecordedInfo, out dvdPreRecordedInfo); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_MediaIdentifier) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDR_MediaIdentifier, out dvdrMediaIdentifier); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_PFI) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDR_PFI, out dvdrPhysicalInformation); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.HDDVD_MediumStatus) == true) - imageFormat.ReadMediaTag(MediaTagType.HDDVD_MediumStatus, out hddvdrMediumStatus); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_LayerCapacity) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDDL_LayerCapacity, out dvdrLayerCapacity); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_MiddleZoneAddress) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDDL_MiddleZoneAddress, out dvdrDlMiddleZoneStart); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_JumpIntervalSize) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDDL_JumpIntervalSize, out dvdrDlJumpIntervalSize); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_ManualLayerJumpLBA) == true) - imageFormat.ReadMediaTag(MediaTagType.DVDDL_ManualLayerJumpLBA, out dvdrDlManualLayerJumpStartLba); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_ADIP) == true) - imageFormat.ReadMediaTag(MediaTagType.DVD_ADIP, out dvdPlusAdip); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DCB) == true) - imageFormat.ReadMediaTag(MediaTagType.DCB, out dvdPlusDcb); - - DvdWritableInfo = new DvdWritableInfo - { - DataContext = new DvdWritableInfoViewModel(imageFormat.Info.MediaType, dvdRamDds, dvdRamCartridgeStatus, - dvdRamSpareArea, lastBorderOutRmd, dvdPreRecordedInfo, - dvdrMediaIdentifier, dvdrPhysicalInformation, - hddvdrMediumStatus, null, dvdrLayerCapacity, - dvdrDlMiddleZoneStart, dvdrDlJumpIntervalSize, - dvdrDlManualLayerJumpStartLba, null, dvdPlusAdip, dvdPlusDcb, - view) - }; - - byte[] blurayBurstCuttingArea = null; - byte[] blurayCartridgeStatus = null; - byte[] blurayDds = null; - byte[] blurayDiscInformation = null; - byte[] blurayPowResources = null; - byte[] bluraySpareAreaInformation = null; - byte[] blurayTrackResources = null; - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_BCA) == true) - imageFormat.ReadMediaTag(MediaTagType.BD_BCA, out blurayBurstCuttingArea); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_CartridgeStatus) == true) - imageFormat.ReadMediaTag(MediaTagType.BD_CartridgeStatus, out blurayCartridgeStatus); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_DDS) == true) - imageFormat.ReadMediaTag(MediaTagType.BD_DDS, out blurayDds); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_DI) == true) - imageFormat.ReadMediaTag(MediaTagType.BD_DI, out blurayDiscInformation); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_POWResourcesInformation) == true) - imageFormat.ReadMediaTag(MediaTagType.MMC_POWResourcesInformation, out blurayPowResources); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_SpareArea) == true) - imageFormat.ReadMediaTag(MediaTagType.BD_SpareArea, out bluraySpareAreaInformation); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_TrackResourcesInformation) == true) - imageFormat.ReadMediaTag(MediaTagType.MMC_TrackResourcesInformation, out blurayTrackResources); - - BlurayInfo = new BlurayInfo - { - DataContext = new BlurayInfoViewModel(blurayDiscInformation, blurayBurstCuttingArea, blurayDds, - blurayCartridgeStatus, bluraySpareAreaInformation, - blurayPowResources, blurayTrackResources, null, null, view) - }; - - byte[] xboxDmi = null; - byte[] xboxSecuritySector = null; - SS.SecuritySector? decodedXboxSecuritySector = null; - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.Xbox_DMI) == true) - imageFormat.ReadMediaTag(MediaTagType.Xbox_DMI, out xboxDmi); - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.Xbox_SecuritySector) == true) - { - errno = imageFormat.ReadMediaTag(MediaTagType.Xbox_SecuritySector, out xboxSecuritySector); - - if(errno == ErrorNumber.NoError) - decodedXboxSecuritySector = SS.Decode(xboxSecuritySector); - } - - XboxInfo = new XboxInfo - { - DataContext = new XboxInfoViewModel(null, xboxDmi, xboxSecuritySector, decodedXboxSecuritySector, view) - }; - - errno = ErrorNumber.NoData; - byte[] pcmciaCis = null; - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.PCMCIA_CIS) == true) - errno = imageFormat.ReadMediaTag(MediaTagType.PCMCIA_CIS, out pcmciaCis); + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_MODESENSE_6) == true) + { + errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_MODESENSE_6, out scsiModeSense6); if(errno == ErrorNumber.NoError) - PcmciaInfo = new PcmciaInfo - { - DataContext = new PcmciaInfoViewModel(pcmciaCis, view) - }; + scsiMode = Modes.DecodeMode6(scsiModeSense6, scsiDeviceType); + } - DeviceType deviceType = DeviceType.Unknown; - byte[] cid = null; - byte[] csd = null; - byte[] ocr = null; - byte[] extendedCsd = null; - byte[] scr = null; + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SCSI_MODESENSE_10) == true) + { + errno = imageFormat.ReadMediaTag(MediaTagType.SCSI_MODESENSE_10, out scsiModeSense10); - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_CID) == true) + if(errno == ErrorNumber.NoError) + scsiMode = Modes.DecodeMode10(scsiModeSense10, scsiDeviceType); + } + + ScsiInfo = new ScsiInfo + { + DataContext = new ScsiInfoViewModel(scsiInquiryData, scsiInquiry, null, scsiMode, scsiDeviceType, + scsiModeSense6, scsiModeSense10, null, view) + }; + + byte[] ataIdentify = null; + byte[] atapiIdentify = null; + errno = ErrorNumber.NoData; + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.ATA_IDENTIFY) == true) + errno = imageFormat.ReadMediaTag(MediaTagType.ATA_IDENTIFY, out ataIdentify); + + else if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.ATAPI_IDENTIFY) == true) + errno = imageFormat.ReadMediaTag(MediaTagType.ATAPI_IDENTIFY, out atapiIdentify); + + if(errno == ErrorNumber.NoError) + AtaInfo = new AtaInfo { - imageFormat.ReadMediaTag(MediaTagType.SD_CID, out cid); - deviceType = DeviceType.SecureDigital; - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_CSD) == true) - { - imageFormat.ReadMediaTag(MediaTagType.SD_CSD, out csd); - deviceType = DeviceType.SecureDigital; - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_OCR) == true) - { - imageFormat.ReadMediaTag(MediaTagType.SD_OCR, out ocr); - deviceType = DeviceType.SecureDigital; - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_SCR) == true) - { - imageFormat.ReadMediaTag(MediaTagType.SD_SCR, out scr); - deviceType = DeviceType.SecureDigital; - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_CID) == true) - { - imageFormat.ReadMediaTag(MediaTagType.MMC_CID, out cid); - deviceType = DeviceType.MMC; - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_CSD) == true) - { - imageFormat.ReadMediaTag(MediaTagType.MMC_CSD, out csd); - deviceType = DeviceType.MMC; - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_OCR) == true) - { - imageFormat.ReadMediaTag(MediaTagType.MMC_OCR, out ocr); - deviceType = DeviceType.MMC; - } - - if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_ExtendedCSD) == true) - { - imageFormat.ReadMediaTag(MediaTagType.MMC_ExtendedCSD, out extendedCsd); - deviceType = DeviceType.MMC; - } - - SdMmcInfo = new SdMmcInfo - { - DataContext = new SdMmcInfoViewModel(deviceType, cid, csd, ocr, extendedCsd, scr) + DataContext = new AtaInfoViewModel(ataIdentify, atapiIdentify, null, view) }; - if(imageFormat is IOpticalMediaImage opticalMediaImage) + byte[] toc = null; + TOC.CDTOC? decodedToc = null; + byte[] fullToc = null; + FullTOC.CDFullTOC? decodedFullToc = null; + byte[] pma = null; + byte[] atip = null; + ATIP.CDATIP decodedAtip = null; + byte[] cdtext = null; + CDTextOnLeadIn.CDText? decodedCdText = null; + string mediaCatalogueNumber = null; + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_TOC) == true) + { + errno = imageFormat.ReadMediaTag(MediaTagType.CD_TOC, out toc); + + if(errno == ErrorNumber.NoError && + toc.Length > 0) { - try + ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(toc, 0)); + + if(dataLen + 2 != toc.Length) { - if(opticalMediaImage.Sessions != null && - opticalMediaImage.Sessions.Count > 0) - foreach(Session session in opticalMediaImage.Sessions) - Sessions.Add(session); - } - catch - { - // ignored + byte[] tmp = new byte[toc.Length + 2]; + Array.Copy(toc, 0, tmp, 2, toc.Length); + tmp[0] = (byte)((toc.Length & 0xFF00) >> 8); + tmp[1] = (byte)(toc.Length & 0xFF); + toc = tmp; } - try + decodedToc = TOC.Decode(toc); + } + } + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_FullTOC) == true) + { + errno = imageFormat.ReadMediaTag(MediaTagType.CD_FullTOC, out fullToc); + + if(errno == ErrorNumber.NoError && + fullToc.Length > 0) + { + ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(fullToc, 0)); + + if(dataLen + 2 != fullToc.Length) { - if(opticalMediaImage.Tracks != null && - opticalMediaImage.Tracks.Count > 0) - foreach(Track track in opticalMediaImage.Tracks) - Tracks.Add(track); + byte[] tmp = new byte[fullToc.Length + 2]; + Array.Copy(fullToc, 0, tmp, 2, fullToc.Length); + tmp[0] = (byte)((fullToc.Length & 0xFF00) >> 8); + tmp[1] = (byte)(fullToc.Length & 0xFF); + fullToc = tmp; } - catch + + decodedFullToc = FullTOC.Decode(fullToc); + } + } + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_PMA) == true) + { + errno = imageFormat.ReadMediaTag(MediaTagType.CD_PMA, out pma); + + if(errno == ErrorNumber.NoError && + pma.Length > 0) + { + ushort dataLen = Swapping.Swap(BitConverter.ToUInt16(pma, 0)); + + if(dataLen + 2 != pma.Length) { - // ignored + byte[] tmp = new byte[pma.Length + 2]; + Array.Copy(pma, 0, tmp, 2, pma.Length); + tmp[0] = (byte)((pma.Length & 0xFF00) >> 8); + tmp[1] = (byte)(pma.Length & 0xFF); + pma = tmp; } } + } - if(imageFormat.DumpHardware is null) - return; + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_ATIP) == true) + { + errno = imageFormat.ReadMediaTag(MediaTagType.CD_ATIP, out atip); - foreach(DumpHardwareType dump in imageFormat.DumpHardware) + if(errno == ErrorNumber.NoError) { - foreach(ExtentType extent in dump.Extents) - DumpHardwareList.Add(new DumpHardwareModel - { - Manufacturer = dump.Manufacturer, - Model = dump.Model, - Serial = dump.Serial, - SoftwareName = dump.Software.Name, - SoftwareVersion = dump.Software.Version, - OperatingSystem = dump.Software.OperatingSystem, - Start = extent.Start, - End = extent.End - }); + uint dataLen = Swapping.Swap(BitConverter.ToUInt32(atip, 0)); + + if(dataLen + 4 != atip.Length) + { + byte[] tmp = new byte[atip.Length + 4]; + Array.Copy(atip, 0, tmp, 4, atip.Length); + tmp[0] = (byte)((atip.Length & 0xFF000000) >> 24); + tmp[1] = (byte)((atip.Length & 0xFF0000) >> 16); + tmp[2] = (byte)((atip.Length & 0xFF00) >> 8); + tmp[3] = (byte)(atip.Length & 0xFF); + atip = tmp; + } + + decodedAtip = ATIP.Decode(atip); } } - public ScsiInfo ScsiInfo { get; } - public AtaInfo AtaInfo { get; } - public CompactDiscInfo CompactDiscInfo { get; } - public DvdInfo DvdInfo { get; } - public DvdWritableInfo DvdWritableInfo { get; } - public BlurayInfo BlurayInfo { get; } - public XboxInfo XboxInfo { get; } - public PcmciaInfo PcmciaInfo { get; } - public SdMmcInfo SdMmcInfo { get; } - public Bitmap MediaLogo { get; } - public string ImagePathText { get; } - public string FilterText { get; } - public string ImageIdentifiedText { get; } - public string MediaTypeText { get; set; } - public string SectorsText { get; set; } - public string HasPartitionsText { get; set; } - public string HasSessionsText { get; set; } - public string ApplicationText { get; set; } - public string CreatorText { get; set; } - public string CreationTimeText { get; set; } - public string LastModificationTimeText { get; set; } - public string MediaSequenceText { get; set; } - public string MediaTitleText { get; set; } - public string MediaManufacturerText { get; set; } - public string MediaModelText { get; set; } - public string MediaSerialNumberText { get; set; } - public string MediaBarcodeText { get; set; } - public string MediaPartNumberText { get; set; } - public string CommentsText => _imageFormat.Info.Comments; - public string DriveManufacturerText { get; set; } - public string DriveModelText { get; set; } - public string DriveSerialNumberText { get; set; } - public string DriveFirmwareRevisionText { get; set; } - public string MediaGeometryText { get; set; } - public ObservableCollection MediaTagsList { get; } - public ObservableCollection SectorTagsList { get; } - public string ImageSizeText { get; set; } - public string ImageFormatText { get; set; } - public ObservableCollection Sessions { get; } - public ObservableCollection Tracks { get; } - public ObservableCollection DumpHardwareList { get; } - public ReactiveCommand EntropyCommand { get; } - public ReactiveCommand VerifyCommand { get; } - public ReactiveCommand ChecksumCommand { get; } - public ReactiveCommand ConvertCommand { get; } - public ReactiveCommand CreateSidecarCommand { get; } - public ReactiveCommand ViewSectorsCommand { get; } - public ReactiveCommand DecodeMediaTagCommand { get; } - public bool DriveInformationVisible => DriveManufacturerText != null || DriveModelText != null || - DriveSerialNumberText != null || DriveFirmwareRevisionText != null || - MediaGeometryText != null; - public bool MediaInformationVisible => MediaSequenceText != null || MediaTitleText != null || - MediaManufacturerText != null || MediaModelText != null || - MediaSerialNumberText != null || MediaBarcodeText != null || - MediaPartNumberText != null; - - void ExecuteEntropyCommand() + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_TEXT) == true) { - if(_imageEntropy != null) + errno = imageFormat.ReadMediaTag(MediaTagType.CD_TEXT, out cdtext); + + if(errno == ErrorNumber.NoError) { - _imageEntropy.Show(); + uint dataLen = Swapping.Swap(BitConverter.ToUInt32(cdtext, 0)); - return; + if(dataLen + 4 != cdtext.Length) + { + byte[] tmp = new byte[cdtext.Length + 4]; + Array.Copy(cdtext, 0, tmp, 4, cdtext.Length); + tmp[0] = (byte)((cdtext.Length & 0xFF000000) >> 24); + tmp[1] = (byte)((cdtext.Length & 0xFF0000) >> 16); + tmp[2] = (byte)((cdtext.Length & 0xFF00) >> 8); + tmp[3] = (byte)(cdtext.Length & 0xFF); + cdtext = tmp; + } + + decodedCdText = CDTextOnLeadIn.Decode(cdtext); } - - _imageEntropy = new ImageEntropy(); - _imageEntropy.DataContext = new ImageEntropyViewModel(_imageFormat, _imageEntropy); - - _imageEntropy.Closed += (sender, args) => _imageEntropy = null; - - _imageEntropy.Show(); } - void ExecuteVerifyCommand() + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.CD_MCN) == true) { - if(_imageVerify != null) - { - _imageVerify.Show(); + errno = imageFormat.ReadMediaTag(MediaTagType.CD_MCN, out byte[] mcn); - return; - } - - _imageVerify = new ImageVerify(); - _imageVerify.DataContext = new ImageVerifyViewModel(_imageFormat, _imageVerify); - - _imageVerify.Closed += (sender, args) => _imageVerify = null; - - _imageVerify.Show(); + if(errno == ErrorNumber.NoError) + mediaCatalogueNumber = Encoding.UTF8.GetString(mcn); } - void ExecuteChecksumCommand() + CompactDiscInfo = new CompactDiscInfo { - if(_imageChecksum != null) - { - _imageChecksum.Show(); + DataContext = new CompactDiscInfoViewModel(toc, atip, null, null, fullToc, pma, cdtext, decodedToc, + decodedAtip, null, decodedFullToc, decodedCdText, null, + mediaCatalogueNumber, null, view) + }; - return; - } + byte[] dvdPfi = null; + byte[] dvdDmi = null; + byte[] dvdCmi = null; + byte[] hddvdCopyrightInformation = null; + byte[] dvdBca = null; + PFI.PhysicalFormatInformation? decodedPfi = null; - _imageChecksum = new ImageChecksum(); - _imageChecksum.DataContext = new ImageChecksumViewModel(_imageFormat, _imageChecksum); + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_PFI) == true) + { + errno = imageFormat.ReadMediaTag(MediaTagType.DVD_PFI, out dvdPfi); - _imageChecksum.Closed += (sender, args) => _imageChecksum = null; - - _imageChecksum.Show(); + if(errno == ErrorNumber.NoError) + decodedPfi = PFI.Decode(dvdPfi, imageFormat.Info.MediaType); } - void ExecuteConvertCommand() + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_DMI) == true) + imageFormat.ReadMediaTag(MediaTagType.DVD_DMI, out dvdDmi); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_CMI) == true) + imageFormat.ReadMediaTag(MediaTagType.DVD_CMI, out dvdCmi); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.HDDVD_CPI) == true) + imageFormat.ReadMediaTag(MediaTagType.HDDVD_CPI, out hddvdCopyrightInformation); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_BCA) == true) + imageFormat.ReadMediaTag(MediaTagType.DVD_BCA, out dvdBca); + + DvdInfo = new DvdInfo { - if(_imageConvert != null) - { - _imageConvert.Show(); + DataContext = new DvdInfoViewModel(imageFormat.Info.MediaType, dvdPfi, dvdDmi, dvdCmi, + hddvdCopyrightInformation, dvdBca, null, decodedPfi, view) + }; - return; - } + byte[] dvdRamDds = null; + byte[] dvdRamCartridgeStatus = null; + byte[] dvdRamSpareArea = null; + byte[] lastBorderOutRmd = null; + byte[] dvdPreRecordedInfo = null; + byte[] dvdrMediaIdentifier = null; + byte[] dvdrPhysicalInformation = null; + byte[] hddvdrMediumStatus = null; + byte[] dvdrLayerCapacity = null; + byte[] dvdrDlMiddleZoneStart = null; + byte[] dvdrDlJumpIntervalSize = null; + byte[] dvdrDlManualLayerJumpStartLba = null; + byte[] dvdPlusAdip = null; + byte[] dvdPlusDcb = null; - _imageConvert = new ImageConvert(); - _imageConvert.DataContext = new ImageConvertViewModel(_imageFormat, _imagePath, _imageConvert); + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_DDS) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDRAM_DDS, out dvdRamDds); - _imageConvert.Closed += (sender, args) => _imageConvert = null; + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_MediumStatus) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDRAM_MediumStatus, out dvdRamCartridgeStatus); - _imageConvert.Show(); + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDRAM_SpareArea) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDRAM_SpareArea, out dvdRamSpareArea); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_RMD) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDR_RMD, out lastBorderOutRmd); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_PreRecordedInfo) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDR_PreRecordedInfo, out dvdPreRecordedInfo); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_MediaIdentifier) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDR_MediaIdentifier, out dvdrMediaIdentifier); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDR_PFI) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDR_PFI, out dvdrPhysicalInformation); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.HDDVD_MediumStatus) == true) + imageFormat.ReadMediaTag(MediaTagType.HDDVD_MediumStatus, out hddvdrMediumStatus); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_LayerCapacity) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDDL_LayerCapacity, out dvdrLayerCapacity); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_MiddleZoneAddress) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDDL_MiddleZoneAddress, out dvdrDlMiddleZoneStart); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_JumpIntervalSize) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDDL_JumpIntervalSize, out dvdrDlJumpIntervalSize); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVDDL_ManualLayerJumpLBA) == true) + imageFormat.ReadMediaTag(MediaTagType.DVDDL_ManualLayerJumpLBA, out dvdrDlManualLayerJumpStartLba); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DVD_ADIP) == true) + imageFormat.ReadMediaTag(MediaTagType.DVD_ADIP, out dvdPlusAdip); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.DCB) == true) + imageFormat.ReadMediaTag(MediaTagType.DCB, out dvdPlusDcb); + + DvdWritableInfo = new DvdWritableInfo + { + DataContext = new DvdWritableInfoViewModel(imageFormat.Info.MediaType, dvdRamDds, dvdRamCartridgeStatus, + dvdRamSpareArea, lastBorderOutRmd, dvdPreRecordedInfo, + dvdrMediaIdentifier, dvdrPhysicalInformation, + hddvdrMediumStatus, null, dvdrLayerCapacity, + dvdrDlMiddleZoneStart, dvdrDlJumpIntervalSize, + dvdrDlManualLayerJumpStartLba, null, dvdPlusAdip, dvdPlusDcb, + view) + }; + + byte[] blurayBurstCuttingArea = null; + byte[] blurayCartridgeStatus = null; + byte[] blurayDds = null; + byte[] blurayDiscInformation = null; + byte[] blurayPowResources = null; + byte[] bluraySpareAreaInformation = null; + byte[] blurayTrackResources = null; + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_BCA) == true) + imageFormat.ReadMediaTag(MediaTagType.BD_BCA, out blurayBurstCuttingArea); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_CartridgeStatus) == true) + imageFormat.ReadMediaTag(MediaTagType.BD_CartridgeStatus, out blurayCartridgeStatus); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_DDS) == true) + imageFormat.ReadMediaTag(MediaTagType.BD_DDS, out blurayDds); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_DI) == true) + imageFormat.ReadMediaTag(MediaTagType.BD_DI, out blurayDiscInformation); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_POWResourcesInformation) == true) + imageFormat.ReadMediaTag(MediaTagType.MMC_POWResourcesInformation, out blurayPowResources); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.BD_SpareArea) == true) + imageFormat.ReadMediaTag(MediaTagType.BD_SpareArea, out bluraySpareAreaInformation); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_TrackResourcesInformation) == true) + imageFormat.ReadMediaTag(MediaTagType.MMC_TrackResourcesInformation, out blurayTrackResources); + + BlurayInfo = new BlurayInfo + { + DataContext = new BlurayInfoViewModel(blurayDiscInformation, blurayBurstCuttingArea, blurayDds, + blurayCartridgeStatus, bluraySpareAreaInformation, + blurayPowResources, blurayTrackResources, null, null, view) + }; + + byte[] xboxDmi = null; + byte[] xboxSecuritySector = null; + SS.SecuritySector? decodedXboxSecuritySector = null; + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.Xbox_DMI) == true) + imageFormat.ReadMediaTag(MediaTagType.Xbox_DMI, out xboxDmi); + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.Xbox_SecuritySector) == true) + { + errno = imageFormat.ReadMediaTag(MediaTagType.Xbox_SecuritySector, out xboxSecuritySector); + + if(errno == ErrorNumber.NoError) + decodedXboxSecuritySector = SS.Decode(xboxSecuritySector); } - void ExecuteCreateSidecarCommand() + XboxInfo = new XboxInfo { - if(_imageSidecar != null) + DataContext = new XboxInfoViewModel(null, xboxDmi, xboxSecuritySector, decodedXboxSecuritySector, view) + }; + + errno = ErrorNumber.NoData; + byte[] pcmciaCis = null; + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.PCMCIA_CIS) == true) + errno = imageFormat.ReadMediaTag(MediaTagType.PCMCIA_CIS, out pcmciaCis); + + if(errno == ErrorNumber.NoError) + PcmciaInfo = new PcmciaInfo { - _imageSidecar.Show(); - - return; - } - - _imageSidecar = new ImageSidecar(); - - // TODO: Pass thru chosen default encoding - _imageSidecar.DataContext = - new ImageSidecarViewModel(_imageFormat, _imagePath, _filter.Id, null, _imageSidecar); - - _imageSidecar.Closed += (sender, args) => _imageSidecar = null; - - _imageSidecar.Show(); - } - - void ExecuteViewSectorsCommand() - { - if(_viewSector != null) - { - _viewSector.Show(); - - return; - } - - _viewSector = new ViewSector - { - DataContext = new ViewSectorViewModel(_imageFormat) + DataContext = new PcmciaInfoViewModel(pcmciaCis, view) }; - _viewSector.Closed += (sender, args) => _viewSector = null; + DeviceType deviceType = DeviceType.Unknown; + byte[] cid = null; + byte[] csd = null; + byte[] ocr = null; + byte[] extendedCsd = null; + byte[] scr = null; - _viewSector.Show(); + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_CID) == true) + { + imageFormat.ReadMediaTag(MediaTagType.SD_CID, out cid); + deviceType = DeviceType.SecureDigital; } - void ExecuteDecodeMediaTagCommand() + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_CSD) == true) { - if(_decodeMediaTags != null) - { - _decodeMediaTags.Show(); + imageFormat.ReadMediaTag(MediaTagType.SD_CSD, out csd); + deviceType = DeviceType.SecureDigital; + } - return; + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_OCR) == true) + { + imageFormat.ReadMediaTag(MediaTagType.SD_OCR, out ocr); + deviceType = DeviceType.SecureDigital; + } + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.SD_SCR) == true) + { + imageFormat.ReadMediaTag(MediaTagType.SD_SCR, out scr); + deviceType = DeviceType.SecureDigital; + } + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_CID) == true) + { + imageFormat.ReadMediaTag(MediaTagType.MMC_CID, out cid); + deviceType = DeviceType.MMC; + } + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_CSD) == true) + { + imageFormat.ReadMediaTag(MediaTagType.MMC_CSD, out csd); + deviceType = DeviceType.MMC; + } + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_OCR) == true) + { + imageFormat.ReadMediaTag(MediaTagType.MMC_OCR, out ocr); + deviceType = DeviceType.MMC; + } + + if(imageFormat.Info.ReadableMediaTags?.Contains(MediaTagType.MMC_ExtendedCSD) == true) + { + imageFormat.ReadMediaTag(MediaTagType.MMC_ExtendedCSD, out extendedCsd); + deviceType = DeviceType.MMC; + } + + SdMmcInfo = new SdMmcInfo + { + DataContext = new SdMmcInfoViewModel(deviceType, cid, csd, ocr, extendedCsd, scr) + }; + + if(imageFormat is IOpticalMediaImage opticalMediaImage) + { + try + { + if(opticalMediaImage.Sessions != null && + opticalMediaImage.Sessions.Count > 0) + foreach(Session session in opticalMediaImage.Sessions) + Sessions.Add(session); + } + catch + { + // ignored } - _decodeMediaTags = new DecodeMediaTags + try { - DataContext = new DecodeMediaTagsViewModel(_imageFormat) - }; + if(opticalMediaImage.Tracks != null && + opticalMediaImage.Tracks.Count > 0) + foreach(Track track in opticalMediaImage.Tracks) + Tracks.Add(track); + } + catch + { + // ignored + } + } - _decodeMediaTags.Closed += (sender, args) => _decodeMediaTags = null; + if(imageFormat.DumpHardware is null) + return; - _decodeMediaTags.Show(); + foreach(DumpHardwareType dump in imageFormat.DumpHardware) + { + foreach(ExtentType extent in dump.Extents) + DumpHardwareList.Add(new DumpHardwareModel + { + Manufacturer = dump.Manufacturer, + Model = dump.Model, + Serial = dump.Serial, + SoftwareName = dump.Software.Name, + SoftwareVersion = dump.Software.Version, + OperatingSystem = dump.Software.OperatingSystem, + Start = extent.Start, + End = extent.End + }); } } + + public ScsiInfo ScsiInfo { get; } + public AtaInfo AtaInfo { get; } + public CompactDiscInfo CompactDiscInfo { get; } + public DvdInfo DvdInfo { get; } + public DvdWritableInfo DvdWritableInfo { get; } + public BlurayInfo BlurayInfo { get; } + public XboxInfo XboxInfo { get; } + public PcmciaInfo PcmciaInfo { get; } + public SdMmcInfo SdMmcInfo { get; } + public Bitmap MediaLogo { get; } + public string ImagePathText { get; } + public string FilterText { get; } + public string ImageIdentifiedText { get; } + public string MediaTypeText { get; set; } + public string SectorsText { get; set; } + public string HasPartitionsText { get; set; } + public string HasSessionsText { get; set; } + public string ApplicationText { get; set; } + public string CreatorText { get; set; } + public string CreationTimeText { get; set; } + public string LastModificationTimeText { get; set; } + public string MediaSequenceText { get; set; } + public string MediaTitleText { get; set; } + public string MediaManufacturerText { get; set; } + public string MediaModelText { get; set; } + public string MediaSerialNumberText { get; set; } + public string MediaBarcodeText { get; set; } + public string MediaPartNumberText { get; set; } + public string CommentsText => _imageFormat.Info.Comments; + public string DriveManufacturerText { get; set; } + public string DriveModelText { get; set; } + public string DriveSerialNumberText { get; set; } + public string DriveFirmwareRevisionText { get; set; } + public string MediaGeometryText { get; set; } + public ObservableCollection MediaTagsList { get; } + public ObservableCollection SectorTagsList { get; } + public string ImageSizeText { get; set; } + public string ImageFormatText { get; set; } + public ObservableCollection Sessions { get; } + public ObservableCollection Tracks { get; } + public ObservableCollection DumpHardwareList { get; } + public ReactiveCommand EntropyCommand { get; } + public ReactiveCommand VerifyCommand { get; } + public ReactiveCommand ChecksumCommand { get; } + public ReactiveCommand ConvertCommand { get; } + public ReactiveCommand CreateSidecarCommand { get; } + public ReactiveCommand ViewSectorsCommand { get; } + public ReactiveCommand DecodeMediaTagCommand { get; } + public bool DriveInformationVisible => DriveManufacturerText != null || DriveModelText != null || + DriveSerialNumberText != null || DriveFirmwareRevisionText != null || + MediaGeometryText != null; + public bool MediaInformationVisible => MediaSequenceText != null || MediaTitleText != null || + MediaManufacturerText != null || MediaModelText != null || + MediaSerialNumberText != null || MediaBarcodeText != null || + MediaPartNumberText != null; + + void ExecuteEntropyCommand() + { + if(_imageEntropy != null) + { + _imageEntropy.Show(); + + return; + } + + _imageEntropy = new ImageEntropy(); + _imageEntropy.DataContext = new ImageEntropyViewModel(_imageFormat, _imageEntropy); + + _imageEntropy.Closed += (sender, args) => _imageEntropy = null; + + _imageEntropy.Show(); + } + + void ExecuteVerifyCommand() + { + if(_imageVerify != null) + { + _imageVerify.Show(); + + return; + } + + _imageVerify = new ImageVerify(); + _imageVerify.DataContext = new ImageVerifyViewModel(_imageFormat, _imageVerify); + + _imageVerify.Closed += (sender, args) => _imageVerify = null; + + _imageVerify.Show(); + } + + void ExecuteChecksumCommand() + { + if(_imageChecksum != null) + { + _imageChecksum.Show(); + + return; + } + + _imageChecksum = new ImageChecksum(); + _imageChecksum.DataContext = new ImageChecksumViewModel(_imageFormat, _imageChecksum); + + _imageChecksum.Closed += (sender, args) => _imageChecksum = null; + + _imageChecksum.Show(); + } + + void ExecuteConvertCommand() + { + if(_imageConvert != null) + { + _imageConvert.Show(); + + return; + } + + _imageConvert = new ImageConvert(); + _imageConvert.DataContext = new ImageConvertViewModel(_imageFormat, _imagePath, _imageConvert); + + _imageConvert.Closed += (sender, args) => _imageConvert = null; + + _imageConvert.Show(); + } + + void ExecuteCreateSidecarCommand() + { + if(_imageSidecar != null) + { + _imageSidecar.Show(); + + return; + } + + _imageSidecar = new ImageSidecar(); + + // TODO: Pass thru chosen default encoding + _imageSidecar.DataContext = + new ImageSidecarViewModel(_imageFormat, _imagePath, _filter.Id, null, _imageSidecar); + + _imageSidecar.Closed += (sender, args) => _imageSidecar = null; + + _imageSidecar.Show(); + } + + void ExecuteViewSectorsCommand() + { + if(_viewSector != null) + { + _viewSector.Show(); + + return; + } + + _viewSector = new ViewSector + { + DataContext = new ViewSectorViewModel(_imageFormat) + }; + + _viewSector.Closed += (sender, args) => _viewSector = null; + + _viewSector.Show(); + } + + void ExecuteDecodeMediaTagCommand() + { + if(_decodeMediaTags != null) + { + _decodeMediaTags.Show(); + + return; + } + + _decodeMediaTags = new DecodeMediaTags + { + DataContext = new DecodeMediaTagsViewModel(_imageFormat) + }; + + _decodeMediaTags.Closed += (sender, args) => _decodeMediaTags = null; + + _decodeMediaTags.Show(); + } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Panels/MediaInfoViewModel.cs b/Aaru.Gui/ViewModels/Panels/MediaInfoViewModel.cs index 45df654b4..f97b960ee 100644 --- a/Aaru.Gui/ViewModels/Panels/MediaInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Panels/MediaInfoViewModel.cs @@ -48,405 +48,404 @@ using MessageBox.Avalonia.Enums; using ReactiveUI; using ScsiInfo = Aaru.Core.Media.Info.ScsiInfo; -namespace Aaru.Gui.ViewModels.Panels +namespace Aaru.Gui.ViewModels.Panels; + +public sealed class MediaInfoViewModel : ViewModelBase { - public sealed class MediaInfoViewModel : ViewModelBase + readonly string _devicePath; + readonly ScsiInfo _scsiInfo; + readonly Window _view; + BlurayInfo _blurayInfo; + CompactDiscInfo _compactDiscInfo; + string _densitySupport; + DvdInfo _dvdInfo; + DvdWritableInfo _dvdWritableInfo; + string _generalVisible; + Bitmap _mediaLogo; + string _mediaSerial; + string _mediaSize; + string _mediaType; + string _mediumSupport; + bool _mmcVisible; + bool _saveDensitySupportVisible; + bool _saveGetConfigurationVisible; + bool _saveMediumSupportVisible; + bool _saveReadCapacity16Visible; + bool _saveReadCapacityVisible; + bool _saveReadMediaSerialVisible; + bool _saveRecognizedFormatLayersVisible; + bool _saveWriteProtectionStatusVisible; + bool _sscVisible; + XboxInfo _xboxInfo; + + public MediaInfoViewModel(ScsiInfo scsiInfo, string devicePath, Window view) { - readonly string _devicePath; - readonly ScsiInfo _scsiInfo; - readonly Window _view; - BlurayInfo _blurayInfo; - CompactDiscInfo _compactDiscInfo; - string _densitySupport; - DvdInfo _dvdInfo; - DvdWritableInfo _dvdWritableInfo; - string _generalVisible; - Bitmap _mediaLogo; - string _mediaSerial; - string _mediaSize; - string _mediaType; - string _mediumSupport; - bool _mmcVisible; - bool _saveDensitySupportVisible; - bool _saveGetConfigurationVisible; - bool _saveMediumSupportVisible; - bool _saveReadCapacity16Visible; - bool _saveReadCapacityVisible; - bool _saveReadMediaSerialVisible; - bool _saveRecognizedFormatLayersVisible; - bool _saveWriteProtectionStatusVisible; - bool _sscVisible; - XboxInfo _xboxInfo; + _view = view; + SaveReadMediaSerialCommand = ReactiveCommand.Create(ExecuteSaveReadMediaSerialCommand); + SaveReadCapacityCommand = ReactiveCommand.Create(ExecuteSaveReadCapacityCommand); + SaveReadCapacity16Command = ReactiveCommand.Create(ExecuteSaveReadCapacity16Command); + SaveGetConfigurationCommand = ReactiveCommand.Create(ExecuteSaveGetConfigurationCommand); + SaveRecognizedFormatLayersCommand = ReactiveCommand.Create(ExecuteSaveRecognizedFormatLayersCommand); + SaveWriteProtectionStatusCommand = ReactiveCommand.Create(ExecuteSaveWriteProtectionStatusCommand); + SaveDensitySupportCommand = ReactiveCommand.Create(ExecuteSaveDensitySupportCommand); + SaveMediumSupportCommand = ReactiveCommand.Create(ExecuteSaveMediumSupportCommand); + DumpCommand = ReactiveCommand.Create(ExecuteDumpCommand); + ScanCommand = ReactiveCommand.Create(ExecuteScanCommand); + IAssetLoader assets = AvaloniaLocator.Current.GetService(); + _devicePath = devicePath; + _scsiInfo = scsiInfo; - public MediaInfoViewModel(ScsiInfo scsiInfo, string devicePath, Window view) + var mediaResource = new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{scsiInfo.MediaType}.png"); + + MediaLogo = assets.Exists(mediaResource) ? new Bitmap(assets.Open(mediaResource)) : null; + + MediaType = scsiInfo.MediaType.ToString(); + + if(scsiInfo.Blocks != 0 && + scsiInfo.BlockSize != 0) { - _view = view; - SaveReadMediaSerialCommand = ReactiveCommand.Create(ExecuteSaveReadMediaSerialCommand); - SaveReadCapacityCommand = ReactiveCommand.Create(ExecuteSaveReadCapacityCommand); - SaveReadCapacity16Command = ReactiveCommand.Create(ExecuteSaveReadCapacity16Command); - SaveGetConfigurationCommand = ReactiveCommand.Create(ExecuteSaveGetConfigurationCommand); - SaveRecognizedFormatLayersCommand = ReactiveCommand.Create(ExecuteSaveRecognizedFormatLayersCommand); - SaveWriteProtectionStatusCommand = ReactiveCommand.Create(ExecuteSaveWriteProtectionStatusCommand); - SaveDensitySupportCommand = ReactiveCommand.Create(ExecuteSaveDensitySupportCommand); - SaveMediumSupportCommand = ReactiveCommand.Create(ExecuteSaveMediumSupportCommand); - DumpCommand = ReactiveCommand.Create(ExecuteDumpCommand); - ScanCommand = ReactiveCommand.Create(ExecuteScanCommand); - IAssetLoader assets = AvaloniaLocator.Current.GetService(); - _devicePath = devicePath; - _scsiInfo = scsiInfo; + ulong totalSize = scsiInfo.Blocks * scsiInfo.BlockSize; - var mediaResource = new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{scsiInfo.MediaType}.png"); + if(totalSize > 1099511627776) + MediaSize = + $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"; + else if(totalSize > 1073741824) + MediaSize = + $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"; + else if(totalSize > 1048576) + MediaSize = + $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"; + else if(totalSize > 1024) + MediaSize = + $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"; + else + MediaSize = + $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize} bytes)"; + } - MediaLogo = assets.Exists(mediaResource) ? new Bitmap(assets.Open(mediaResource)) : null; + if(scsiInfo.MediaSerialNumber != null) + { + var sbSerial = new StringBuilder(); - MediaType = scsiInfo.MediaType.ToString(); + for(int i = 4; i < scsiInfo.MediaSerialNumber.Length; i++) + sbSerial.AppendFormat("{0:X2}", scsiInfo.MediaSerialNumber[i]); - if(scsiInfo.Blocks != 0 && - scsiInfo.BlockSize != 0) + MediaSerial = sbSerial.ToString(); + } + + SaveReadMediaSerialVisible = scsiInfo.MediaSerialNumber != null; + SaveReadCapacityVisible = scsiInfo.ReadCapacity != null; + SaveReadCapacity16Visible = scsiInfo.ReadCapacity16 != null; + + SaveGetConfigurationVisible = scsiInfo.MmcConfiguration != null; + SaveRecognizedFormatLayersVisible = scsiInfo.RecognizedFormatLayers != null; + SaveWriteProtectionStatusVisible = scsiInfo.WriteProtectionStatus != null; + + MmcVisible = SaveGetConfigurationVisible || SaveRecognizedFormatLayersVisible || + SaveWriteProtectionStatusVisible; + + if(scsiInfo.DensitySupportHeader.HasValue) + DensitySupport = Decoders.SCSI.SSC.DensitySupport.PrettifyDensity(scsiInfo.DensitySupportHeader); + + if(scsiInfo.MediaTypeSupportHeader.HasValue) + MediumSupport = Decoders.SCSI.SSC.DensitySupport.PrettifyMediumType(scsiInfo.MediaTypeSupportHeader); + + SaveDensitySupportVisible = scsiInfo.DensitySupport != null; + SaveMediumSupportVisible = scsiInfo.MediaTypeSupport != null; + + SscVisible = SaveDensitySupportVisible || SaveMediumSupportVisible; + + CompactDiscInfo = new CompactDiscInfo + { + DataContext = new CompactDiscInfoViewModel(scsiInfo.Toc, scsiInfo.Atip, scsiInfo.DiscInformation, + scsiInfo.Session, scsiInfo.RawToc, scsiInfo.Pma, + scsiInfo.CdTextLeadIn, scsiInfo.DecodedToc, + scsiInfo.DecodedAtip, scsiInfo.DecodedSession, + scsiInfo.FullToc, scsiInfo.DecodedCdTextLeadIn, + scsiInfo.DecodedDiscInformation, scsiInfo.Mcn, + scsiInfo.Isrcs, _view) + }; + + DvdInfo = new DvdInfo + { + DataContext = new DvdInfoViewModel(scsiInfo.MediaType, scsiInfo.DvdPfi, scsiInfo.DvdDmi, + scsiInfo.DvdCmi, scsiInfo.HddvdCopyrightInformation, scsiInfo.DvdBca, + scsiInfo.DvdAacs, scsiInfo.DecodedPfi, _view) + }; + + XboxInfo = new XboxInfo + { + DataContext = new XboxInfoViewModel(scsiInfo.XgdInfo, scsiInfo.DvdDmi, scsiInfo.XboxSecuritySector, + scsiInfo.DecodedXboxSecuritySector, _view) + }; + + DvdWritableInfo = new DvdWritableInfo + { + DataContext = new DvdWritableInfoViewModel(scsiInfo.MediaType, scsiInfo.DvdRamDds, + scsiInfo.DvdRamCartridgeStatus, scsiInfo.DvdRamSpareArea, + scsiInfo.LastBorderOutRmd, scsiInfo.DvdPreRecordedInfo, + scsiInfo.DvdrMediaIdentifier, + scsiInfo.DvdrPhysicalInformation, + scsiInfo.HddvdrMediumStatus, scsiInfo.HddvdrLastRmd, + scsiInfo.DvdrLayerCapacity, scsiInfo.DvdrDlMiddleZoneStart, + scsiInfo.DvdrDlJumpIntervalSize, + scsiInfo.DvdrDlManualLayerJumpStartLba, + scsiInfo.DvdrDlRemapAnchorPoint, scsiInfo.DvdPlusAdip, + scsiInfo.DvdPlusDcb, _view) + }; + + BlurayInfo = new BlurayInfo + { + DataContext = new BlurayInfoViewModel(scsiInfo.BlurayDiscInformation, scsiInfo.BlurayBurstCuttingArea, + scsiInfo.BlurayDds, scsiInfo.BlurayCartridgeStatus, + scsiInfo.BluraySpareAreaInformation, scsiInfo.BlurayPowResources, + scsiInfo.BlurayTrackResources, scsiInfo.BlurayRawDfl, + scsiInfo.BlurayPac, _view) + }; + } + + public ReactiveCommand SaveReadMediaSerialCommand { get; } + public ReactiveCommand SaveReadCapacityCommand { get; } + public ReactiveCommand SaveReadCapacity16Command { get; } + public ReactiveCommand SaveGetConfigurationCommand { get; } + public ReactiveCommand SaveRecognizedFormatLayersCommand { get; } + public ReactiveCommand SaveWriteProtectionStatusCommand { get; } + public ReactiveCommand SaveDensitySupportCommand { get; } + public ReactiveCommand SaveMediumSupportCommand { get; } + public ReactiveCommand DumpCommand { get; } + public ReactiveCommand ScanCommand { get; } + + public Bitmap MediaLogo + { + get => _mediaLogo; + set => this.RaiseAndSetIfChanged(ref _mediaLogo, value); + } + + public string GeneralVisible + { + get => _generalVisible; + set => this.RaiseAndSetIfChanged(ref _generalVisible, value); + } + + public string MediaType + { + get => _mediaType; + set => this.RaiseAndSetIfChanged(ref _mediaType, value); + } + + public string MediaSize + { + get => _mediaSize; + set => this.RaiseAndSetIfChanged(ref _mediaSize, value); + } + + public string MediaSerial + { + get => _mediaSerial; + set => this.RaiseAndSetIfChanged(ref _mediaSerial, value); + } + + public bool SaveReadMediaSerialVisible + { + get => _saveReadMediaSerialVisible; + set => this.RaiseAndSetIfChanged(ref _saveReadMediaSerialVisible, value); + } + + public bool SaveReadCapacityVisible + { + get => _saveReadCapacityVisible; + set => this.RaiseAndSetIfChanged(ref _saveReadCapacityVisible, value); + } + + public bool SaveReadCapacity16Visible + { + get => _saveReadCapacity16Visible; + set => this.RaiseAndSetIfChanged(ref _saveReadCapacity16Visible, value); + } + + public bool MmcVisible + { + get => _mmcVisible; + set => this.RaiseAndSetIfChanged(ref _mmcVisible, value); + } + + public bool SaveGetConfigurationVisible + { + get => _saveGetConfigurationVisible; + set => this.RaiseAndSetIfChanged(ref _saveGetConfigurationVisible, value); + } + + public bool SaveRecognizedFormatLayersVisible + { + get => _saveRecognizedFormatLayersVisible; + set => this.RaiseAndSetIfChanged(ref _saveRecognizedFormatLayersVisible, value); + } + + public bool SaveWriteProtectionStatusVisible + { + get => _saveWriteProtectionStatusVisible; + set => this.RaiseAndSetIfChanged(ref _saveWriteProtectionStatusVisible, value); + } + + public bool SscVisible + { + get => _sscVisible; + set => this.RaiseAndSetIfChanged(ref _sscVisible, value); + } + + public string DensitySupport + { + get => _densitySupport; + set => this.RaiseAndSetIfChanged(ref _densitySupport, value); + } + + public string MediumSupport + { + get => _mediumSupport; + set => this.RaiseAndSetIfChanged(ref _mediumSupport, value); + } + + public bool SaveDensitySupportVisible + { + get => _saveDensitySupportVisible; + set => this.RaiseAndSetIfChanged(ref _saveDensitySupportVisible, value); + } + + public bool SaveMediumSupportVisible + { + get => _saveMediumSupportVisible; + set => this.RaiseAndSetIfChanged(ref _saveMediumSupportVisible, value); + } + + public CompactDiscInfo CompactDiscInfo + { + get => _compactDiscInfo; + set => this.RaiseAndSetIfChanged(ref _compactDiscInfo, value); + } + + public DvdInfo DvdInfo + { + get => _dvdInfo; + set => this.RaiseAndSetIfChanged(ref _dvdInfo, value); + } + + public DvdWritableInfo DvdWritableInfo + { + get => _dvdWritableInfo; + set => this.RaiseAndSetIfChanged(ref _dvdWritableInfo, value); + } + + public XboxInfo XboxInfo + { + get => _xboxInfo; + set => this.RaiseAndSetIfChanged(ref _xboxInfo, value); + } + + public BlurayInfo BlurayInfo + { + get => _blurayInfo; + set => this.RaiseAndSetIfChanged(ref _blurayInfo, value); + } + + async void SaveElement(byte[] data) + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] { - ulong totalSize = scsiInfo.Blocks * scsiInfo.BlockSize; + "*.bin" + }), + Name = "Binary" + }); - if(totalSize > 1099511627776) - MediaSize = - $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize / 1099511627776d:F3} TiB)"; - else if(totalSize > 1073741824) - MediaSize = - $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize / 1073741824d:F3} GiB)"; - else if(totalSize > 1048576) - MediaSize = - $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize / 1048576d:F3} MiB)"; - else if(totalSize > 1024) - MediaSize = - $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize / 1024d:F3} KiB)"; - else - MediaSize = - $"Media has {scsiInfo.Blocks} blocks of {scsiInfo.BlockSize} bytes/each. (for a total of {totalSize} bytes)"; - } + string result = await dlgSaveBinary.ShowAsync(_view); - if(scsiInfo.MediaSerialNumber != null) - { - var sbSerial = new StringBuilder(); + if(result is null) + return; - for(int i = 4; i < scsiInfo.MediaSerialNumber.Length; i++) - sbSerial.AppendFormat("{0:X2}", scsiInfo.MediaSerialNumber[i]); + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(data, 0, data.Length); - MediaSerial = sbSerial.ToString(); - } + saveFs.Close(); + } - SaveReadMediaSerialVisible = scsiInfo.MediaSerialNumber != null; - SaveReadCapacityVisible = scsiInfo.ReadCapacity != null; - SaveReadCapacity16Visible = scsiInfo.ReadCapacity16 != null; + void ExecuteSaveReadMediaSerialCommand() => SaveElement(_scsiInfo.MediaSerialNumber); - SaveGetConfigurationVisible = scsiInfo.MmcConfiguration != null; - SaveRecognizedFormatLayersVisible = scsiInfo.RecognizedFormatLayers != null; - SaveWriteProtectionStatusVisible = scsiInfo.WriteProtectionStatus != null; + void ExecuteSaveReadCapacityCommand() => SaveElement(_scsiInfo.ReadCapacity); - MmcVisible = SaveGetConfigurationVisible || SaveRecognizedFormatLayersVisible || - SaveWriteProtectionStatusVisible; + void ExecuteSaveReadCapacity16Command() => SaveElement(_scsiInfo.ReadCapacity16); - if(scsiInfo.DensitySupportHeader.HasValue) - DensitySupport = Decoders.SCSI.SSC.DensitySupport.PrettifyDensity(scsiInfo.DensitySupportHeader); + void ExecuteSaveGetConfigurationCommand() => SaveElement(_scsiInfo.MmcConfiguration); - if(scsiInfo.MediaTypeSupportHeader.HasValue) - MediumSupport = Decoders.SCSI.SSC.DensitySupport.PrettifyMediumType(scsiInfo.MediaTypeSupportHeader); + void ExecuteSaveRecognizedFormatLayersCommand() => SaveElement(_scsiInfo.RecognizedFormatLayers); - SaveDensitySupportVisible = scsiInfo.DensitySupport != null; - SaveMediumSupportVisible = scsiInfo.MediaTypeSupport != null; + void ExecuteSaveWriteProtectionStatusCommand() => SaveElement(_scsiInfo.WriteProtectionStatus); - SscVisible = SaveDensitySupportVisible || SaveMediumSupportVisible; + void ExecuteSaveDensitySupportCommand() => SaveElement(_scsiInfo.DensitySupport); - CompactDiscInfo = new CompactDiscInfo - { - DataContext = new CompactDiscInfoViewModel(scsiInfo.Toc, scsiInfo.Atip, scsiInfo.DiscInformation, - scsiInfo.Session, scsiInfo.RawToc, scsiInfo.Pma, - scsiInfo.CdTextLeadIn, scsiInfo.DecodedToc, - scsiInfo.DecodedAtip, scsiInfo.DecodedSession, - scsiInfo.FullToc, scsiInfo.DecodedCdTextLeadIn, - scsiInfo.DecodedDiscInformation, scsiInfo.Mcn, - scsiInfo.Isrcs, _view) - }; + void ExecuteSaveMediumSupportCommand() => SaveElement(_scsiInfo.MediaTypeSupport); - DvdInfo = new DvdInfo - { - DataContext = new DvdInfoViewModel(scsiInfo.MediaType, scsiInfo.DvdPfi, scsiInfo.DvdDmi, - scsiInfo.DvdCmi, scsiInfo.HddvdCopyrightInformation, scsiInfo.DvdBca, - scsiInfo.DvdAacs, scsiInfo.DecodedPfi, _view) - }; + async void ExecuteDumpCommand() + { + if(_scsiInfo.MediaType == CommonTypes.MediaType.GDR || + _scsiInfo.MediaType == CommonTypes.MediaType.GDROM) + { + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", "GD-ROM dump support is not yet implemented.", ButtonEnum.Ok, + Icon.Error).ShowDialog(_view); - XboxInfo = new XboxInfo - { - DataContext = new XboxInfoViewModel(scsiInfo.XgdInfo, scsiInfo.DvdDmi, scsiInfo.XboxSecuritySector, - scsiInfo.DecodedXboxSecuritySector, _view) - }; - - DvdWritableInfo = new DvdWritableInfo - { - DataContext = new DvdWritableInfoViewModel(scsiInfo.MediaType, scsiInfo.DvdRamDds, - scsiInfo.DvdRamCartridgeStatus, scsiInfo.DvdRamSpareArea, - scsiInfo.LastBorderOutRmd, scsiInfo.DvdPreRecordedInfo, - scsiInfo.DvdrMediaIdentifier, - scsiInfo.DvdrPhysicalInformation, - scsiInfo.HddvdrMediumStatus, scsiInfo.HddvdrLastRmd, - scsiInfo.DvdrLayerCapacity, scsiInfo.DvdrDlMiddleZoneStart, - scsiInfo.DvdrDlJumpIntervalSize, - scsiInfo.DvdrDlManualLayerJumpStartLba, - scsiInfo.DvdrDlRemapAnchorPoint, scsiInfo.DvdPlusAdip, - scsiInfo.DvdPlusDcb, _view) - }; - - BlurayInfo = new BlurayInfo - { - DataContext = new BlurayInfoViewModel(scsiInfo.BlurayDiscInformation, scsiInfo.BlurayBurstCuttingArea, - scsiInfo.BlurayDds, scsiInfo.BlurayCartridgeStatus, - scsiInfo.BluraySpareAreaInformation, scsiInfo.BlurayPowResources, - scsiInfo.BlurayTrackResources, scsiInfo.BlurayRawDfl, - scsiInfo.BlurayPac, _view) - }; + return; } - public ReactiveCommand SaveReadMediaSerialCommand { get; } - public ReactiveCommand SaveReadCapacityCommand { get; } - public ReactiveCommand SaveReadCapacity16Command { get; } - public ReactiveCommand SaveGetConfigurationCommand { get; } - public ReactiveCommand SaveRecognizedFormatLayersCommand { get; } - public ReactiveCommand SaveWriteProtectionStatusCommand { get; } - public ReactiveCommand SaveDensitySupportCommand { get; } - public ReactiveCommand SaveMediumSupportCommand { get; } - public ReactiveCommand DumpCommand { get; } - public ReactiveCommand ScanCommand { get; } - - public Bitmap MediaLogo + if((_scsiInfo.MediaType == CommonTypes.MediaType.XGD || _scsiInfo.MediaType == CommonTypes.MediaType.XGD2 || + _scsiInfo.MediaType == CommonTypes.MediaType.XGD3) && + _scsiInfo.DeviceInfo.ScsiInquiry?.KreonPresent != true) { - get => _mediaLogo; - set => this.RaiseAndSetIfChanged(ref _mediaLogo, value); + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", "Dumping Xbox discs require a Kreon drive.", ButtonEnum.Ok, + Icon.Error).ShowDialog(_view); + + return; } - public string GeneralVisible + var mediaDumpWindow = new MediaDump(); + + mediaDumpWindow.DataContext = + new MediaDumpViewModel(_devicePath, _scsiInfo.DeviceInfo, mediaDumpWindow, _scsiInfo); + + mediaDumpWindow.Show(); + } + + async void ExecuteScanCommand() + { + switch(_scsiInfo.MediaType) { - get => _generalVisible; - set => this.RaiseAndSetIfChanged(ref _generalVisible, value); - } - - public string MediaType - { - get => _mediaType; - set => this.RaiseAndSetIfChanged(ref _mediaType, value); - } - - public string MediaSize - { - get => _mediaSize; - set => this.RaiseAndSetIfChanged(ref _mediaSize, value); - } - - public string MediaSerial - { - get => _mediaSerial; - set => this.RaiseAndSetIfChanged(ref _mediaSerial, value); - } - - public bool SaveReadMediaSerialVisible - { - get => _saveReadMediaSerialVisible; - set => this.RaiseAndSetIfChanged(ref _saveReadMediaSerialVisible, value); - } - - public bool SaveReadCapacityVisible - { - get => _saveReadCapacityVisible; - set => this.RaiseAndSetIfChanged(ref _saveReadCapacityVisible, value); - } - - public bool SaveReadCapacity16Visible - { - get => _saveReadCapacity16Visible; - set => this.RaiseAndSetIfChanged(ref _saveReadCapacity16Visible, value); - } - - public bool MmcVisible - { - get => _mmcVisible; - set => this.RaiseAndSetIfChanged(ref _mmcVisible, value); - } - - public bool SaveGetConfigurationVisible - { - get => _saveGetConfigurationVisible; - set => this.RaiseAndSetIfChanged(ref _saveGetConfigurationVisible, value); - } - - public bool SaveRecognizedFormatLayersVisible - { - get => _saveRecognizedFormatLayersVisible; - set => this.RaiseAndSetIfChanged(ref _saveRecognizedFormatLayersVisible, value); - } - - public bool SaveWriteProtectionStatusVisible - { - get => _saveWriteProtectionStatusVisible; - set => this.RaiseAndSetIfChanged(ref _saveWriteProtectionStatusVisible, value); - } - - public bool SscVisible - { - get => _sscVisible; - set => this.RaiseAndSetIfChanged(ref _sscVisible, value); - } - - public string DensitySupport - { - get => _densitySupport; - set => this.RaiseAndSetIfChanged(ref _densitySupport, value); - } - - public string MediumSupport - { - get => _mediumSupport; - set => this.RaiseAndSetIfChanged(ref _mediumSupport, value); - } - - public bool SaveDensitySupportVisible - { - get => _saveDensitySupportVisible; - set => this.RaiseAndSetIfChanged(ref _saveDensitySupportVisible, value); - } - - public bool SaveMediumSupportVisible - { - get => _saveMediumSupportVisible; - set => this.RaiseAndSetIfChanged(ref _saveMediumSupportVisible, value); - } - - public CompactDiscInfo CompactDiscInfo - { - get => _compactDiscInfo; - set => this.RaiseAndSetIfChanged(ref _compactDiscInfo, value); - } - - public DvdInfo DvdInfo - { - get => _dvdInfo; - set => this.RaiseAndSetIfChanged(ref _dvdInfo, value); - } - - public DvdWritableInfo DvdWritableInfo - { - get => _dvdWritableInfo; - set => this.RaiseAndSetIfChanged(ref _dvdWritableInfo, value); - } - - public XboxInfo XboxInfo - { - get => _xboxInfo; - set => this.RaiseAndSetIfChanged(ref _xboxInfo, value); - } - - public BlurayInfo BlurayInfo - { - get => _blurayInfo; - set => this.RaiseAndSetIfChanged(ref _blurayInfo, value); - } - - async void SaveElement(byte[] data) - { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); - - string result = await dlgSaveBinary.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(data, 0, data.Length); - - saveFs.Close(); - } - - void ExecuteSaveReadMediaSerialCommand() => SaveElement(_scsiInfo.MediaSerialNumber); - - void ExecuteSaveReadCapacityCommand() => SaveElement(_scsiInfo.ReadCapacity); - - void ExecuteSaveReadCapacity16Command() => SaveElement(_scsiInfo.ReadCapacity16); - - void ExecuteSaveGetConfigurationCommand() => SaveElement(_scsiInfo.MmcConfiguration); - - void ExecuteSaveRecognizedFormatLayersCommand() => SaveElement(_scsiInfo.RecognizedFormatLayers); - - void ExecuteSaveWriteProtectionStatusCommand() => SaveElement(_scsiInfo.WriteProtectionStatus); - - void ExecuteSaveDensitySupportCommand() => SaveElement(_scsiInfo.DensitySupport); - - void ExecuteSaveMediumSupportCommand() => SaveElement(_scsiInfo.MediaTypeSupport); - - async void ExecuteDumpCommand() - { - if(_scsiInfo.MediaType == CommonTypes.MediaType.GDR || - _scsiInfo.MediaType == CommonTypes.MediaType.GDROM) - { + // TODO: GD-ROM + case CommonTypes.MediaType.GDR: + case CommonTypes.MediaType.GDROM: await MessageBoxManager. - GetMessageBoxStandardWindow("Error", "GD-ROM dump support is not yet implemented.", ButtonEnum.Ok, - Icon.Error).ShowDialog(_view); + GetMessageBoxStandardWindow("Error", "GD-ROM scan support is not yet implemented.", + ButtonEnum.Ok, Icon.Error).ShowDialog(_view); return; - } - if((_scsiInfo.MediaType == CommonTypes.MediaType.XGD || _scsiInfo.MediaType == CommonTypes.MediaType.XGD2 || - _scsiInfo.MediaType == CommonTypes.MediaType.XGD3) && - _scsiInfo.DeviceInfo.ScsiInquiry?.KreonPresent != true) - { + // TODO: Xbox + case CommonTypes.MediaType.XGD: + case CommonTypes.MediaType.XGD2: + case CommonTypes.MediaType.XGD3: await MessageBoxManager. - GetMessageBoxStandardWindow("Error", "Dumping Xbox discs require a Kreon drive.", ButtonEnum.Ok, - Icon.Error).ShowDialog(_view); + GetMessageBoxStandardWindow("Error", "Scanning Xbox discs is not yet supported.", + ButtonEnum.Ok, Icon.Error).ShowDialog(_view); return; - } - - var mediaDumpWindow = new MediaDump(); - - mediaDumpWindow.DataContext = - new MediaDumpViewModel(_devicePath, _scsiInfo.DeviceInfo, mediaDumpWindow, _scsiInfo); - - mediaDumpWindow.Show(); } - async void ExecuteScanCommand() - { - switch(_scsiInfo.MediaType) - { - // TODO: GD-ROM - case CommonTypes.MediaType.GDR: - case CommonTypes.MediaType.GDROM: - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", "GD-ROM scan support is not yet implemented.", - ButtonEnum.Ok, Icon.Error).ShowDialog(_view); + var mediaScanWindow = new MediaScan(); - return; + mediaScanWindow.DataContext = new MediaScanViewModel(_devicePath, mediaScanWindow); - // TODO: Xbox - case CommonTypes.MediaType.XGD: - case CommonTypes.MediaType.XGD2: - case CommonTypes.MediaType.XGD3: - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", "Scanning Xbox discs is not yet supported.", - ButtonEnum.Ok, Icon.Error).ShowDialog(_view); - - return; - } - - var mediaScanWindow = new MediaScan(); - - mediaScanWindow.DataContext = new MediaScanViewModel(_devicePath, mediaScanWindow); - - mediaScanWindow.Show(); - } + mediaScanWindow.Show(); } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Panels/PartitionViewModel.cs b/Aaru.Gui/ViewModels/Panels/PartitionViewModel.cs index 79ca8d48c..01d6cad2f 100644 --- a/Aaru.Gui/ViewModels/Panels/PartitionViewModel.cs +++ b/Aaru.Gui/ViewModels/Panels/PartitionViewModel.cs @@ -32,25 +32,24 @@ using Aaru.CommonTypes; -namespace Aaru.Gui.ViewModels.Panels -{ - public sealed class PartitionViewModel - { - public PartitionViewModel(Partition partition) - { - NameText = $"Partition name: {partition.Name}"; - TypeText = $"Partition type: {partition.Type}"; - StartText = $"Partition start: sector {partition.Start}, byte {partition.Offset}"; - LengthText = $"Partition length: {partition.Length} sectors, {partition.Size} bytes"; - DescriptionLabelText = "Partition description:"; - DescriptionText = partition.Description; - } +namespace Aaru.Gui.ViewModels.Panels; - public string NameText { get; } - public string TypeText { get; } - public string StartText { get; } - public string LengthText { get; } - public string DescriptionLabelText { get; } - public string DescriptionText { get; } +public sealed class PartitionViewModel +{ + public PartitionViewModel(Partition partition) + { + NameText = $"Partition name: {partition.Name}"; + TypeText = $"Partition type: {partition.Type}"; + StartText = $"Partition start: sector {partition.Start}, byte {partition.Offset}"; + LengthText = $"Partition length: {partition.Length} sectors, {partition.Size} bytes"; + DescriptionLabelText = "Partition description:"; + DescriptionText = partition.Description; } + + public string NameText { get; } + public string TypeText { get; } + public string StartText { get; } + public string LengthText { get; } + public string DescriptionLabelText { get; } + public string DescriptionText { get; } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Panels/SubdirectoryViewModel.cs b/Aaru.Gui/ViewModels/Panels/SubdirectoryViewModel.cs index d5e5e3ff1..e475870d3 100644 --- a/Aaru.Gui/ViewModels/Panels/SubdirectoryViewModel.cs +++ b/Aaru.Gui/ViewModels/Panels/SubdirectoryViewModel.cs @@ -49,221 +49,204 @@ using MessageBox.Avalonia.Enums; using ReactiveUI; using FileAttributes = Aaru.CommonTypes.Structs.FileAttributes; -namespace Aaru.Gui.ViewModels.Panels +namespace Aaru.Gui.ViewModels.Panels; + +public sealed class SubdirectoryViewModel { - public sealed class SubdirectoryViewModel + readonly SubdirectoryModel _model; + readonly Window _view; + + public SubdirectoryViewModel([NotNull] SubdirectoryModel model, Window view) { - readonly SubdirectoryModel _model; - readonly Window _view; + Entries = new ObservableCollection(); + SelectedEntries = new List(); + ExtractFilesCommand = ReactiveCommand.Create(ExecuteExtractFilesCommand); + _model = model; + _view = view; - public SubdirectoryViewModel([NotNull] SubdirectoryModel model, Window view) + ErrorNumber errno = model.Plugin.ReadDir(model.Path, out List dirents); + + if(errno != ErrorNumber.NoError) { - Entries = new ObservableCollection(); - SelectedEntries = new List(); - ExtractFilesCommand = ReactiveCommand.Create(ExecuteExtractFilesCommand); - _model = model; - _view = view; + MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {errno} trying to read \"{model.Path}\" of chosen filesystem", + ButtonEnum.Ok, Icon.Error).ShowDialog(view); - ErrorNumber errno = model.Plugin.ReadDir(model.Path, out List dirents); + return; + } + + foreach(string dirent in dirents) + { + errno = model.Plugin.Stat(model.Path + "/" + dirent, out FileEntryInfo stat); if(errno != ErrorNumber.NoError) { - MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {errno} trying to read \"{model.Path}\" of chosen filesystem", - ButtonEnum.Ok, Icon.Error).ShowDialog(view); + AaruConsole. + ErrorWriteLine($"Error {errno} trying to get information about filesystem entry named {dirent}"); - return; + continue; } - foreach(string dirent in dirents) + if(stat.Attributes.HasFlag(FileAttributes.Directory) && + !model.Listed) { - errno = model.Plugin.Stat(model.Path + "/" + dirent, out FileEntryInfo stat); - - if(errno != ErrorNumber.NoError) + model.Subdirectories.Add(new SubdirectoryModel { - AaruConsole. - ErrorWriteLine($"Error {errno} trying to get information about filesystem entry named {dirent}"); - - continue; - } - - if(stat.Attributes.HasFlag(FileAttributes.Directory) && - !model.Listed) - { - model.Subdirectories.Add(new SubdirectoryModel - { - Name = dirent, - Path = model.Path + "/" + dirent, - Plugin = model.Plugin - }); - - continue; - } - - Entries.Add(new FileModel - { - Name = dirent, - Stat = stat + Name = dirent, + Path = model.Path + "/" + dirent, + Plugin = model.Plugin }); + + continue; } + + Entries.Add(new FileModel + { + Name = dirent, + Stat = stat + }); } + } - public ObservableCollection Entries { get; } - public List SelectedEntries { get; } - public ReactiveCommand ExtractFilesCommand { get; } + public ObservableCollection Entries { get; } + public List SelectedEntries { get; } + public ReactiveCommand ExtractFilesCommand { get; } - async void ExecuteExtractFilesCommand() + async void ExecuteExtractFilesCommand() + { + if(SelectedEntries.Count == 0) + return; + + ButtonResult mboxResult; + + var saveFilesFolderDialog = new OpenFolderDialog { - if(SelectedEntries.Count == 0) - return; + Title = "Choose destination folder..." + }; - ButtonResult mboxResult; + string result = await saveFilesFolderDialog.ShowAsync(_view); - var saveFilesFolderDialog = new OpenFolderDialog - { - Title = "Choose destination folder..." - }; + if(result is null) + return; - string result = await saveFilesFolderDialog.ShowAsync(_view); + Statistics.AddCommand("extract-files"); - if(result is null) - return; + string folder = saveFilesFolderDialog.Directory; - Statistics.AddCommand("extract-files"); + foreach(FileModel file in SelectedEntries) + { + string filename = file.Name; - string folder = saveFilesFolderDialog.Directory; + if(DetectOS.IsWindows) + if(filename.Contains('<') || + filename.Contains('>') || + filename.Contains(':') || + filename.Contains('\\') || + filename.Contains('/') || + filename.Contains('|') || + filename.Contains('?') || + filename.Contains('*') || + filename.Any(c => c < 32) || + filename.ToUpperInvariant() == "CON" || + filename.ToUpperInvariant() == "PRN" || + filename.ToUpperInvariant() == "AUX" || + filename.ToUpperInvariant() == "COM1" || + filename.ToUpperInvariant() == "COM2" || + filename.ToUpperInvariant() == "COM3" || + filename.ToUpperInvariant() == "COM4" || + filename.ToUpperInvariant() == "COM5" || + filename.ToUpperInvariant() == "COM6" || + filename.ToUpperInvariant() == "COM7" || + filename.ToUpperInvariant() == "COM8" || + filename.ToUpperInvariant() == "COM9" || + filename.ToUpperInvariant() == "LPT1" || + filename.ToUpperInvariant() == "LPT2" || + filename.ToUpperInvariant() == "LPT3" || + filename.ToUpperInvariant() == "LPT4" || + filename.ToUpperInvariant() == "LPT5" || + filename.ToUpperInvariant() == "LPT6" || + filename.ToUpperInvariant() == "LPT7" || + filename.ToUpperInvariant() == "LPT8" || + filename.ToUpperInvariant() == "LPT9" || + filename.Last() == '.' || + filename.Last() == ' ') + { + char[] chars; - foreach(FileModel file in SelectedEntries) - { - string filename = file.Name; + if(filename.Last() == '.' || + filename.Last() == ' ') + chars = new char[filename.Length - 1]; + else + chars = new char[filename.Length]; - if(DetectOS.IsWindows) - if(filename.Contains('<') || - filename.Contains('>') || - filename.Contains(':') || - filename.Contains('\\') || - filename.Contains('/') || - filename.Contains('|') || - filename.Contains('?') || - filename.Contains('*') || - filename.Any(c => c < 32) || - filename.ToUpperInvariant() == "CON" || - filename.ToUpperInvariant() == "PRN" || - filename.ToUpperInvariant() == "AUX" || - filename.ToUpperInvariant() == "COM1" || - filename.ToUpperInvariant() == "COM2" || - filename.ToUpperInvariant() == "COM3" || - filename.ToUpperInvariant() == "COM4" || - filename.ToUpperInvariant() == "COM5" || - filename.ToUpperInvariant() == "COM6" || - filename.ToUpperInvariant() == "COM7" || - filename.ToUpperInvariant() == "COM8" || - filename.ToUpperInvariant() == "COM9" || - filename.ToUpperInvariant() == "LPT1" || - filename.ToUpperInvariant() == "LPT2" || - filename.ToUpperInvariant() == "LPT3" || - filename.ToUpperInvariant() == "LPT4" || - filename.ToUpperInvariant() == "LPT5" || - filename.ToUpperInvariant() == "LPT6" || - filename.ToUpperInvariant() == "LPT7" || - filename.ToUpperInvariant() == "LPT8" || - filename.ToUpperInvariant() == "LPT9" || - filename.Last() == '.' || - filename.Last() == ' ') - { - char[] chars; - - if(filename.Last() == '.' || - filename.Last() == ' ') - chars = new char[filename.Length - 1]; - else - chars = new char[filename.Length]; - - for(int ci = 0; ci < chars.Length; ci++) - switch(filename[ci]) - { - case '<': - case '>': - case ':': - case '\\': - case '/': - case '|': - case '?': - case '*': - case '\u0000': - case '\u0001': - case '\u0002': - case '\u0003': - case '\u0004': - case '\u0005': - case '\u0006': - case '\u0007': - case '\u0008': - case '\u0009': - case '\u000A': - case '\u000B': - case '\u000C': - case '\u000D': - case '\u000E': - case '\u000F': - case '\u0010': - case '\u0011': - case '\u0012': - case '\u0013': - case '\u0014': - case '\u0015': - case '\u0016': - case '\u0017': - case '\u0018': - case '\u0019': - case '\u001A': - case '\u001B': - case '\u001C': - case '\u001D': - case '\u001E': - case '\u001F': - chars[ci] = '_'; - - break; - default: - chars[ci] = filename[ci]; - - break; - } - - if(filename.StartsWith("CON", StringComparison.InvariantCultureIgnoreCase) || - filename.StartsWith("PRN", StringComparison.InvariantCultureIgnoreCase) || - filename.StartsWith("AUX", StringComparison.InvariantCultureIgnoreCase) || - filename.StartsWith("COM", StringComparison.InvariantCultureIgnoreCase) || - filename.StartsWith("LPT", StringComparison.InvariantCultureIgnoreCase)) + for(int ci = 0; ci < chars.Length; ci++) + switch(filename[ci]) { - chars[0] = '_'; - chars[1] = '_'; - chars[2] = '_'; + case '<': + case '>': + case ':': + case '\\': + case '/': + case '|': + case '?': + case '*': + case '\u0000': + case '\u0001': + case '\u0002': + case '\u0003': + case '\u0004': + case '\u0005': + case '\u0006': + case '\u0007': + case '\u0008': + case '\u0009': + case '\u000A': + case '\u000B': + case '\u000C': + case '\u000D': + case '\u000E': + case '\u000F': + case '\u0010': + case '\u0011': + case '\u0012': + case '\u0013': + case '\u0014': + case '\u0015': + case '\u0016': + case '\u0017': + case '\u0018': + case '\u0019': + case '\u001A': + case '\u001B': + case '\u001C': + case '\u001D': + case '\u001E': + case '\u001F': + chars[ci] = '_'; + + break; + default: + chars[ci] = filename[ci]; + + break; } - string corrected = new(chars); - - mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Unsupported filename", - $"The file name {filename} is not supported on this platform.\nDo you want to rename it to {corrected}?", - ButtonEnum.YesNoCancel, Icon.Warning).ShowDialog(_view); - - if(mboxResult == ButtonResult.Cancel) - return; - - if(mboxResult == ButtonResult.No) - continue; - - filename = corrected; + if(filename.StartsWith("CON", StringComparison.InvariantCultureIgnoreCase) || + filename.StartsWith("PRN", StringComparison.InvariantCultureIgnoreCase) || + filename.StartsWith("AUX", StringComparison.InvariantCultureIgnoreCase) || + filename.StartsWith("COM", StringComparison.InvariantCultureIgnoreCase) || + filename.StartsWith("LPT", StringComparison.InvariantCultureIgnoreCase)) + { + chars[0] = '_'; + chars[1] = '_'; + chars[2] = '_'; } - string outputPath = Path.Combine(folder, filename); + string corrected = new(chars); - if(File.Exists(outputPath)) - { - mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Existing file", - $"A file named {filename} already exists on the destination folder.\nDo you want to overwrite it?", + mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Unsupported filename", + $"The file name {filename} is not supported on this platform.\nDo you want to rename it to {corrected}?", ButtonEnum.YesNoCancel, Icon.Warning).ShowDialog(_view); if(mboxResult == ButtonResult.Cancel) @@ -272,87 +255,103 @@ namespace Aaru.Gui.ViewModels.Panels if(mboxResult == ButtonResult.No) continue; - try - { - File.Delete(outputPath); - } - catch(IOException) - { - mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Cannot delete", - "Could not delete existing file.\nDo you want to continue?", - ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); - - if(mboxResult == ButtonResult.No) - return; - } + filename = corrected; } + string outputPath = Path.Combine(folder, filename); + + if(File.Exists(outputPath)) + { + mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Existing file", + $"A file named {filename} already exists on the destination folder.\nDo you want to overwrite it?", + ButtonEnum.YesNoCancel, Icon.Warning).ShowDialog(_view); + + if(mboxResult == ButtonResult.Cancel) + return; + + if(mboxResult == ButtonResult.No) + continue; + try { - byte[] outBuf = Array.Empty(); - - ErrorNumber error = - _model.Plugin.Read(_model.Path + "/" + file.Name, 0, file.Stat.Length, ref outBuf); - - if(error != ErrorNumber.NoError) - { - mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Error reading file", - $"Error {error} reading file.\nDo you want to continue?", - ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); - - if(mboxResult == ButtonResult.No) - return; - - continue; - } - - var fs = new FileStream(outputPath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); - - fs.Write(outBuf, 0, outBuf.Length); - fs.Close(); - var fi = new FileInfo(outputPath); - #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body - try - { - if(file.Stat.CreationTimeUtc.HasValue) - fi.CreationTimeUtc = file.Stat.CreationTimeUtc.Value; - } - catch - { - // ignored - } - - try - { - if(file.Stat.LastWriteTimeUtc.HasValue) - fi.LastWriteTimeUtc = file.Stat.LastWriteTimeUtc.Value; - } - catch - { - // ignored - } - - try - { - if(file.Stat.AccessTimeUtc.HasValue) - fi.LastAccessTimeUtc = file.Stat.AccessTimeUtc.Value; - } - catch - { - // ignored - } - #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body + File.Delete(outputPath); } catch(IOException) { - mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Cannot create file", - "Could not create destination file.\nDo you want to continue?", + mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Cannot delete", + "Could not delete existing file.\nDo you want to continue?", ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); if(mboxResult == ButtonResult.No) return; } } + + try + { + byte[] outBuf = Array.Empty(); + + ErrorNumber error = + _model.Plugin.Read(_model.Path + "/" + file.Name, 0, file.Stat.Length, ref outBuf); + + if(error != ErrorNumber.NoError) + { + mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Error reading file", + $"Error {error} reading file.\nDo you want to continue?", + ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); + + if(mboxResult == ButtonResult.No) + return; + + continue; + } + + var fs = new FileStream(outputPath, FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None); + + fs.Write(outBuf, 0, outBuf.Length); + fs.Close(); + var fi = new FileInfo(outputPath); + #pragma warning disable RECS0022 // A catch clause that catches System.Exception and has an empty body + try + { + if(file.Stat.CreationTimeUtc.HasValue) + fi.CreationTimeUtc = file.Stat.CreationTimeUtc.Value; + } + catch + { + // ignored + } + + try + { + if(file.Stat.LastWriteTimeUtc.HasValue) + fi.LastWriteTimeUtc = file.Stat.LastWriteTimeUtc.Value; + } + catch + { + // ignored + } + + try + { + if(file.Stat.AccessTimeUtc.HasValue) + fi.LastAccessTimeUtc = file.Stat.AccessTimeUtc.Value; + } + catch + { + // ignored + } + #pragma warning restore RECS0022 // A catch clause that catches System.Exception and has an empty body + } + catch(IOException) + { + mboxResult = await MessageBoxManager.GetMessageBoxStandardWindow("Cannot create file", + "Could not create destination file.\nDo you want to continue?", + ButtonEnum.YesNo, Icon.Error).ShowDialog(_view); + + if(mboxResult == ButtonResult.No) + return; + } } } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/AtaInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/AtaInfoViewModel.cs index c35342e16..fe4a03ed3 100644 --- a/Aaru.Gui/ViewModels/Tabs/AtaInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/AtaInfoViewModel.cs @@ -38,143 +38,142 @@ using Avalonia.Controls; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public sealed class AtaInfoViewModel : ViewModelBase { - public sealed class AtaInfoViewModel : ViewModelBase + readonly byte[] _ata; + readonly byte[] _atapi; + readonly Window _view; + + public AtaInfoViewModel([CanBeNull] byte[] ataIdentify, byte[] atapiIdentify, + AtaErrorRegistersChs? ataMcptError, Window view) { - readonly byte[] _ata; - readonly byte[] _atapi; - readonly Window _view; + SaveAtaBinaryCommand = ReactiveCommand.Create(ExecuteSaveAtaBinaryCommand); + SaveAtaTextCommand = ReactiveCommand.Create(ExecuteSaveAtaTextCommand); - public AtaInfoViewModel([CanBeNull] byte[] ataIdentify, byte[] atapiIdentify, - AtaErrorRegistersChs? ataMcptError, Window view) + _ata = ataIdentify; + _atapi = atapiIdentify; + _view = view; + + if(ataIdentify == null && + atapiIdentify == null) + return; + + if(ataIdentify != null) { - SaveAtaBinaryCommand = ReactiveCommand.Create(ExecuteSaveAtaBinaryCommand); - SaveAtaTextCommand = ReactiveCommand.Create(ExecuteSaveAtaTextCommand); + AtaMcptVisible = true; + AtaMcptChecked = ataMcptError.HasValue; + AtaOrAtapiText = "ATA IDENTIFY DEVICE"; - _ata = ataIdentify; - _atapi = atapiIdentify; - _view = view; - - if(ataIdentify == null && - atapiIdentify == null) - return; - - if(ataIdentify != null) + if(ataMcptError.HasValue) { - AtaMcptVisible = true; - AtaMcptChecked = ataMcptError.HasValue; - AtaOrAtapiText = "ATA IDENTIFY DEVICE"; - - if(ataMcptError.HasValue) + switch(ataMcptError.Value.DeviceHead & 0x7) { - switch(ataMcptError.Value.DeviceHead & 0x7) - { - case 0: - AtaMcptText = "Device reports incorrect media card type"; + case 0: + AtaMcptText = "Device reports incorrect media card type"; - break; - case 1: - AtaMcptText = "Device contains a Secure Digital card"; + break; + case 1: + AtaMcptText = "Device contains a Secure Digital card"; - break; - case 2: - AtaMcptText = "Device contains a MultiMediaCard "; + break; + case 2: + AtaMcptText = "Device contains a MultiMediaCard "; - break; - case 3: - AtaMcptText = "Device contains a Secure Digital I/O card"; + break; + case 3: + AtaMcptText = "Device contains a Secure Digital I/O card"; - break; - case 4: - AtaMcptText = "Device contains a Smart Media card"; + break; + case 4: + AtaMcptText = "Device contains a Smart Media card"; - break; - default: - AtaMcptText = - $"Device contains unknown media card type {ataMcptError.Value.DeviceHead & 0x07}"; + break; + default: + AtaMcptText = + $"Device contains unknown media card type {ataMcptError.Value.DeviceHead & 0x07}"; - break; - } - - AtaMcptWriteProtectionChecked = (ataMcptError.Value.DeviceHead & 0x08) == 0x08; - - ushort specificData = (ushort)((ataMcptError.Value.CylinderHigh * 0x100) + - ataMcptError.Value.CylinderLow); - - AtaMcptSpecificDataText = $"Card specific data: 0x{specificData:X4}"; + break; } - AtaIdentifyText = Identify.Prettify(_ata); - } - else - { - AtaOrAtapiText = "ATA PACKET IDENTIFY DEVICE"; - AtaIdentifyText = Identify.Prettify(_atapi); + AtaMcptWriteProtectionChecked = (ataMcptError.Value.DeviceHead & 0x08) == 0x08; + + ushort specificData = (ushort)((ataMcptError.Value.CylinderHigh * 0x100) + + ataMcptError.Value.CylinderLow); + + AtaMcptSpecificDataText = $"Card specific data: 0x{specificData:X4}"; } + + AtaIdentifyText = Identify.Prettify(_ata); } - - public string AtaIdentifyText { get; } - public string AtaMcptText { get; } - public string AtaMcptSpecificDataText { get; } - public bool AtaMcptChecked { get; } - public bool AtaMcptWriteProtectionChecked { get; } - public bool AtaMcptVisible { get; } - public ReactiveCommand SaveAtaBinaryCommand { get; } - public ReactiveCommand SaveAtaTextCommand { get; } - - public string AtaOrAtapiText { get; } - - async void ExecuteSaveAtaBinaryCommand() + else { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); - - string result = await dlgSaveBinary.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - - if(_ata != null) - saveFs.Write(_ata, 0, _ata.Length); - else if(_atapi != null) - saveFs.Write(_atapi, 0, _atapi.Length); - - saveFs.Close(); - } - - async void ExecuteSaveAtaTextCommand() - { - var dlgSaveText = new SaveFileDialog(); - - dlgSaveText.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.txt" - }), - Name = "Text" - }); - - string result = await dlgSaveText.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - var saveSw = new StreamWriter(saveFs); - saveSw.Write(AtaIdentifyText); - saveFs.Close(); + AtaOrAtapiText = "ATA PACKET IDENTIFY DEVICE"; + AtaIdentifyText = Identify.Prettify(_atapi); } } + + public string AtaIdentifyText { get; } + public string AtaMcptText { get; } + public string AtaMcptSpecificDataText { get; } + public bool AtaMcptChecked { get; } + public bool AtaMcptWriteProtectionChecked { get; } + public bool AtaMcptVisible { get; } + public ReactiveCommand SaveAtaBinaryCommand { get; } + public ReactiveCommand SaveAtaTextCommand { get; } + + public string AtaOrAtapiText { get; } + + async void ExecuteSaveAtaBinaryCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + + if(_ata != null) + saveFs.Write(_ata, 0, _ata.Length); + else if(_atapi != null) + saveFs.Write(_atapi, 0, _atapi.Length); + + saveFs.Close(); + } + + async void ExecuteSaveAtaTextCommand() + { + var dlgSaveText = new SaveFileDialog(); + + dlgSaveText.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.txt" + }), + Name = "Text" + }); + + string result = await dlgSaveText.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + var saveSw = new StreamWriter(saveFs); + saveSw.Write(AtaIdentifyText); + saveFs.Close(); + } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/BlurayInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/BlurayInfoViewModel.cs index 5e253c004..75a10ae81 100644 --- a/Aaru.Gui/ViewModels/Tabs/BlurayInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/BlurayInfoViewModel.cs @@ -39,162 +39,161 @@ using Avalonia.Controls; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public sealed class BlurayInfoViewModel { - public sealed class BlurayInfoViewModel + readonly byte[] _burstCuttingArea; + readonly byte[] _cartridgeStatus; + readonly byte[] _dds; + readonly byte[] _discInformation; + readonly byte[] _pac; + readonly byte[] _powResources; + readonly byte[] _rawDfl; + readonly byte[] _spareAreaInformation; + readonly byte[] _trackResources; + readonly Window _view; + + public BlurayInfoViewModel([CanBeNull] byte[] blurayDiscInformation, [CanBeNull] byte[] blurayBurstCuttingArea, + [CanBeNull] byte[] blurayDds, [CanBeNull] byte[] blurayCartridgeStatus, + [CanBeNull] byte[] bluraySpareAreaInformation, [CanBeNull] byte[] blurayPowResources, + [CanBeNull] byte[] blurayTrackResources, [CanBeNull] byte[] blurayRawDfl, + [CanBeNull] byte[] blurayPac, Window view) { - readonly byte[] _burstCuttingArea; - readonly byte[] _cartridgeStatus; - readonly byte[] _dds; - readonly byte[] _discInformation; - readonly byte[] _pac; - readonly byte[] _powResources; - readonly byte[] _rawDfl; - readonly byte[] _spareAreaInformation; - readonly byte[] _trackResources; - readonly Window _view; + _view = view; + _discInformation = blurayDiscInformation; + _burstCuttingArea = blurayBurstCuttingArea; + _dds = blurayDds; + _cartridgeStatus = blurayCartridgeStatus; + _spareAreaInformation = bluraySpareAreaInformation; + _powResources = blurayPowResources; + _trackResources = blurayTrackResources; + _rawDfl = blurayRawDfl; + _pac = blurayPac; + SaveBlurayDiscInformationCommand = ReactiveCommand.Create(ExecuteSaveBlurayDiscInformationCommand); + SaveBlurayBurstCuttingAreaCommand = ReactiveCommand.Create(ExecuteSaveBlurayBurstCuttingAreaCommand); + SaveBlurayDdsCommand = ReactiveCommand.Create(ExecuteSaveBlurayDdsCommand); + SaveBlurayCartridgeStatusCommand = ReactiveCommand.Create(ExecuteSaveBlurayCartridgeStatusCommand); - public BlurayInfoViewModel([CanBeNull] byte[] blurayDiscInformation, [CanBeNull] byte[] blurayBurstCuttingArea, - [CanBeNull] byte[] blurayDds, [CanBeNull] byte[] blurayCartridgeStatus, - [CanBeNull] byte[] bluraySpareAreaInformation, [CanBeNull] byte[] blurayPowResources, - [CanBeNull] byte[] blurayTrackResources, [CanBeNull] byte[] blurayRawDfl, - [CanBeNull] byte[] blurayPac, Window view) + SaveBluraySpareAreaInformationCommand = + ReactiveCommand.Create(ExecuteSaveBluraySpareAreaInformationCommand); + + SaveBlurayPowResourcesCommand = ReactiveCommand.Create(ExecuteSaveBlurayPowResourcesCommand); + SaveBlurayTrackResourcesCommand = ReactiveCommand.Create(ExecuteSaveBlurayTrackResourcesCommand); + SaveBlurayRawDflCommand = ReactiveCommand.Create(ExecuteSaveBlurayRawDflCommand); + SaveBlurayPacCommand = ReactiveCommand.Create(ExecuteSaveBlurayPacCommand); + + if(blurayDiscInformation != null) { - _view = view; - _discInformation = blurayDiscInformation; - _burstCuttingArea = blurayBurstCuttingArea; - _dds = blurayDds; - _cartridgeStatus = blurayCartridgeStatus; - _spareAreaInformation = bluraySpareAreaInformation; - _powResources = blurayPowResources; - _trackResources = blurayTrackResources; - _rawDfl = blurayRawDfl; - _pac = blurayPac; - SaveBlurayDiscInformationCommand = ReactiveCommand.Create(ExecuteSaveBlurayDiscInformationCommand); - SaveBlurayBurstCuttingAreaCommand = ReactiveCommand.Create(ExecuteSaveBlurayBurstCuttingAreaCommand); - SaveBlurayDdsCommand = ReactiveCommand.Create(ExecuteSaveBlurayDdsCommand); - SaveBlurayCartridgeStatusCommand = ReactiveCommand.Create(ExecuteSaveBlurayCartridgeStatusCommand); - - SaveBluraySpareAreaInformationCommand = - ReactiveCommand.Create(ExecuteSaveBluraySpareAreaInformationCommand); - - SaveBlurayPowResourcesCommand = ReactiveCommand.Create(ExecuteSaveBlurayPowResourcesCommand); - SaveBlurayTrackResourcesCommand = ReactiveCommand.Create(ExecuteSaveBlurayTrackResourcesCommand); - SaveBlurayRawDflCommand = ReactiveCommand.Create(ExecuteSaveBlurayRawDflCommand); - SaveBlurayPacCommand = ReactiveCommand.Create(ExecuteSaveBlurayPacCommand); - - if(blurayDiscInformation != null) - { - SaveBlurayDiscInformationVisible = true; - BlurayDiscInformationText = DI.Prettify(blurayDiscInformation); - } - - if(blurayBurstCuttingArea != null) - { - SaveBlurayBurstCuttingAreaVisible = true; - BlurayBurstCuttingAreaText = BCA.Prettify(blurayBurstCuttingArea); - } - - if(blurayDds != null) - { - SaveBlurayDdsVisible = true; - BlurayDdsText = DDS.Prettify(blurayDds); - } - - if(blurayCartridgeStatus != null) - { - SaveBlurayCartridgeStatusVisible = true; - BlurayCartridgeStatusText = Cartridge.Prettify(blurayCartridgeStatus); - } - - if(bluraySpareAreaInformation != null) - { - SaveBluraySpareAreaInformationVisible = true; - BluraySpareAreaInformationText = Spare.Prettify(bluraySpareAreaInformation); - } - - if(blurayPowResources != null) - { - SaveBlurayPowResourcesVisible = true; - BlurayPowResourcesText = DiscInformation.Prettify(blurayPowResources); - } - - if(blurayTrackResources != null) - { - SaveBlurayTrackResourcesVisible = true; - BlurayTrackResourcesText = DiscInformation.Prettify(blurayTrackResources); - } - - SaveBlurayRawDflVisible = blurayRawDfl != null; - SaveBlurayPacVisible = blurayPac != null; + SaveBlurayDiscInformationVisible = true; + BlurayDiscInformationText = DI.Prettify(blurayDiscInformation); } - public string BlurayDiscInformationText { get; } - public string BlurayBurstCuttingAreaText { get; } - public string BlurayDdsText { get; } - public string BlurayCartridgeStatusText { get; } - public string BluraySpareAreaInformationText { get; } - public string BlurayPowResourcesText { get; } - public string BlurayTrackResourcesText { get; } - public ReactiveCommand SaveBlurayDiscInformationCommand { get; } - public ReactiveCommand SaveBlurayBurstCuttingAreaCommand { get; } - public ReactiveCommand SaveBlurayDdsCommand { get; } - public ReactiveCommand SaveBlurayCartridgeStatusCommand { get; } - public ReactiveCommand SaveBluraySpareAreaInformationCommand { get; } - public ReactiveCommand SaveBlurayPowResourcesCommand { get; } - public ReactiveCommand SaveBlurayTrackResourcesCommand { get; } - public ReactiveCommand SaveBlurayRawDflCommand { get; } - public ReactiveCommand SaveBlurayPacCommand { get; } - public bool SaveBlurayDiscInformationVisible { get; } - public bool SaveBlurayBurstCuttingAreaVisible { get; } - public bool SaveBlurayDdsVisible { get; } - public bool SaveBlurayCartridgeStatusVisible { get; } - public bool SaveBluraySpareAreaInformationVisible { get; } - public bool SaveBlurayPowResourcesVisible { get; } - public bool SaveBlurayTrackResourcesVisible { get; } - public bool SaveBlurayRawDflVisible { get; } - public bool SaveBlurayPacVisible { get; } - - async void SaveElement(byte[] data) + if(blurayBurstCuttingArea != null) { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); - - string result = await dlgSaveBinary.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(data, 0, data.Length); - - saveFs.Close(); + SaveBlurayBurstCuttingAreaVisible = true; + BlurayBurstCuttingAreaText = BCA.Prettify(blurayBurstCuttingArea); } - void ExecuteSaveBlurayDiscInformationCommand() => SaveElement(_discInformation); + if(blurayDds != null) + { + SaveBlurayDdsVisible = true; + BlurayDdsText = DDS.Prettify(blurayDds); + } - void ExecuteSaveBlurayBurstCuttingAreaCommand() => SaveElement(_burstCuttingArea); + if(blurayCartridgeStatus != null) + { + SaveBlurayCartridgeStatusVisible = true; + BlurayCartridgeStatusText = Cartridge.Prettify(blurayCartridgeStatus); + } - void ExecuteSaveBlurayDdsCommand() => SaveElement(_dds); + if(bluraySpareAreaInformation != null) + { + SaveBluraySpareAreaInformationVisible = true; + BluraySpareAreaInformationText = Spare.Prettify(bluraySpareAreaInformation); + } - void ExecuteSaveBlurayCartridgeStatusCommand() => SaveElement(_cartridgeStatus); + if(blurayPowResources != null) + { + SaveBlurayPowResourcesVisible = true; + BlurayPowResourcesText = DiscInformation.Prettify(blurayPowResources); + } - void ExecuteSaveBluraySpareAreaInformationCommand() => SaveElement(_spareAreaInformation); + if(blurayTrackResources != null) + { + SaveBlurayTrackResourcesVisible = true; + BlurayTrackResourcesText = DiscInformation.Prettify(blurayTrackResources); + } - void ExecuteSaveBlurayPowResourcesCommand() => SaveElement(_powResources); - - void ExecuteSaveBlurayTrackResourcesCommand() => SaveElement(_trackResources); - - void ExecuteSaveBlurayRawDflCommand() => SaveElement(_rawDfl); - - void ExecuteSaveBlurayPacCommand() => SaveElement(_pac); + SaveBlurayRawDflVisible = blurayRawDfl != null; + SaveBlurayPacVisible = blurayPac != null; } + + public string BlurayDiscInformationText { get; } + public string BlurayBurstCuttingAreaText { get; } + public string BlurayDdsText { get; } + public string BlurayCartridgeStatusText { get; } + public string BluraySpareAreaInformationText { get; } + public string BlurayPowResourcesText { get; } + public string BlurayTrackResourcesText { get; } + public ReactiveCommand SaveBlurayDiscInformationCommand { get; } + public ReactiveCommand SaveBlurayBurstCuttingAreaCommand { get; } + public ReactiveCommand SaveBlurayDdsCommand { get; } + public ReactiveCommand SaveBlurayCartridgeStatusCommand { get; } + public ReactiveCommand SaveBluraySpareAreaInformationCommand { get; } + public ReactiveCommand SaveBlurayPowResourcesCommand { get; } + public ReactiveCommand SaveBlurayTrackResourcesCommand { get; } + public ReactiveCommand SaveBlurayRawDflCommand { get; } + public ReactiveCommand SaveBlurayPacCommand { get; } + public bool SaveBlurayDiscInformationVisible { get; } + public bool SaveBlurayBurstCuttingAreaVisible { get; } + public bool SaveBlurayDdsVisible { get; } + public bool SaveBlurayCartridgeStatusVisible { get; } + public bool SaveBluraySpareAreaInformationVisible { get; } + public bool SaveBlurayPowResourcesVisible { get; } + public bool SaveBlurayTrackResourcesVisible { get; } + public bool SaveBlurayRawDflVisible { get; } + public bool SaveBlurayPacVisible { get; } + + async void SaveElement(byte[] data) + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(data, 0, data.Length); + + saveFs.Close(); + } + + void ExecuteSaveBlurayDiscInformationCommand() => SaveElement(_discInformation); + + void ExecuteSaveBlurayBurstCuttingAreaCommand() => SaveElement(_burstCuttingArea); + + void ExecuteSaveBlurayDdsCommand() => SaveElement(_dds); + + void ExecuteSaveBlurayCartridgeStatusCommand() => SaveElement(_cartridgeStatus); + + void ExecuteSaveBluraySpareAreaInformationCommand() => SaveElement(_spareAreaInformation); + + void ExecuteSaveBlurayPowResourcesCommand() => SaveElement(_powResources); + + void ExecuteSaveBlurayTrackResourcesCommand() => SaveElement(_trackResources); + + void ExecuteSaveBlurayRawDflCommand() => SaveElement(_rawDfl); + + void ExecuteSaveBlurayPacCommand() => SaveElement(_pac); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/CompactDiscInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/CompactDiscInfoViewModel.cs index cfade5642..af8ee046e 100644 --- a/Aaru.Gui/ViewModels/Tabs/CompactDiscInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/CompactDiscInfoViewModel.cs @@ -40,263 +40,262 @@ using Aaru.Gui.Models; using Avalonia.Controls; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public sealed class CompactDiscInfoViewModel : ViewModelBase { - public sealed class CompactDiscInfoViewModel : ViewModelBase + readonly byte[] _atipData; + readonly byte[] _cdTextLeadInData; + readonly byte[] _compactDiscInformationData; + readonly byte[] _pmaData; + readonly byte[] _rawTocData; + readonly byte[] _sessionData; + readonly byte[] _tocData; + readonly Window _view; + + public CompactDiscInfoViewModel(byte[] toc, byte[] atip, byte[] compactDiscInformation, byte[] session, + byte[] rawToc, byte[] pma, byte[] cdTextLeadIn, TOC.CDTOC? decodedToc, + ATIP.CDATIP decodedAtip, Session.CDSessionInfo? decodedSession, + FullTOC.CDFullTOC? fullToc, CDTextOnLeadIn.CDText? decodedCdTextLeadIn, + DiscInformation.StandardDiscInformation? decodedCompactDiscInformation, + string mcn, Dictionary isrcs, Window view) { - readonly byte[] _atipData; - readonly byte[] _cdTextLeadInData; - readonly byte[] _compactDiscInformationData; - readonly byte[] _pmaData; - readonly byte[] _rawTocData; - readonly byte[] _sessionData; - readonly byte[] _tocData; - readonly Window _view; + _tocData = toc; + _atipData = atip; + _compactDiscInformationData = compactDiscInformation; + _sessionData = session; + _rawTocData = rawToc; + _pmaData = pma; + _cdTextLeadInData = cdTextLeadIn; + _view = view; + IsrcList = new ObservableCollection(); + SaveCdInformationCommand = ReactiveCommand.Create(ExecuteSaveCdInformationCommand); + SaveCdTocCommand = ReactiveCommand.Create(ExecuteSaveCdTocCommand); + SaveCdFullTocCommand = ReactiveCommand.Create(ExecuteSaveCdFullTocCommand); + SaveCdSessionCommand = ReactiveCommand.Create(ExecuteSaveCdSessionCommand); + SaveCdTextCommand = ReactiveCommand.Create(ExecuteSaveCdTextCommand); + SaveCdAtipCommand = ReactiveCommand.Create(ExecuteSaveCdAtipCommand); + SaveCdPmaCommand = ReactiveCommand.Create(ExecuteSaveCdPmaCommand); - public CompactDiscInfoViewModel(byte[] toc, byte[] atip, byte[] compactDiscInformation, byte[] session, - byte[] rawToc, byte[] pma, byte[] cdTextLeadIn, TOC.CDTOC? decodedToc, - ATIP.CDATIP decodedAtip, Session.CDSessionInfo? decodedSession, - FullTOC.CDFullTOC? fullToc, CDTextOnLeadIn.CDText? decodedCdTextLeadIn, - DiscInformation.StandardDiscInformation? decodedCompactDiscInformation, - string mcn, Dictionary isrcs, Window view) + if(decodedCompactDiscInformation.HasValue) + CdInformationText = DiscInformation.Prettify000b(decodedCompactDiscInformation); + + if(decodedToc.HasValue) + CdTocText = TOC.Prettify(decodedToc); + + if(fullToc.HasValue) + CdFullTocText = FullTOC.Prettify(fullToc); + + if(decodedSession.HasValue) + CdSessionText = Session.Prettify(decodedSession); + + if(decodedCdTextLeadIn.HasValue) + CdTextText = CDTextOnLeadIn.Prettify(decodedCdTextLeadIn); + + if(decodedAtip != null) + CdAtipText = ATIP.Prettify(atip); + + if(!string.IsNullOrEmpty(mcn)) + McnText = mcn; + + if(isrcs != null && + isrcs.Count > 0) { - _tocData = toc; - _atipData = atip; - _compactDiscInformationData = compactDiscInformation; - _sessionData = session; - _rawTocData = rawToc; - _pmaData = pma; - _cdTextLeadInData = cdTextLeadIn; - _view = view; - IsrcList = new ObservableCollection(); - SaveCdInformationCommand = ReactiveCommand.Create(ExecuteSaveCdInformationCommand); - SaveCdTocCommand = ReactiveCommand.Create(ExecuteSaveCdTocCommand); - SaveCdFullTocCommand = ReactiveCommand.Create(ExecuteSaveCdFullTocCommand); - SaveCdSessionCommand = ReactiveCommand.Create(ExecuteSaveCdSessionCommand); - SaveCdTextCommand = ReactiveCommand.Create(ExecuteSaveCdTextCommand); - SaveCdAtipCommand = ReactiveCommand.Create(ExecuteSaveCdAtipCommand); - SaveCdPmaCommand = ReactiveCommand.Create(ExecuteSaveCdPmaCommand); - - if(decodedCompactDiscInformation.HasValue) - CdInformationText = DiscInformation.Prettify000b(decodedCompactDiscInformation); - - if(decodedToc.HasValue) - CdTocText = TOC.Prettify(decodedToc); - - if(fullToc.HasValue) - CdFullTocText = FullTOC.Prettify(fullToc); - - if(decodedSession.HasValue) - CdSessionText = Session.Prettify(decodedSession); - - if(decodedCdTextLeadIn.HasValue) - CdTextText = CDTextOnLeadIn.Prettify(decodedCdTextLeadIn); - - if(decodedAtip != null) - CdAtipText = ATIP.Prettify(atip); - - if(!string.IsNullOrEmpty(mcn)) - McnText = mcn; - - if(isrcs != null && - isrcs.Count > 0) - { - foreach(KeyValuePair isrc in isrcs) - IsrcList.Add(new IsrcModel - { - Track = isrc.Key.ToString(), - Isrc = isrc.Value - }); - } - - MiscellaneousVisible = McnText != null || isrcs?.Count > 0 || pma != null; - CdPmaVisible = pma != null; - } - - public string CdInformationText { get; } - public string CdTocText { get; } - public string CdFullTocText { get; } - public string CdSessionText { get; } - public string CdTextText { get; } - public string CdAtipText { get; } - public bool MiscellaneousVisible { get; } - public string McnText { get; } - public bool CdPmaVisible { get; } - public ReactiveCommand SaveCdInformationCommand { get; } - public ReactiveCommand SaveCdTocCommand { get; } - public ReactiveCommand SaveCdFullTocCommand { get; } - public ReactiveCommand SaveCdSessionCommand { get; } - public ReactiveCommand SaveCdTextCommand { get; } - public ReactiveCommand SaveCdAtipCommand { get; } - public ReactiveCommand SaveCdPmaCommand { get; } - public ObservableCollection IsrcList { get; } - - async void ExecuteSaveCdInformationCommand() - { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] + foreach(KeyValuePair isrc in isrcs) + IsrcList.Add(new IsrcModel { - "*.bin" - }), - Name = "Binary" - }); - - string result = await dlgSaveBinary.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_compactDiscInformationData, 0, _compactDiscInformationData.Length); - - saveFs.Close(); + Track = isrc.Key.ToString(), + Isrc = isrc.Value + }); } - async void ExecuteSaveCdTocCommand() + MiscellaneousVisible = McnText != null || isrcs?.Count > 0 || pma != null; + CdPmaVisible = pma != null; + } + + public string CdInformationText { get; } + public string CdTocText { get; } + public string CdFullTocText { get; } + public string CdSessionText { get; } + public string CdTextText { get; } + public string CdAtipText { get; } + public bool MiscellaneousVisible { get; } + public string McnText { get; } + public bool CdPmaVisible { get; } + public ReactiveCommand SaveCdInformationCommand { get; } + public ReactiveCommand SaveCdTocCommand { get; } + public ReactiveCommand SaveCdFullTocCommand { get; } + public ReactiveCommand SaveCdSessionCommand { get; } + public ReactiveCommand SaveCdTextCommand { get; } + public ReactiveCommand SaveCdAtipCommand { get; } + public ReactiveCommand SaveCdPmaCommand { get; } + public ObservableCollection IsrcList { get; } + + async void ExecuteSaveCdInformationCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter + Extensions = new List(new[] { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + "*.bin" + }), + Name = "Binary" + }); - string result = await dlgSaveBinary.ShowAsync(_view); + string result = await dlgSaveBinary.ShowAsync(_view); - if(result is null) - return; + if(result is null) + return; - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_tocData, 0, _tocData.Length); + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_compactDiscInformationData, 0, _compactDiscInformationData.Length); - saveFs.Close(); - } + saveFs.Close(); + } - async void ExecuteSaveCdFullTocCommand() + async void ExecuteSaveCdTocCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter + Extensions = new List(new[] { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + "*.bin" + }), + Name = "Binary" + }); - string result = await dlgSaveBinary.ShowAsync(_view); + string result = await dlgSaveBinary.ShowAsync(_view); - if(result is null) - return; + if(result is null) + return; - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_rawTocData, 0, _rawTocData.Length); + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_tocData, 0, _tocData.Length); - saveFs.Close(); - } + saveFs.Close(); + } - async void ExecuteSaveCdSessionCommand() + async void ExecuteSaveCdFullTocCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter + Extensions = new List(new[] { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + "*.bin" + }), + Name = "Binary" + }); - string result = await dlgSaveBinary.ShowAsync(_view); + string result = await dlgSaveBinary.ShowAsync(_view); - if(result is null) - return; + if(result is null) + return; - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_sessionData, 0, _sessionData.Length); + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_rawTocData, 0, _rawTocData.Length); - saveFs.Close(); - } + saveFs.Close(); + } - async void ExecuteSaveCdTextCommand() + async void ExecuteSaveCdSessionCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter + Extensions = new List(new[] { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + "*.bin" + }), + Name = "Binary" + }); - string result = await dlgSaveBinary.ShowAsync(_view); + string result = await dlgSaveBinary.ShowAsync(_view); - if(result is null) - return; + if(result is null) + return; - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_cdTextLeadInData, 0, _cdTextLeadInData.Length); + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_sessionData, 0, _sessionData.Length); - saveFs.Close(); - } + saveFs.Close(); + } - async void ExecuteSaveCdAtipCommand() + async void ExecuteSaveCdTextCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter + Extensions = new List(new[] { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + "*.bin" + }), + Name = "Binary" + }); - string result = await dlgSaveBinary.ShowAsync(_view); + string result = await dlgSaveBinary.ShowAsync(_view); - if(result is null) - return; + if(result is null) + return; - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_atipData, 0, _atipData.Length); + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_cdTextLeadInData, 0, _cdTextLeadInData.Length); - saveFs.Close(); - } + saveFs.Close(); + } - async void ExecuteSaveCdPmaCommand() + async void ExecuteSaveCdAtipCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter + Extensions = new List(new[] { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + "*.bin" + }), + Name = "Binary" + }); - string result = await dlgSaveBinary.ShowAsync(_view); + string result = await dlgSaveBinary.ShowAsync(_view); - if(result is null) - return; + if(result is null) + return; - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_pmaData, 0, _pmaData.Length); + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_atipData, 0, _atipData.Length); - saveFs.Close(); - } + saveFs.Close(); + } + + async void ExecuteSaveCdPmaCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_pmaData, 0, _pmaData.Length); + + saveFs.Close(); } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/DvdInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/DvdInfoViewModel.cs index 20711119e..9b0fc097d 100644 --- a/Aaru.Gui/ViewModels/Tabs/DvdInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/DvdInfoViewModel.cs @@ -39,119 +39,118 @@ using Avalonia.Controls; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public sealed class DvdInfoViewModel { - public sealed class DvdInfoViewModel + readonly byte[] _dvdAacs; + readonly byte[] _dvdBca; + readonly byte[] _dvdCmi; + readonly byte[] _dvdDmi; + readonly byte[] _dvdPfi; + readonly byte[] _hddvdCopyrightInformation; + readonly Window _view; + + public DvdInfoViewModel(MediaType mediaType, [CanBeNull] byte[] pfi, [CanBeNull] byte[] dmi, + [CanBeNull] byte[] cmi, [CanBeNull] byte[] hdCopyrightInformation, + [CanBeNull] byte[] bca, [CanBeNull] byte[] aacs, + PFI.PhysicalFormatInformation? decodedPfi, Window view) { - readonly byte[] _dvdAacs; - readonly byte[] _dvdBca; - readonly byte[] _dvdCmi; - readonly byte[] _dvdDmi; - readonly byte[] _dvdPfi; - readonly byte[] _hddvdCopyrightInformation; - readonly Window _view; + _dvdPfi = pfi; + _dvdDmi = dmi; + _dvdCmi = cmi; + _hddvdCopyrightInformation = hdCopyrightInformation; + _dvdBca = bca; + _dvdAacs = aacs; + _view = view; + SaveDvdPfiCommand = ReactiveCommand.Create(ExecuteSaveDvdPfiCommand); + SaveDvdDmiCommand = ReactiveCommand.Create(ExecuteSaveDvdDmiCommand); + SaveDvdCmiCommand = ReactiveCommand.Create(ExecuteSaveDvdCmiCommand); + SaveHdDvdCmiCommand = ReactiveCommand.Create(ExecuteSaveHdDvdCmiCommand); + SaveDvdBcaCommand = ReactiveCommand.Create(ExecuteSaveDvdBcaCommand); + SaveDvdAacsCommand = ReactiveCommand.Create(ExecuteSaveDvdAacsCommand); - public DvdInfoViewModel(MediaType mediaType, [CanBeNull] byte[] pfi, [CanBeNull] byte[] dmi, - [CanBeNull] byte[] cmi, [CanBeNull] byte[] hdCopyrightInformation, - [CanBeNull] byte[] bca, [CanBeNull] byte[] aacs, - PFI.PhysicalFormatInformation? decodedPfi, Window view) + /* TODO: Pass back + switch(mediaType) { - _dvdPfi = pfi; - _dvdDmi = dmi; - _dvdCmi = cmi; - _hddvdCopyrightInformation = hdCopyrightInformation; - _dvdBca = bca; - _dvdAacs = aacs; - _view = view; - SaveDvdPfiCommand = ReactiveCommand.Create(ExecuteSaveDvdPfiCommand); - SaveDvdDmiCommand = ReactiveCommand.Create(ExecuteSaveDvdDmiCommand); - SaveDvdCmiCommand = ReactiveCommand.Create(ExecuteSaveDvdCmiCommand); - SaveHdDvdCmiCommand = ReactiveCommand.Create(ExecuteSaveHdDvdCmiCommand); - SaveDvdBcaCommand = ReactiveCommand.Create(ExecuteSaveDvdBcaCommand); - SaveDvdAacsCommand = ReactiveCommand.Create(ExecuteSaveDvdAacsCommand); + case MediaType.HDDVDROM: + case MediaType.HDDVDRAM: + case MediaType.HDDVDR: + case MediaType.HDDVDRW: + case MediaType.HDDVDRDL: + case MediaType.HDDVDRWDL: + Text = "HD DVD"; - /* TODO: Pass back - switch(mediaType) - { - case MediaType.HDDVDROM: - case MediaType.HDDVDRAM: - case MediaType.HDDVDR: - case MediaType.HDDVDRW: - case MediaType.HDDVDRDL: - case MediaType.HDDVDRWDL: - Text = "HD DVD"; + break; + default: + Text = "DVD"; - break; - default: - Text = "DVD"; - - break; - } - */ - - if(decodedPfi.HasValue) - DvdPfiText = PFI.Prettify(decodedPfi); - - if(cmi != null) - DvdCmiText = CSS_CPRM.PrettifyLeadInCopyright(cmi); - - SaveDvdPfiVisible = pfi != null; - SaveDvdDmiVisible = dmi != null; - SaveDvdCmiVisible = cmi != null; - SaveHdDvdCmiVisible = hdCopyrightInformation != null; - SaveDvdBcaVisible = bca != null; - SaveDvdAacsVisible = aacs != null; + break; } + */ - public ReactiveCommand SaveDvdPfiCommand { get; } - public ReactiveCommand SaveDvdDmiCommand { get; } - public ReactiveCommand SaveDvdCmiCommand { get; } - public ReactiveCommand SaveHdDvdCmiCommand { get; } - public ReactiveCommand SaveDvdBcaCommand { get; } - public ReactiveCommand SaveDvdAacsCommand { get; } - public string DvdPfiText { get; } - public string DvdCmiText { get; } - public bool SaveDvdPfiVisible { get; } - public bool SaveDvdDmiVisible { get; } - public bool SaveDvdCmiVisible { get; } - public bool SaveHdDvdCmiVisible { get; } - public bool SaveDvdBcaVisible { get; } - public bool SaveDvdAacsVisible { get; } + if(decodedPfi.HasValue) + DvdPfiText = PFI.Prettify(decodedPfi); - async void SaveElement(byte[] data) - { - var dlgSaveBinary = new SaveFileDialog(); + if(cmi != null) + DvdCmiText = CSS_CPRM.PrettifyLeadInCopyright(cmi); - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); - - string result = await dlgSaveBinary.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(data, 0, data.Length); - - saveFs.Close(); - } - - void ExecuteSaveDvdPfiCommand() => SaveElement(_dvdPfi); - - void ExecuteSaveDvdDmiCommand() => SaveElement(_dvdDmi); - - void ExecuteSaveDvdCmiCommand() => SaveElement(_dvdCmi); - - void ExecuteSaveHdDvdCmiCommand() => SaveElement(_hddvdCopyrightInformation); - - void ExecuteSaveDvdBcaCommand() => SaveElement(_dvdBca); - - void ExecuteSaveDvdAacsCommand() => SaveElement(_dvdAacs); + SaveDvdPfiVisible = pfi != null; + SaveDvdDmiVisible = dmi != null; + SaveDvdCmiVisible = cmi != null; + SaveHdDvdCmiVisible = hdCopyrightInformation != null; + SaveDvdBcaVisible = bca != null; + SaveDvdAacsVisible = aacs != null; } + + public ReactiveCommand SaveDvdPfiCommand { get; } + public ReactiveCommand SaveDvdDmiCommand { get; } + public ReactiveCommand SaveDvdCmiCommand { get; } + public ReactiveCommand SaveHdDvdCmiCommand { get; } + public ReactiveCommand SaveDvdBcaCommand { get; } + public ReactiveCommand SaveDvdAacsCommand { get; } + public string DvdPfiText { get; } + public string DvdCmiText { get; } + public bool SaveDvdPfiVisible { get; } + public bool SaveDvdDmiVisible { get; } + public bool SaveDvdCmiVisible { get; } + public bool SaveHdDvdCmiVisible { get; } + public bool SaveDvdBcaVisible { get; } + public bool SaveDvdAacsVisible { get; } + + async void SaveElement(byte[] data) + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(data, 0, data.Length); + + saveFs.Close(); + } + + void ExecuteSaveDvdPfiCommand() => SaveElement(_dvdPfi); + + void ExecuteSaveDvdDmiCommand() => SaveElement(_dvdDmi); + + void ExecuteSaveDvdCmiCommand() => SaveElement(_dvdCmi); + + void ExecuteSaveHdDvdCmiCommand() => SaveElement(_hddvdCopyrightInformation); + + void ExecuteSaveDvdBcaCommand() => SaveElement(_dvdBca); + + void ExecuteSaveDvdAacsCommand() => SaveElement(_dvdAacs); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/DvdWritableInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/DvdWritableInfoViewModel.cs index 1814b62cc..ae63db73d 100644 --- a/Aaru.Gui/ViewModels/Tabs/DvdWritableInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/DvdWritableInfoViewModel.cs @@ -38,255 +38,254 @@ using Aaru.Decoders.DVD; using Avalonia.Controls; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public sealed class DvdWritableInfoViewModel { - public sealed class DvdWritableInfoViewModel + readonly byte[] _dvdLastBorderOutRmd; + readonly byte[] _dvdPlusAdip; + readonly byte[] _dvdPlusDcb; + readonly byte[] _dvdPreRecordedInfo; + readonly byte[] _dvdRamCartridgeStatus; + readonly byte[] _dvdRamDds; + readonly byte[] _dvdRamSpareArea; + readonly byte[] _dvdrDlJumpIntervalSize; + readonly byte[] _dvdrDlManualLayerJumpStartLba; + readonly byte[] _dvdrDlMiddleZoneStart; + readonly byte[] _dvdrDlRemapAnchorPoint; + readonly byte[] _dvdrLayerCapacity; + readonly byte[] _dvdrMediaIdentifier; + readonly byte[] _dvdrPhysicalInformation; + readonly byte[] _hddvdrLastRmd; + readonly byte[] _hddvdrMediumStatus; + readonly Window _view; + + public DvdWritableInfoViewModel(MediaType mediaType, byte[] dds, byte[] cartridgeStatus, byte[] spareArea, + byte[] lastBorderOutRmd, byte[] preRecordedInfo, byte[] mediaIdentifier, + byte[] physicalInformation, byte[] mediumStatus, byte[] hdLastRmd, + byte[] layerCapacity, byte[] middleZoneStart, byte[] jumpIntervalSize, + byte[] manualLayerJumpStartLba, byte[] remapAnchorPoint, byte[] adip, + byte[] dcb, Window view) { - readonly byte[] _dvdLastBorderOutRmd; - readonly byte[] _dvdPlusAdip; - readonly byte[] _dvdPlusDcb; - readonly byte[] _dvdPreRecordedInfo; - readonly byte[] _dvdRamCartridgeStatus; - readonly byte[] _dvdRamDds; - readonly byte[] _dvdRamSpareArea; - readonly byte[] _dvdrDlJumpIntervalSize; - readonly byte[] _dvdrDlManualLayerJumpStartLba; - readonly byte[] _dvdrDlMiddleZoneStart; - readonly byte[] _dvdrDlRemapAnchorPoint; - readonly byte[] _dvdrLayerCapacity; - readonly byte[] _dvdrMediaIdentifier; - readonly byte[] _dvdrPhysicalInformation; - readonly byte[] _hddvdrLastRmd; - readonly byte[] _hddvdrMediumStatus; - readonly Window _view; + _view = view; + SaveDvdRamDdsCommand = ReactiveCommand.Create(ExecuteSaveDvdRamDdsCommand); + SaveDvdRamCartridgeStatusCommand = ReactiveCommand.Create(ExecuteSaveDvdRamCartridgeStatusCommand); - public DvdWritableInfoViewModel(MediaType mediaType, byte[] dds, byte[] cartridgeStatus, byte[] spareArea, - byte[] lastBorderOutRmd, byte[] preRecordedInfo, byte[] mediaIdentifier, - byte[] physicalInformation, byte[] mediumStatus, byte[] hdLastRmd, - byte[] layerCapacity, byte[] middleZoneStart, byte[] jumpIntervalSize, - byte[] manualLayerJumpStartLba, byte[] remapAnchorPoint, byte[] adip, - byte[] dcb, Window view) + SaveDvdRamSpareAreaInformationCommand = + ReactiveCommand.Create(ExecuteSaveDvdRamSpareAreaInformationCommand); + + SaveLastBorderOutRmdCommand = ReactiveCommand.Create(ExecuteSaveLastBorderOutRmdCommand); + SaveDvdPreRecordedInfoCommand = ReactiveCommand.Create(ExecuteSaveDvdPreRecordedInfoCommand); + SaveDvdrMediaIdentifierCommand = ReactiveCommand.Create(ExecuteSaveDvdrMediaIdentifierCommand); + SaveDvdrPhysicalInformationCommand = ReactiveCommand.Create(ExecuteSaveDvdrPhysicalInformationCommand); + SaveHddvdrMediumStatusCommand = ReactiveCommand.Create(ExecuteSaveHddvdrMediumStatusCommand); + SaveHddvdrLastRmdCommand = ReactiveCommand.Create(ExecuteSaveHddvdrLastRmdCommand); + SaveDvdrLayerCapacityCommand = ReactiveCommand.Create(ExecuteSaveDvdrLayerCapacityCommand); + SaveDvdrDlMiddleZoneStartCommand = ReactiveCommand.Create(ExecuteSaveDvdrDlMiddleZoneStartCommand); + SaveDvdrDlJumpIntervalSizeCommand = ReactiveCommand.Create(ExecuteSaveDvdrDlJumpIntervalSizeCommand); + + SaveDvdrDlManualLayerJumpStartLbaCommand = + ReactiveCommand.Create(ExecuteSaveDvdrDlManualLayerJumpStartLbaCommand); + + SaveDvdrDlRemapAnchorPointCommand = ReactiveCommand.Create(ExecuteSaveDvdrDlRemapAnchorPointCommand); + SaveDvdPlusAdipCommand = ReactiveCommand.Create(ExecuteSaveDvdPlusAdipCommand); + SaveDvdPlusDcbCommand = ReactiveCommand.Create(ExecuteSaveDvdPlusDcbCommand); + + _dvdRamDds = dds; + _dvdRamCartridgeStatus = cartridgeStatus; + _dvdRamSpareArea = spareArea; + _dvdLastBorderOutRmd = lastBorderOutRmd; + _dvdPreRecordedInfo = preRecordedInfo; + _dvdrMediaIdentifier = mediaIdentifier; + _dvdrPhysicalInformation = physicalInformation; + _hddvdrMediumStatus = mediumStatus; + _hddvdrLastRmd = hdLastRmd; + _dvdrLayerCapacity = layerCapacity; + _dvdrDlMiddleZoneStart = middleZoneStart; + _dvdrDlJumpIntervalSize = jumpIntervalSize; + _dvdrDlManualLayerJumpStartLba = manualLayerJumpStartLba; + _dvdrDlRemapAnchorPoint = remapAnchorPoint; + _dvdPlusAdip = adip; + _dvdPlusDcb = dcb; + + /* TODO: Pass back + switch(mediaType) { - _view = view; - SaveDvdRamDdsCommand = ReactiveCommand.Create(ExecuteSaveDvdRamDdsCommand); - SaveDvdRamCartridgeStatusCommand = ReactiveCommand.Create(ExecuteSaveDvdRamCartridgeStatusCommand); + case MediaType.DVDR: + Text = "DVD-R"; - SaveDvdRamSpareAreaInformationCommand = - ReactiveCommand.Create(ExecuteSaveDvdRamSpareAreaInformationCommand); + break; + case MediaType.DVDRW: + Text = "DVD-RW"; - SaveLastBorderOutRmdCommand = ReactiveCommand.Create(ExecuteSaveLastBorderOutRmdCommand); - SaveDvdPreRecordedInfoCommand = ReactiveCommand.Create(ExecuteSaveDvdPreRecordedInfoCommand); - SaveDvdrMediaIdentifierCommand = ReactiveCommand.Create(ExecuteSaveDvdrMediaIdentifierCommand); - SaveDvdrPhysicalInformationCommand = ReactiveCommand.Create(ExecuteSaveDvdrPhysicalInformationCommand); - SaveHddvdrMediumStatusCommand = ReactiveCommand.Create(ExecuteSaveHddvdrMediumStatusCommand); - SaveHddvdrLastRmdCommand = ReactiveCommand.Create(ExecuteSaveHddvdrLastRmdCommand); - SaveDvdrLayerCapacityCommand = ReactiveCommand.Create(ExecuteSaveDvdrLayerCapacityCommand); - SaveDvdrDlMiddleZoneStartCommand = ReactiveCommand.Create(ExecuteSaveDvdrDlMiddleZoneStartCommand); - SaveDvdrDlJumpIntervalSizeCommand = ReactiveCommand.Create(ExecuteSaveDvdrDlJumpIntervalSizeCommand); + break; + case MediaType.DVDPR: + Text = "DVD+R"; - SaveDvdrDlManualLayerJumpStartLbaCommand = - ReactiveCommand.Create(ExecuteSaveDvdrDlManualLayerJumpStartLbaCommand); + break; + case MediaType.DVDPRW: + Text = "DVD+RW"; - SaveDvdrDlRemapAnchorPointCommand = ReactiveCommand.Create(ExecuteSaveDvdrDlRemapAnchorPointCommand); - SaveDvdPlusAdipCommand = ReactiveCommand.Create(ExecuteSaveDvdPlusAdipCommand); - SaveDvdPlusDcbCommand = ReactiveCommand.Create(ExecuteSaveDvdPlusDcbCommand); + break; + case MediaType.DVDPRWDL: + Text = "DVD+RW DL"; - _dvdRamDds = dds; - _dvdRamCartridgeStatus = cartridgeStatus; - _dvdRamSpareArea = spareArea; - _dvdLastBorderOutRmd = lastBorderOutRmd; - _dvdPreRecordedInfo = preRecordedInfo; - _dvdrMediaIdentifier = mediaIdentifier; - _dvdrPhysicalInformation = physicalInformation; - _hddvdrMediumStatus = mediumStatus; - _hddvdrLastRmd = hdLastRmd; - _dvdrLayerCapacity = layerCapacity; - _dvdrDlMiddleZoneStart = middleZoneStart; - _dvdrDlJumpIntervalSize = jumpIntervalSize; - _dvdrDlManualLayerJumpStartLba = manualLayerJumpStartLba; - _dvdrDlRemapAnchorPoint = remapAnchorPoint; - _dvdPlusAdip = adip; - _dvdPlusDcb = dcb; + break; + case MediaType.DVDRDL: + Text = "DVD-R DL"; - /* TODO: Pass back - switch(mediaType) - { - case MediaType.DVDR: - Text = "DVD-R"; + break; + case MediaType.DVDPRDL: + Text = "DVD+R DL"; - break; - case MediaType.DVDRW: - Text = "DVD-RW"; + break; + case MediaType.DVDRAM: + Text = "DVD-RAM"; - break; - case MediaType.DVDPR: - Text = "DVD+R"; + break; + case MediaType.DVDRWDL: + Text = "DVD-RW DL"; - break; - case MediaType.DVDPRW: - Text = "DVD+RW"; + break; + case MediaType.HDDVDRAM: + Text = "HD DVD-RAM"; - break; - case MediaType.DVDPRWDL: - Text = "DVD+RW DL"; + break; + case MediaType.HDDVDR: + Text = "HD DVD-R"; - break; - case MediaType.DVDRDL: - Text = "DVD-R DL"; + break; + case MediaType.HDDVDRW: + Text = "HD DVD-RW"; - break; - case MediaType.DVDPRDL: - Text = "DVD+R DL"; + break; + case MediaType.HDDVDRDL: + Text = "HD DVD-R DL"; - break; - case MediaType.DVDRAM: - Text = "DVD-RAM"; + break; + case MediaType.HDDVDRWDL: + Text = "HD DVD-RW DL"; - break; - case MediaType.DVDRWDL: - Text = "DVD-RW DL"; - - break; - case MediaType.HDDVDRAM: - Text = "HD DVD-RAM"; - - break; - case MediaType.HDDVDR: - Text = "HD DVD-R"; - - break; - case MediaType.HDDVDRW: - Text = "HD DVD-RW"; - - break; - case MediaType.HDDVDRDL: - Text = "HD DVD-R DL"; - - break; - case MediaType.HDDVDRWDL: - Text = "HD DVD-RW DL"; - - break; - } - */ - - if(dds != null) - DvdRamDdsText = DDS.Prettify(dds); - - if(cartridgeStatus != null) - DvdRamCartridgeStatusText = Cartridge.Prettify(cartridgeStatus); - - if(spareArea != null) - DvdRamSpareAreaInformationText = Spare.Prettify(spareArea); - - SaveDvdRamDdsVisible = dds != null; - SaveDvdRamCartridgeStatusVisible = cartridgeStatus != null; - SaveDvdRamSpareAreaInformationVisible = spareArea != null; - SaveLastBorderOutRmdVisible = lastBorderOutRmd != null; - SaveDvdPreRecordedInfoVisible = preRecordedInfo != null; - SaveDvdrMediaIdentifierVisible = mediaIdentifier != null; - SaveDvdrPhysicalInformationVisible = physicalInformation != null; - SaveHddvdrMediumStatusVisible = mediumStatus != null; - SaveHddvdrLastRmdVisible = hdLastRmd != null; - SaveDvdrLayerCapacityVisible = layerCapacity != null; - SaveDvdrDlMiddleZoneStartVisible = middleZoneStart != null; - SaveDvdrDlJumpIntervalSizeVisible = jumpIntervalSize != null; - SaveDvdrDlManualLayerJumpStartLbaVisible = manualLayerJumpStartLba != null; - SaveDvdrDlRemapAnchorPointVisible = remapAnchorPoint != null; - SaveDvdPlusAdipVisible = adip != null; - SaveDvdPlusDcbVisible = dcb != null; + break; } + */ - public string DvdRamDdsText { get; } - public string DvdRamCartridgeStatusText { get; } - public string DvdRamSpareAreaInformationText { get; } - public bool SaveDvdRamDdsVisible { get; } - public bool SaveDvdRamCartridgeStatusVisible { get; } - public bool SaveDvdRamSpareAreaInformationVisible { get; } - public bool SaveLastBorderOutRmdVisible { get; } - public bool SaveDvdPreRecordedInfoVisible { get; } - public bool SaveDvdrMediaIdentifierVisible { get; } - public bool SaveDvdrPhysicalInformationVisible { get; } - public bool SaveHddvdrMediumStatusVisible { get; } - public bool SaveHddvdrLastRmdVisible { get; } - public bool SaveDvdrLayerCapacityVisible { get; } - public bool SaveDvdrDlMiddleZoneStartVisible { get; } - public bool SaveDvdrDlJumpIntervalSizeVisible { get; } - public bool SaveDvdrDlManualLayerJumpStartLbaVisible { get; } - public bool SaveDvdrDlRemapAnchorPointVisible { get; } - public bool SaveDvdPlusAdipVisible { get; } - public bool SaveDvdPlusDcbVisible { get; } - public ReactiveCommand SaveDvdRamDdsCommand { get; } - public ReactiveCommand SaveDvdRamCartridgeStatusCommand { get; } - public ReactiveCommand SaveDvdRamSpareAreaInformationCommand { get; } - public ReactiveCommand SaveLastBorderOutRmdCommand { get; } - public ReactiveCommand SaveDvdPreRecordedInfoCommand { get; } - public ReactiveCommand SaveDvdrMediaIdentifierCommand { get; } - public ReactiveCommand SaveDvdrPhysicalInformationCommand { get; } - public ReactiveCommand SaveHddvdrMediumStatusCommand { get; } - public ReactiveCommand SaveHddvdrLastRmdCommand { get; } - public ReactiveCommand SaveDvdrLayerCapacityCommand { get; } - public ReactiveCommand SaveDvdrDlMiddleZoneStartCommand { get; } - public ReactiveCommand SaveDvdrDlJumpIntervalSizeCommand { get; } - public ReactiveCommand SaveDvdrDlManualLayerJumpStartLbaCommand { get; } - public ReactiveCommand SaveDvdrDlRemapAnchorPointCommand { get; } - public ReactiveCommand SaveDvdPlusAdipCommand { get; } - public ReactiveCommand SaveDvdPlusDcbCommand { get; } + if(dds != null) + DvdRamDdsText = DDS.Prettify(dds); - async void SaveElement(byte[] data) - { - var dlgSaveBinary = new SaveFileDialog(); + if(cartridgeStatus != null) + DvdRamCartridgeStatusText = Cartridge.Prettify(cartridgeStatus); - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + if(spareArea != null) + DvdRamSpareAreaInformationText = Spare.Prettify(spareArea); - string result = await dlgSaveBinary.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(data, 0, data.Length); - - saveFs.Close(); - } - - void ExecuteSaveDvdRamDdsCommand() => SaveElement(_dvdRamDds); - - void ExecuteSaveDvdRamCartridgeStatusCommand() => SaveElement(_dvdRamCartridgeStatus); - - void ExecuteSaveDvdRamSpareAreaInformationCommand() => SaveElement(_dvdRamSpareArea); - - void ExecuteSaveLastBorderOutRmdCommand() => SaveElement(_dvdLastBorderOutRmd); - - void ExecuteSaveDvdPreRecordedInfoCommand() => SaveElement(_dvdPreRecordedInfo); - - void ExecuteSaveDvdrMediaIdentifierCommand() => SaveElement(_dvdrMediaIdentifier); - - void ExecuteSaveDvdrPhysicalInformationCommand() => SaveElement(_dvdrPhysicalInformation); - - void ExecuteSaveHddvdrMediumStatusCommand() => SaveElement(_hddvdrMediumStatus); - - void ExecuteSaveHddvdrLastRmdCommand() => SaveElement(_hddvdrLastRmd); - - void ExecuteSaveDvdrLayerCapacityCommand() => SaveElement(_dvdrLayerCapacity); - - void ExecuteSaveDvdrDlMiddleZoneStartCommand() => SaveElement(_dvdrDlMiddleZoneStart); - - void ExecuteSaveDvdrDlJumpIntervalSizeCommand() => SaveElement(_dvdrDlJumpIntervalSize); - - void ExecuteSaveDvdrDlManualLayerJumpStartLbaCommand() => SaveElement(_dvdrDlManualLayerJumpStartLba); - - void ExecuteSaveDvdrDlRemapAnchorPointCommand() => SaveElement(_dvdrDlRemapAnchorPoint); - - void ExecuteSaveDvdPlusAdipCommand() => SaveElement(_dvdPlusAdip); - - void ExecuteSaveDvdPlusDcbCommand() => SaveElement(_dvdPlusDcb); + SaveDvdRamDdsVisible = dds != null; + SaveDvdRamCartridgeStatusVisible = cartridgeStatus != null; + SaveDvdRamSpareAreaInformationVisible = spareArea != null; + SaveLastBorderOutRmdVisible = lastBorderOutRmd != null; + SaveDvdPreRecordedInfoVisible = preRecordedInfo != null; + SaveDvdrMediaIdentifierVisible = mediaIdentifier != null; + SaveDvdrPhysicalInformationVisible = physicalInformation != null; + SaveHddvdrMediumStatusVisible = mediumStatus != null; + SaveHddvdrLastRmdVisible = hdLastRmd != null; + SaveDvdrLayerCapacityVisible = layerCapacity != null; + SaveDvdrDlMiddleZoneStartVisible = middleZoneStart != null; + SaveDvdrDlJumpIntervalSizeVisible = jumpIntervalSize != null; + SaveDvdrDlManualLayerJumpStartLbaVisible = manualLayerJumpStartLba != null; + SaveDvdrDlRemapAnchorPointVisible = remapAnchorPoint != null; + SaveDvdPlusAdipVisible = adip != null; + SaveDvdPlusDcbVisible = dcb != null; } + + public string DvdRamDdsText { get; } + public string DvdRamCartridgeStatusText { get; } + public string DvdRamSpareAreaInformationText { get; } + public bool SaveDvdRamDdsVisible { get; } + public bool SaveDvdRamCartridgeStatusVisible { get; } + public bool SaveDvdRamSpareAreaInformationVisible { get; } + public bool SaveLastBorderOutRmdVisible { get; } + public bool SaveDvdPreRecordedInfoVisible { get; } + public bool SaveDvdrMediaIdentifierVisible { get; } + public bool SaveDvdrPhysicalInformationVisible { get; } + public bool SaveHddvdrMediumStatusVisible { get; } + public bool SaveHddvdrLastRmdVisible { get; } + public bool SaveDvdrLayerCapacityVisible { get; } + public bool SaveDvdrDlMiddleZoneStartVisible { get; } + public bool SaveDvdrDlJumpIntervalSizeVisible { get; } + public bool SaveDvdrDlManualLayerJumpStartLbaVisible { get; } + public bool SaveDvdrDlRemapAnchorPointVisible { get; } + public bool SaveDvdPlusAdipVisible { get; } + public bool SaveDvdPlusDcbVisible { get; } + public ReactiveCommand SaveDvdRamDdsCommand { get; } + public ReactiveCommand SaveDvdRamCartridgeStatusCommand { get; } + public ReactiveCommand SaveDvdRamSpareAreaInformationCommand { get; } + public ReactiveCommand SaveLastBorderOutRmdCommand { get; } + public ReactiveCommand SaveDvdPreRecordedInfoCommand { get; } + public ReactiveCommand SaveDvdrMediaIdentifierCommand { get; } + public ReactiveCommand SaveDvdrPhysicalInformationCommand { get; } + public ReactiveCommand SaveHddvdrMediumStatusCommand { get; } + public ReactiveCommand SaveHddvdrLastRmdCommand { get; } + public ReactiveCommand SaveDvdrLayerCapacityCommand { get; } + public ReactiveCommand SaveDvdrDlMiddleZoneStartCommand { get; } + public ReactiveCommand SaveDvdrDlJumpIntervalSizeCommand { get; } + public ReactiveCommand SaveDvdrDlManualLayerJumpStartLbaCommand { get; } + public ReactiveCommand SaveDvdrDlRemapAnchorPointCommand { get; } + public ReactiveCommand SaveDvdPlusAdipCommand { get; } + public ReactiveCommand SaveDvdPlusDcbCommand { get; } + + async void SaveElement(byte[] data) + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(data, 0, data.Length); + + saveFs.Close(); + } + + void ExecuteSaveDvdRamDdsCommand() => SaveElement(_dvdRamDds); + + void ExecuteSaveDvdRamCartridgeStatusCommand() => SaveElement(_dvdRamCartridgeStatus); + + void ExecuteSaveDvdRamSpareAreaInformationCommand() => SaveElement(_dvdRamSpareArea); + + void ExecuteSaveLastBorderOutRmdCommand() => SaveElement(_dvdLastBorderOutRmd); + + void ExecuteSaveDvdPreRecordedInfoCommand() => SaveElement(_dvdPreRecordedInfo); + + void ExecuteSaveDvdrMediaIdentifierCommand() => SaveElement(_dvdrMediaIdentifier); + + void ExecuteSaveDvdrPhysicalInformationCommand() => SaveElement(_dvdrPhysicalInformation); + + void ExecuteSaveHddvdrMediumStatusCommand() => SaveElement(_hddvdrMediumStatus); + + void ExecuteSaveHddvdrLastRmdCommand() => SaveElement(_hddvdrLastRmd); + + void ExecuteSaveDvdrLayerCapacityCommand() => SaveElement(_dvdrLayerCapacity); + + void ExecuteSaveDvdrDlMiddleZoneStartCommand() => SaveElement(_dvdrDlMiddleZoneStart); + + void ExecuteSaveDvdrDlJumpIntervalSizeCommand() => SaveElement(_dvdrDlJumpIntervalSize); + + void ExecuteSaveDvdrDlManualLayerJumpStartLbaCommand() => SaveElement(_dvdrDlManualLayerJumpStartLba); + + void ExecuteSaveDvdrDlRemapAnchorPointCommand() => SaveElement(_dvdrDlRemapAnchorPoint); + + void ExecuteSaveDvdPlusAdipCommand() => SaveElement(_dvdPlusAdip); + + void ExecuteSaveDvdPlusDcbCommand() => SaveElement(_dvdPlusDcb); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/PcmciaInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/PcmciaInfoViewModel.cs index 78cb6c946..618b98e9d 100644 --- a/Aaru.Gui/ViewModels/Tabs/PcmciaInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/PcmciaInfoViewModel.cs @@ -41,154 +41,153 @@ using Avalonia.Controls; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public class PcmciaInfoViewModel : ViewModelBase { - public class PcmciaInfoViewModel : ViewModelBase + readonly byte[] _cis; + readonly Window _view; + string _pcmciaCisText; + PcmciaCisModel _selectedCis; + + internal PcmciaInfoViewModel([CanBeNull] byte[] pcmciaCis, Window view) { - readonly byte[] _cis; - readonly Window _view; - string _pcmciaCisText; - PcmciaCisModel _selectedCis; + if(pcmciaCis == null) + return; - internal PcmciaInfoViewModel([CanBeNull] byte[] pcmciaCis, Window view) - { - if(pcmciaCis == null) - return; + _cis = pcmciaCis; + CisList = new ObservableCollection(); + SavePcmciaCisCommand = ReactiveCommand.Create(ExecuteSavePcmciaCisCommand); - _cis = pcmciaCis; - CisList = new ObservableCollection(); - SavePcmciaCisCommand = ReactiveCommand.Create(ExecuteSavePcmciaCisCommand); + _view = view; - _view = view; + Tuple[] tuples = CIS.GetTuples(_cis); - Tuple[] tuples = CIS.GetTuples(_cis); + if(tuples != null) + foreach(Tuple tuple in tuples) + { + string tupleCode; + string tupleDescription; - if(tuples != null) - foreach(Tuple tuple in tuples) + switch(tuple.Code) { - string tupleCode; - string tupleDescription; + case TupleCodes.CISTPL_NULL: + case TupleCodes.CISTPL_END: continue; + case TupleCodes.CISTPL_DEVICEGEO: + case TupleCodes.CISTPL_DEVICEGEO_A: + tupleCode = "Device Geometry Tuples"; + tupleDescription = CIS.PrettifyDeviceGeometryTuple(tuple); - switch(tuple.Code) - { - case TupleCodes.CISTPL_NULL: - case TupleCodes.CISTPL_END: continue; - case TupleCodes.CISTPL_DEVICEGEO: - case TupleCodes.CISTPL_DEVICEGEO_A: - tupleCode = "Device Geometry Tuples"; - tupleDescription = CIS.PrettifyDeviceGeometryTuple(tuple); + break; + case TupleCodes.CISTPL_MANFID: + tupleCode = "Manufacturer Identification Tuple"; + tupleDescription = CIS.PrettifyManufacturerIdentificationTuple(tuple); - break; - case TupleCodes.CISTPL_MANFID: - tupleCode = "Manufacturer Identification Tuple"; - tupleDescription = CIS.PrettifyManufacturerIdentificationTuple(tuple); + break; + case TupleCodes.CISTPL_VERS_1: + tupleCode = "Level 1 Version / Product Information Tuple"; + tupleDescription = CIS.PrettifyLevel1VersionTuple(tuple); - break; - case TupleCodes.CISTPL_VERS_1: - tupleCode = "Level 1 Version / Product Information Tuple"; - tupleDescription = CIS.PrettifyLevel1VersionTuple(tuple); + break; + case TupleCodes.CISTPL_ALTSTR: + case TupleCodes.CISTPL_BAR: + case TupleCodes.CISTPL_BATTERY: + case TupleCodes.CISTPL_BYTEORDER: + case TupleCodes.CISTPL_CFTABLE_ENTRY: + case TupleCodes.CISTPL_CFTABLE_ENTRY_CB: + case TupleCodes.CISTPL_CHECKSUM: + case TupleCodes.CISTPL_CONFIG: + case TupleCodes.CISTPL_CONFIG_CB: + case TupleCodes.CISTPL_DATE: + case TupleCodes.CISTPL_DEVICE: + case TupleCodes.CISTPL_DEVICE_A: + case TupleCodes.CISTPL_DEVICE_OA: + case TupleCodes.CISTPL_DEVICE_OC: + case TupleCodes.CISTPL_EXTDEVIC: + case TupleCodes.CISTPL_FORMAT: + case TupleCodes.CISTPL_FORMAT_A: + case TupleCodes.CISTPL_FUNCE: + case TupleCodes.CISTPL_FUNCID: + case TupleCodes.CISTPL_GEOMETRY: + case TupleCodes.CISTPL_INDIRECT: + case TupleCodes.CISTPL_JEDEC_A: + case TupleCodes.CISTPL_JEDEC_C: + case TupleCodes.CISTPL_LINKTARGET: + case TupleCodes.CISTPL_LONGLINK_A: + case TupleCodes.CISTPL_LONGLINK_C: + case TupleCodes.CISTPL_LONGLINK_CB: + case TupleCodes.CISTPL_LONGLINK_MFC: + case TupleCodes.CISTPL_NO_LINK: + case TupleCodes.CISTPL_ORG: + case TupleCodes.CISTPL_PWR_MGMNT: + case TupleCodes.CISTPL_SPCL: + case TupleCodes.CISTPL_SWIL: + case TupleCodes.CISTPL_VERS_2: + tupleCode = $"Undecoded tuple ID {tuple.Code}"; + tupleDescription = $"Undecoded tuple ID {tuple.Code}"; - break; - case TupleCodes.CISTPL_ALTSTR: - case TupleCodes.CISTPL_BAR: - case TupleCodes.CISTPL_BATTERY: - case TupleCodes.CISTPL_BYTEORDER: - case TupleCodes.CISTPL_CFTABLE_ENTRY: - case TupleCodes.CISTPL_CFTABLE_ENTRY_CB: - case TupleCodes.CISTPL_CHECKSUM: - case TupleCodes.CISTPL_CONFIG: - case TupleCodes.CISTPL_CONFIG_CB: - case TupleCodes.CISTPL_DATE: - case TupleCodes.CISTPL_DEVICE: - case TupleCodes.CISTPL_DEVICE_A: - case TupleCodes.CISTPL_DEVICE_OA: - case TupleCodes.CISTPL_DEVICE_OC: - case TupleCodes.CISTPL_EXTDEVIC: - case TupleCodes.CISTPL_FORMAT: - case TupleCodes.CISTPL_FORMAT_A: - case TupleCodes.CISTPL_FUNCE: - case TupleCodes.CISTPL_FUNCID: - case TupleCodes.CISTPL_GEOMETRY: - case TupleCodes.CISTPL_INDIRECT: - case TupleCodes.CISTPL_JEDEC_A: - case TupleCodes.CISTPL_JEDEC_C: - case TupleCodes.CISTPL_LINKTARGET: - case TupleCodes.CISTPL_LONGLINK_A: - case TupleCodes.CISTPL_LONGLINK_C: - case TupleCodes.CISTPL_LONGLINK_CB: - case TupleCodes.CISTPL_LONGLINK_MFC: - case TupleCodes.CISTPL_NO_LINK: - case TupleCodes.CISTPL_ORG: - case TupleCodes.CISTPL_PWR_MGMNT: - case TupleCodes.CISTPL_SPCL: - case TupleCodes.CISTPL_SWIL: - case TupleCodes.CISTPL_VERS_2: - tupleCode = $"Undecoded tuple ID {tuple.Code}"; - tupleDescription = $"Undecoded tuple ID {tuple.Code}"; + break; + default: + tupleCode = $"0x{(byte)tuple.Code:X2}"; + tupleDescription = $"Found unknown tuple ID 0x{(byte)tuple.Code:X2}"; - break; - default: - tupleCode = $"0x{(byte)tuple.Code:X2}"; - tupleDescription = $"Found unknown tuple ID 0x{(byte)tuple.Code:X2}"; - - break; - } - - CisList.Add(new PcmciaCisModel - { - Code = tupleCode, - Description = tupleDescription - }); + break; } - else - AaruConsole.DebugWriteLine("Device-Info command", "PCMCIA CIS returned no tuples"); - } - public ObservableCollection CisList { get; } - - public string PcmciaCisText - { - get => _pcmciaCisText; - set => this.RaiseAndSetIfChanged(ref _pcmciaCisText, value); - } - - public PcmciaCisModel SelectedCis - { - get => _selectedCis; - set - { - if(_selectedCis == value) - return; - - PcmciaCisText = value?.Description; - this.RaiseAndSetIfChanged(ref _selectedCis, value); - } - } - - public ReactiveCommand SavePcmciaCisCommand { get; } - - async void ExecuteSavePcmciaCisCommand() - { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] + CisList.Add(new PcmciaCisModel { - "*.bin" - }), - Name = "Binary" - }); + Code = tupleCode, + Description = tupleDescription + }); + } + else + AaruConsole.DebugWriteLine("Device-Info command", "PCMCIA CIS returned no tuples"); + } - string result = await dlgSaveBinary.ShowAsync(_view); + public ObservableCollection CisList { get; } - if(result is null) + public string PcmciaCisText + { + get => _pcmciaCisText; + set => this.RaiseAndSetIfChanged(ref _pcmciaCisText, value); + } + + public PcmciaCisModel SelectedCis + { + get => _selectedCis; + set + { + if(_selectedCis == value) return; - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_cis, 0, _cis.Length); - - saveFs.Close(); + PcmciaCisText = value?.Description; + this.RaiseAndSetIfChanged(ref _selectedCis, value); } } + + public ReactiveCommand SavePcmciaCisCommand { get; } + + async void ExecuteSavePcmciaCisCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_cis, 0, _cis.Length); + + saveFs.Close(); + } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/ScsiInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/ScsiInfoViewModel.cs index 46a945f6e..7a7b006c3 100644 --- a/Aaru.Gui/ViewModels/Tabs/ScsiInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/ScsiInfoViewModel.cs @@ -45,1082 +45,1081 @@ using Avalonia.Controls; using ReactiveUI; using Inquiry = Aaru.CommonTypes.Structs.Devices.SCSI.Inquiry; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public sealed class ScsiInfoViewModel : ViewModelBase { - public sealed class ScsiInfoViewModel : ViewModelBase + readonly byte[] _configuration; + readonly byte[] _scsiModeSense10; + readonly byte[] _scsiModeSense6; + readonly Window _view; + string _evpdPageText; + string _mmcFeatureText; + string _scsiModeSensePageText; + object _selectedEvpdPage; + object _selectedMmcFeature; + object _selectedModeSensePage; + + public ScsiInfoViewModel(byte[] scsiInquiryData, Inquiry? scsiInquiry, Dictionary scsiEvpdPages, + Modes.DecodedMode? scsiMode, PeripheralDeviceTypes scsiType, byte[] scsiModeSense6, + byte[] scsiModeSense10, byte[] mmcConfiguration, Window view) { - readonly byte[] _configuration; - readonly byte[] _scsiModeSense10; - readonly byte[] _scsiModeSense6; - readonly Window _view; - string _evpdPageText; - string _mmcFeatureText; - string _scsiModeSensePageText; - object _selectedEvpdPage; - object _selectedMmcFeature; - object _selectedModeSensePage; + InquiryData = scsiInquiryData; + _scsiModeSense6 = scsiModeSense6; + _scsiModeSense10 = scsiModeSense10; + _configuration = mmcConfiguration; + _view = view; + ModeSensePages = new ObservableCollection(); + EvpdPages = new ObservableCollection(); + MmcFeatures = new ObservableCollection(); + SaveInquiryBinaryCommand = ReactiveCommand.Create(ExecuteSaveInquiryBinaryCommand); + SaveInquiryTextCommand = ReactiveCommand.Create(ExecuteSaveInquiryTextCommand); + SaveModeSense6Command = ReactiveCommand.Create(ExecuteSaveModeSense6Command); + SaveModeSense10Command = ReactiveCommand.Create(ExecuteSaveModeSense10Command); + SaveEvpdPageCommand = ReactiveCommand.Create(ExecuteSaveEvpdPageCommand); + SaveMmcFeaturesCommand = ReactiveCommand.Create(ExecuteSaveMmcFeaturesCommand); - public ScsiInfoViewModel(byte[] scsiInquiryData, Inquiry? scsiInquiry, Dictionary scsiEvpdPages, - Modes.DecodedMode? scsiMode, PeripheralDeviceTypes scsiType, byte[] scsiModeSense6, - byte[] scsiModeSense10, byte[] mmcConfiguration, Window view) + if(InquiryData == null || + !scsiInquiry.HasValue) + return; + + ScsiInquiryText = Decoders.SCSI.Inquiry.Prettify(scsiInquiry); + + if(scsiMode.HasValue) { - InquiryData = scsiInquiryData; - _scsiModeSense6 = scsiModeSense6; - _scsiModeSense10 = scsiModeSense10; - _configuration = mmcConfiguration; - _view = view; - ModeSensePages = new ObservableCollection(); - EvpdPages = new ObservableCollection(); - MmcFeatures = new ObservableCollection(); - SaveInquiryBinaryCommand = ReactiveCommand.Create(ExecuteSaveInquiryBinaryCommand); - SaveInquiryTextCommand = ReactiveCommand.Create(ExecuteSaveInquiryTextCommand); - SaveModeSense6Command = ReactiveCommand.Create(ExecuteSaveModeSense6Command); - SaveModeSense10Command = ReactiveCommand.Create(ExecuteSaveModeSense10Command); - SaveEvpdPageCommand = ReactiveCommand.Create(ExecuteSaveEvpdPageCommand); - SaveMmcFeaturesCommand = ReactiveCommand.Create(ExecuteSaveMmcFeaturesCommand); - - if(InquiryData == null || - !scsiInquiry.HasValue) - return; - - ScsiInquiryText = Decoders.SCSI.Inquiry.Prettify(scsiInquiry); - - if(scsiMode.HasValue) + ModeSensePages.Add(new ScsiPageModel { - ModeSensePages.Add(new ScsiPageModel + Page = "Header", + Description = Modes.PrettifyModeHeader(scsiMode.Value.Header, scsiType) + }); + + if(scsiMode.Value.Pages != null) + foreach(Modes.ModePage page in scsiMode.Value.Pages.OrderBy(t => t.Page).ThenBy(t => t.Subpage)) { - Page = "Header", - Description = Modes.PrettifyModeHeader(scsiMode.Value.Header, scsiType) - }); + string pageNumberText = page.Subpage == 0 ? $"MODE {page.Page:X2}h" + : $"MODE {page.Page:X2} Subpage {page.Subpage:X2}"; - if(scsiMode.Value.Pages != null) - foreach(Modes.ModePage page in scsiMode.Value.Pages.OrderBy(t => t.Page).ThenBy(t => t.Subpage)) + string decodedText; + + switch(page.Page) { - string pageNumberText = page.Subpage == 0 ? $"MODE {page.Page:X2}h" - : $"MODE {page.Page:X2} Subpage {page.Subpage:X2}"; - - string decodedText; - - switch(page.Page) + case 0x00: { - case 0x00: - { - if(scsiType == PeripheralDeviceTypes.MultiMediaDevice && - page.Subpage == 0) - decodedText = Modes.PrettifyModePage_00_SFF(page.PageResponse); - else - decodedText = "Undecoded"; - - break; - } - case 0x01: - { - if(page.Subpage == 0) - decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice - ? Modes.PrettifyModePage_01_MMC(page.PageResponse) - : Modes.PrettifyModePage_01(page.PageResponse); - else - goto default; - - break; - } - case 0x02: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_02(page.PageResponse); - else - goto default; - - break; - } - case 0x03: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_03(page.PageResponse); - else - goto default; - - break; - } - case 0x04: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_04(page.PageResponse); - else - goto default; - - break; - } - case 0x05: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_05(page.PageResponse); - else - goto default; - - break; - } - case 0x06: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_06(page.PageResponse); - else - goto default; - - break; - } - case 0x07: - { - if(page.Subpage == 0) - decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice - ? Modes.PrettifyModePage_07_MMC(page.PageResponse) - : Modes.PrettifyModePage_07(page.PageResponse); - else - goto default; - - break; - } - case 0x08: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_08(page.PageResponse); - else - goto default; - - break; - } - case 0x0A: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_0A(page.PageResponse); - else if(page.Subpage == 1) - decodedText = Modes.PrettifyModePage_0A_S01(page.PageResponse); - else - goto default; - - break; - } - case 0x0B: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_0B(page.PageResponse); - else - goto default; - - break; - } - case 0x0D: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_0D(page.PageResponse); - else - goto default; - - break; - } - case 0x0E: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_0E(page.PageResponse); - else - goto default; - - break; - } - case 0x0F: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_0F(page.PageResponse); - else - goto default; - - break; - } - case 0x10: - { - if(page.Subpage == 0) - decodedText = scsiType == PeripheralDeviceTypes.SequentialAccess - ? Modes.PrettifyModePage_10_SSC(page.PageResponse) - : Modes.PrettifyModePage_10(page.PageResponse); - else - goto default; - - break; - } - case 0x11: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_11(page.PageResponse); - else - goto default; - - break; - } - case 0x12: - case 0x13: - case 0x14: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_12_13_14(page.PageResponse); - else - goto default; - - break; - } - case 0x1A: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_1A(page.PageResponse); - else if(page.Subpage == 1) - decodedText = Modes.PrettifyModePage_1A_S01(page.PageResponse); - else - goto default; - - break; - } - case 0x1B: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_1B(page.PageResponse); - else - goto default; - - break; - } - case 0x1C: - { - if(page.Subpage == 0) - decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice - ? Modes.PrettifyModePage_1C_SFF(page.PageResponse) - : Modes.PrettifyModePage_1C(page.PageResponse); - else if(page.Subpage == 1) - decodedText = Modes.PrettifyModePage_1C_S01(page.PageResponse); - else - goto default; - - break; - } - case 0x1D: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_1D(page.PageResponse); - else - goto default; - - break; - } - case 0x21: - { - if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == - "CERTANCE") - decodedText = Modes.PrettifyCertanceModePage_21(page.PageResponse); - else - goto default; - - break; - } - case 0x22: - { - if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == - "CERTANCE") - decodedText = Modes.PrettifyCertanceModePage_22(page.PageResponse); - else - goto default; - - break; - } - case 0x24: - { - if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM") - decodedText = Modes.PrettifyIBMModePage_24(page.PageResponse); - else - goto default; - - break; - } - case 0x2A: - { - if(page.Subpage == 0) - decodedText = Modes.PrettifyModePage_2A(page.PageResponse); - else - goto default; - - break; - } - case 0x2F: - { - if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM") - decodedText = Modes.PrettifyIBMModePage_2F(page.PageResponse); - else - goto default; - - break; - } - case 0x30: - { - if(Modes.IsAppleModePage_30(page.PageResponse)) - decodedText = "Drive identifies as Apple OEM drive"; - else - goto default; - - break; - } - case 0x3B: - { - if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP") - decodedText = Modes.PrettifyHPModePage_3B(page.PageResponse); - else - goto default; - - break; - } - case 0x3C: - { - if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP") - decodedText = Modes.PrettifyHPModePage_3C(page.PageResponse); - else - goto default; - - break; - } - case 0x3D: - { - if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM") - decodedText = Modes.PrettifyIBMModePage_3D(page.PageResponse); - else if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP") - decodedText = Modes.PrettifyHPModePage_3D(page.PageResponse); - else - goto default; - - break; - } - case 0x3E: - { - if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "FUJITSU") - decodedText = Modes.PrettifyFujitsuModePage_3E(page.PageResponse); - else if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP") - decodedText = Modes.PrettifyHPModePage_3E(page.PageResponse); - else - goto default; - - break; - } - default: - { + if(scsiType == PeripheralDeviceTypes.MultiMediaDevice && + page.Subpage == 0) + decodedText = Modes.PrettifyModePage_00_SFF(page.PageResponse); + else decodedText = "Undecoded"; - break; - } + break; } - - // TODO: Automatic error reporting - if(decodedText == null) - decodedText = "Error decoding page, please open an issue."; - - ModeSensePages.Add(new ScsiPageModel + case 0x01: { - Page = pageNumberText, - Description = decodedText - }); - } - } + if(page.Subpage == 0) + decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice + ? Modes.PrettifyModePage_01_MMC(page.PageResponse) + : Modes.PrettifyModePage_01(page.PageResponse); + else + goto default; - if(scsiEvpdPages != null) - { - foreach(KeyValuePair page in scsiEvpdPages.OrderBy(t => t.Key)) - { - string evpdPageTitle = ""; - string evpdDecodedPage; - - if(page.Key >= 0x01 && - page.Key <= 0x7F) - { - evpdPageTitle = $"ASCII Page {page.Key:X2}h"; - evpdDecodedPage = EVPD.DecodeASCIIPage(page.Value); - } - else if(page.Key == 0x80) - { - evpdPageTitle = "Unit Serial Number"; - evpdDecodedPage = EVPD.DecodePage80(page.Value); - } - else if(page.Key == 0x81) - { - evpdPageTitle = "SCSI Implemented operating definitions"; - evpdDecodedPage = EVPD.PrettifyPage_81(page.Value); - } - else if(page.Key == 0x82) - { - evpdPageTitle = "ASCII implemented operating definitions"; - evpdDecodedPage = EVPD.DecodePage82(page.Value); - } - else if(page.Key == 0x83) - { - evpdPageTitle = "SCSI Device identification"; - evpdDecodedPage = EVPD.PrettifyPage_83(page.Value); - } - else if(page.Key == 0x84) - { - evpdPageTitle = "SCSI Software Interface Identifiers"; - evpdDecodedPage = EVPD.PrettifyPage_84(page.Value); - } - else if(page.Key == 0x85) - { - evpdPageTitle = "SCSI Management Network Addresses"; - evpdDecodedPage = EVPD.PrettifyPage_85(page.Value); - } - else if(page.Key == 0x86) - { - evpdPageTitle = "SCSI Extended INQUIRY Data"; - evpdDecodedPage = EVPD.PrettifyPage_86(page.Value); - } - else if(page.Key == 0x89) - { - evpdPageTitle = "SCSI to ATA Translation Layer Data"; - evpdDecodedPage = EVPD.PrettifyPage_89(page.Value); - } - else if(page.Key == 0xB0) - { - evpdPageTitle = "SCSI Sequential-access Device Capabilities"; - evpdDecodedPage = EVPD.PrettifyPage_B0(page.Value); - } - else if(page.Key == 0xB1) - { - evpdPageTitle = "Manufacturer-assigned Serial Number"; - evpdDecodedPage = EVPD.DecodePageB1(page.Value); - } - else if(page.Key == 0xB2) - { - evpdPageTitle = "TapeAlert Supported Flags Bitmap"; - evpdDecodedPage = $"0x{EVPD.DecodePageB2(page.Value):X16}"; - } - else if(page.Key == 0xB3) - { - evpdPageTitle = "Automation Device Serial Number"; - evpdDecodedPage = EVPD.DecodePageB3(page.Value); - } - else if(page.Key == 0xB4) - { - evpdPageTitle = "Data Transfer Device Element Address"; - evpdDecodedPage = EVPD.DecodePageB4(page.Value); - } - else if(page.Key == 0xC0 && - StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). - Trim() == "quantum") - { - evpdPageTitle = "Quantum Firmware Build Information page"; - evpdDecodedPage = EVPD.PrettifyPage_C0_Quantum(page.Value); - } - else if(page.Key == 0xC0 && - StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). - Trim() == "seagate") - { - evpdPageTitle = "Seagate Firmware Numbers page"; - evpdDecodedPage = EVPD.PrettifyPage_C0_Seagate(page.Value); - } - else if(page.Key == 0xC0 && - StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). - Trim() == "ibm") - { - evpdPageTitle = "IBM Drive Component Revision Levels page"; - evpdDecodedPage = EVPD.PrettifyPage_C0_IBM(page.Value); - } - else if(page.Key == 0xC1 && - StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). - Trim() == "ibm") - { - evpdPageTitle = "IBM Drive Serial Numbers page"; - evpdDecodedPage = EVPD.PrettifyPage_C1_IBM(page.Value); - } - else if((page.Key == 0xC0 || page.Key == 0xC1) && - StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). - Trim() == "certance") - { - evpdPageTitle = "Certance Drive Component Revision Levels page"; - evpdDecodedPage = EVPD.PrettifyPage_C0_C1_Certance(page.Value); - } - else if((page.Key == 0xC2 || page.Key == 0xC3 || page.Key == 0xC4 || page.Key == 0xC5 || - page.Key == 0xC6) && - StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). - Trim() == "certance") - { - switch(page.Key) - { - case 0xC2: - evpdPageTitle = "Head Assembly Serial Number"; - - break; - case 0xC3: - evpdPageTitle = "Reel Motor 1 Serial Number"; - - break; - case 0xC4: - evpdPageTitle = "Reel Motor 2 Serial Number"; - - break; - case 0xC5: - evpdPageTitle = "Board Serial Number"; - - break; - case 0xC6: - evpdPageTitle = "Base Mechanical Serial Number"; - - break; + break; } - - evpdDecodedPage = EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(page.Value); - } - else if((page.Key == 0xC0 || page.Key == 0xC1 || page.Key == 0xC2 || page.Key == 0xC3 || - page.Key == 0xC4 || page.Key == 0xC5) && - StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). - Trim() == "hp") - { - switch(page.Key) + case 0x02: { - case 0xC0: - evpdPageTitle = "HP Drive Firmware Revision Levels page:"; + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_02(page.PageResponse); + else + goto default; - break; - case 0xC1: - evpdPageTitle = "HP Drive Hardware Revision Levels page:"; - - break; - case 0xC2: - evpdPageTitle = "HP Drive PCA Revision Levels page:"; - - break; - case 0xC3: - evpdPageTitle = "HP Drive Mechanism Revision Levels page:"; - - break; - case 0xC4: - evpdPageTitle = "HP Drive Head Assembly Revision Levels page:"; - - break; - case 0xC5: - evpdPageTitle = "HP Drive ACI Revision Levels page:"; - - break; + break; } + case 0x03: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_03(page.PageResponse); + else + goto default; - evpdDecodedPage = EVPD.PrettifyPage_C0_to_C5_HP(page.Value); - } - else if(page.Key == 0xDF && - StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). - Trim() == "certance") - { - evpdPageTitle = "Certance drive status page"; - evpdDecodedPage = EVPD.PrettifyPage_DF_Certance(page.Value); - } - else - { - if(page.Key == 0x00) - continue; + break; + } + case 0x04: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_04(page.PageResponse); + else + goto default; - evpdPageTitle = $"Page {page.Key:X2}h"; - evpdDecodedPage = "Undecoded"; + break; + } + case 0x05: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_05(page.PageResponse); + else + goto default; - AaruConsole.DebugWriteLine("Device-Info command", "Found undecoded SCSI VPD page 0x{0:X2}", - page.Key); + break; + } + case 0x06: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_06(page.PageResponse); + else + goto default; + + break; + } + case 0x07: + { + if(page.Subpage == 0) + decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice + ? Modes.PrettifyModePage_07_MMC(page.PageResponse) + : Modes.PrettifyModePage_07(page.PageResponse); + else + goto default; + + break; + } + case 0x08: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_08(page.PageResponse); + else + goto default; + + break; + } + case 0x0A: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_0A(page.PageResponse); + else if(page.Subpage == 1) + decodedText = Modes.PrettifyModePage_0A_S01(page.PageResponse); + else + goto default; + + break; + } + case 0x0B: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_0B(page.PageResponse); + else + goto default; + + break; + } + case 0x0D: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_0D(page.PageResponse); + else + goto default; + + break; + } + case 0x0E: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_0E(page.PageResponse); + else + goto default; + + break; + } + case 0x0F: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_0F(page.PageResponse); + else + goto default; + + break; + } + case 0x10: + { + if(page.Subpage == 0) + decodedText = scsiType == PeripheralDeviceTypes.SequentialAccess + ? Modes.PrettifyModePage_10_SSC(page.PageResponse) + : Modes.PrettifyModePage_10(page.PageResponse); + else + goto default; + + break; + } + case 0x11: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_11(page.PageResponse); + else + goto default; + + break; + } + case 0x12: + case 0x13: + case 0x14: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_12_13_14(page.PageResponse); + else + goto default; + + break; + } + case 0x1A: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_1A(page.PageResponse); + else if(page.Subpage == 1) + decodedText = Modes.PrettifyModePage_1A_S01(page.PageResponse); + else + goto default; + + break; + } + case 0x1B: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_1B(page.PageResponse); + else + goto default; + + break; + } + case 0x1C: + { + if(page.Subpage == 0) + decodedText = scsiType == PeripheralDeviceTypes.MultiMediaDevice + ? Modes.PrettifyModePage_1C_SFF(page.PageResponse) + : Modes.PrettifyModePage_1C(page.PageResponse); + else if(page.Subpage == 1) + decodedText = Modes.PrettifyModePage_1C_S01(page.PageResponse); + else + goto default; + + break; + } + case 0x1D: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_1D(page.PageResponse); + else + goto default; + + break; + } + case 0x21: + { + if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == + "CERTANCE") + decodedText = Modes.PrettifyCertanceModePage_21(page.PageResponse); + else + goto default; + + break; + } + case 0x22: + { + if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == + "CERTANCE") + decodedText = Modes.PrettifyCertanceModePage_22(page.PageResponse); + else + goto default; + + break; + } + case 0x24: + { + if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM") + decodedText = Modes.PrettifyIBMModePage_24(page.PageResponse); + else + goto default; + + break; + } + case 0x2A: + { + if(page.Subpage == 0) + decodedText = Modes.PrettifyModePage_2A(page.PageResponse); + else + goto default; + + break; + } + case 0x2F: + { + if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM") + decodedText = Modes.PrettifyIBMModePage_2F(page.PageResponse); + else + goto default; + + break; + } + case 0x30: + { + if(Modes.IsAppleModePage_30(page.PageResponse)) + decodedText = "Drive identifies as Apple OEM drive"; + else + goto default; + + break; + } + case 0x3B: + { + if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP") + decodedText = Modes.PrettifyHPModePage_3B(page.PageResponse); + else + goto default; + + break; + } + case 0x3C: + { + if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP") + decodedText = Modes.PrettifyHPModePage_3C(page.PageResponse); + else + goto default; + + break; + } + case 0x3D: + { + if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "IBM") + decodedText = Modes.PrettifyIBMModePage_3D(page.PageResponse); + else if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP") + decodedText = Modes.PrettifyHPModePage_3D(page.PageResponse); + else + goto default; + + break; + } + case 0x3E: + { + if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "FUJITSU") + decodedText = Modes.PrettifyFujitsuModePage_3E(page.PageResponse); + else if(StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).Trim() == "HP") + decodedText = Modes.PrettifyHPModePage_3E(page.PageResponse); + else + goto default; + + break; + } + default: + { + decodedText = "Undecoded"; + + break; + } } - EvpdPages.Add(new ScsiPageModel + // TODO: Automatic error reporting + if(decodedText == null) + decodedText = "Error decoding page, please open an issue."; + + ModeSensePages.Add(new ScsiPageModel { - Page = evpdPageTitle, - Data = page.Value, - Description = evpdDecodedPage + Page = pageNumberText, + Description = decodedText }); } - } + } - if(_configuration != null) + if(scsiEvpdPages != null) + { + foreach(KeyValuePair page in scsiEvpdPages.OrderBy(t => t.Key)) { - Features.SeparatedFeatures ftr = Features.Separate(_configuration); + string evpdPageTitle = ""; + string evpdDecodedPage; - AaruConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION length is {0} bytes", - ftr.DataLength); - - AaruConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION current profile is {0:X4}h", - ftr.CurrentProfile); - - if(ftr.Descriptors != null) - foreach(Features.FeatureDescriptor desc in ftr.Descriptors) + if(page.Key >= 0x01 && + page.Key <= 0x7F) + { + evpdPageTitle = $"ASCII Page {page.Key:X2}h"; + evpdDecodedPage = EVPD.DecodeASCIIPage(page.Value); + } + else if(page.Key == 0x80) + { + evpdPageTitle = "Unit Serial Number"; + evpdDecodedPage = EVPD.DecodePage80(page.Value); + } + else if(page.Key == 0x81) + { + evpdPageTitle = "SCSI Implemented operating definitions"; + evpdDecodedPage = EVPD.PrettifyPage_81(page.Value); + } + else if(page.Key == 0x82) + { + evpdPageTitle = "ASCII implemented operating definitions"; + evpdDecodedPage = EVPD.DecodePage82(page.Value); + } + else if(page.Key == 0x83) + { + evpdPageTitle = "SCSI Device identification"; + evpdDecodedPage = EVPD.PrettifyPage_83(page.Value); + } + else if(page.Key == 0x84) + { + evpdPageTitle = "SCSI Software Interface Identifiers"; + evpdDecodedPage = EVPD.PrettifyPage_84(page.Value); + } + else if(page.Key == 0x85) + { + evpdPageTitle = "SCSI Management Network Addresses"; + evpdDecodedPage = EVPD.PrettifyPage_85(page.Value); + } + else if(page.Key == 0x86) + { + evpdPageTitle = "SCSI Extended INQUIRY Data"; + evpdDecodedPage = EVPD.PrettifyPage_86(page.Value); + } + else if(page.Key == 0x89) + { + evpdPageTitle = "SCSI to ATA Translation Layer Data"; + evpdDecodedPage = EVPD.PrettifyPage_89(page.Value); + } + else if(page.Key == 0xB0) + { + evpdPageTitle = "SCSI Sequential-access Device Capabilities"; + evpdDecodedPage = EVPD.PrettifyPage_B0(page.Value); + } + else if(page.Key == 0xB1) + { + evpdPageTitle = "Manufacturer-assigned Serial Number"; + evpdDecodedPage = EVPD.DecodePageB1(page.Value); + } + else if(page.Key == 0xB2) + { + evpdPageTitle = "TapeAlert Supported Flags Bitmap"; + evpdDecodedPage = $"0x{EVPD.DecodePageB2(page.Value):X16}"; + } + else if(page.Key == 0xB3) + { + evpdPageTitle = "Automation Device Serial Number"; + evpdDecodedPage = EVPD.DecodePageB3(page.Value); + } + else if(page.Key == 0xB4) + { + evpdPageTitle = "Data Transfer Device Element Address"; + evpdDecodedPage = EVPD.DecodePageB4(page.Value); + } + else if(page.Key == 0xC0 && + StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). + Trim() == "quantum") + { + evpdPageTitle = "Quantum Firmware Build Information page"; + evpdDecodedPage = EVPD.PrettifyPage_C0_Quantum(page.Value); + } + else if(page.Key == 0xC0 && + StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). + Trim() == "seagate") + { + evpdPageTitle = "Seagate Firmware Numbers page"; + evpdDecodedPage = EVPD.PrettifyPage_C0_Seagate(page.Value); + } + else if(page.Key == 0xC0 && + StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). + Trim() == "ibm") + { + evpdPageTitle = "IBM Drive Component Revision Levels page"; + evpdDecodedPage = EVPD.PrettifyPage_C0_IBM(page.Value); + } + else if(page.Key == 0xC1 && + StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). + Trim() == "ibm") + { + evpdPageTitle = "IBM Drive Serial Numbers page"; + evpdDecodedPage = EVPD.PrettifyPage_C1_IBM(page.Value); + } + else if((page.Key == 0xC0 || page.Key == 0xC1) && + StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). + Trim() == "certance") + { + evpdPageTitle = "Certance Drive Component Revision Levels page"; + evpdDecodedPage = EVPD.PrettifyPage_C0_C1_Certance(page.Value); + } + else if((page.Key == 0xC2 || page.Key == 0xC3 || page.Key == 0xC4 || page.Key == 0xC5 || + page.Key == 0xC6) && + StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). + Trim() == "certance") + { + switch(page.Key) { - string featureNumber = $"Feature {desc.Code:X4}h"; - string featureDescription; - AaruConsole.DebugWriteLine("Device-Info command", "Feature {0:X4}h", desc.Code); + case 0xC2: + evpdPageTitle = "Head Assembly Serial Number"; - switch(desc.Code) - { - case 0x0000: - featureDescription = Features.Prettify_0000(desc.Data); + break; + case 0xC3: + evpdPageTitle = "Reel Motor 1 Serial Number"; - break; - case 0x0001: - featureDescription = Features.Prettify_0001(desc.Data); + break; + case 0xC4: + evpdPageTitle = "Reel Motor 2 Serial Number"; - break; - case 0x0002: - featureDescription = Features.Prettify_0002(desc.Data); + break; + case 0xC5: + evpdPageTitle = "Board Serial Number"; - break; - case 0x0003: - featureDescription = Features.Prettify_0003(desc.Data); + break; + case 0xC6: + evpdPageTitle = "Base Mechanical Serial Number"; - break; - case 0x0004: - featureDescription = Features.Prettify_0004(desc.Data); - - break; - case 0x0010: - featureDescription = Features.Prettify_0010(desc.Data); - - break; - case 0x001D: - featureDescription = Features.Prettify_001D(desc.Data); - - break; - case 0x001E: - featureDescription = Features.Prettify_001E(desc.Data); - - break; - case 0x001F: - featureDescription = Features.Prettify_001F(desc.Data); - - break; - case 0x0020: - featureDescription = Features.Prettify_0020(desc.Data); - - break; - case 0x0021: - featureDescription = Features.Prettify_0021(desc.Data); - - break; - case 0x0022: - featureDescription = Features.Prettify_0022(desc.Data); - - break; - case 0x0023: - featureDescription = Features.Prettify_0023(desc.Data); - - break; - case 0x0024: - featureDescription = Features.Prettify_0024(desc.Data); - - break; - case 0x0025: - featureDescription = Features.Prettify_0025(desc.Data); - - break; - case 0x0026: - featureDescription = Features.Prettify_0026(desc.Data); - - break; - case 0x0027: - featureDescription = Features.Prettify_0027(desc.Data); - - break; - case 0x0028: - featureDescription = Features.Prettify_0028(desc.Data); - - break; - case 0x0029: - featureDescription = Features.Prettify_0029(desc.Data); - - break; - case 0x002A: - featureDescription = Features.Prettify_002A(desc.Data); - - break; - case 0x002B: - featureDescription = Features.Prettify_002B(desc.Data); - - break; - case 0x002C: - featureDescription = Features.Prettify_002C(desc.Data); - - break; - case 0x002D: - featureDescription = Features.Prettify_002D(desc.Data); - - break; - case 0x002E: - featureDescription = Features.Prettify_002E(desc.Data); - - break; - case 0x002F: - featureDescription = Features.Prettify_002F(desc.Data); - - break; - case 0x0030: - featureDescription = Features.Prettify_0030(desc.Data); - - break; - case 0x0031: - featureDescription = Features.Prettify_0031(desc.Data); - - break; - case 0x0032: - featureDescription = Features.Prettify_0032(desc.Data); - - break; - case 0x0033: - featureDescription = Features.Prettify_0033(desc.Data); - - break; - case 0x0035: - featureDescription = Features.Prettify_0035(desc.Data); - - break; - case 0x0037: - featureDescription = Features.Prettify_0037(desc.Data); - - break; - case 0x0038: - featureDescription = Features.Prettify_0038(desc.Data); - - break; - case 0x003A: - featureDescription = Features.Prettify_003A(desc.Data); - - break; - case 0x003B: - featureDescription = Features.Prettify_003B(desc.Data); - - break; - case 0x0040: - featureDescription = Features.Prettify_0040(desc.Data); - - break; - case 0x0041: - featureDescription = Features.Prettify_0041(desc.Data); - - break; - case 0x0042: - featureDescription = Features.Prettify_0042(desc.Data); - - break; - case 0x0050: - featureDescription = Features.Prettify_0050(desc.Data); - - break; - case 0x0051: - featureDescription = Features.Prettify_0051(desc.Data); - - break; - case 0x0080: - featureDescription = Features.Prettify_0080(desc.Data); - - break; - case 0x0100: - featureDescription = Features.Prettify_0100(desc.Data); - - break; - case 0x0101: - featureDescription = Features.Prettify_0101(desc.Data); - - break; - case 0x0102: - featureDescription = Features.Prettify_0102(desc.Data); - - break; - case 0x0103: - featureDescription = Features.Prettify_0103(desc.Data); - - break; - case 0x0104: - featureDescription = Features.Prettify_0104(desc.Data); - - break; - case 0x0105: - featureDescription = Features.Prettify_0105(desc.Data); - - break; - case 0x0106: - featureDescription = Features.Prettify_0106(desc.Data); - - break; - case 0x0107: - featureDescription = Features.Prettify_0107(desc.Data); - - break; - case 0x0108: - featureDescription = Features.Prettify_0108(desc.Data); - - break; - case 0x0109: - featureDescription = Features.Prettify_0109(desc.Data); - - break; - case 0x010A: - featureDescription = Features.Prettify_010A(desc.Data); - - break; - case 0x010B: - featureDescription = Features.Prettify_010B(desc.Data); - - break; - case 0x010C: - featureDescription = Features.Prettify_010C(desc.Data); - - break; - case 0x010D: - featureDescription = Features.Prettify_010D(desc.Data); - - break; - case 0x010E: - featureDescription = Features.Prettify_010E(desc.Data); - - break; - case 0x0110: - featureDescription = Features.Prettify_0110(desc.Data); - - break; - case 0x0113: - featureDescription = Features.Prettify_0113(desc.Data); - - break; - case 0x0142: - featureDescription = Features.Prettify_0142(desc.Data); - - break; - default: - featureDescription = "Unknown feature"; - - break; - } - - MmcFeatures.Add(new ScsiPageModel - { - Page = featureNumber, - Description = featureDescription - }); + break; } + + evpdDecodedPage = EVPD.PrettifyPage_C2_C3_C4_C5_C6_Certance(page.Value); + } + else if((page.Key == 0xC0 || page.Key == 0xC1 || page.Key == 0xC2 || page.Key == 0xC3 || + page.Key == 0xC4 || page.Key == 0xC5) && + StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). + Trim() == "hp") + { + switch(page.Key) + { + case 0xC0: + evpdPageTitle = "HP Drive Firmware Revision Levels page:"; + + break; + case 0xC1: + evpdPageTitle = "HP Drive Hardware Revision Levels page:"; + + break; + case 0xC2: + evpdPageTitle = "HP Drive PCA Revision Levels page:"; + + break; + case 0xC3: + evpdPageTitle = "HP Drive Mechanism Revision Levels page:"; + + break; + case 0xC4: + evpdPageTitle = "HP Drive Head Assembly Revision Levels page:"; + + break; + case 0xC5: + evpdPageTitle = "HP Drive ACI Revision Levels page:"; + + break; + } + + evpdDecodedPage = EVPD.PrettifyPage_C0_to_C5_HP(page.Value); + } + else if(page.Key == 0xDF && + StringHandlers.CToString(scsiInquiry.Value.VendorIdentification).ToLowerInvariant(). + Trim() == "certance") + { + evpdPageTitle = "Certance drive status page"; + evpdDecodedPage = EVPD.PrettifyPage_DF_Certance(page.Value); + } else - AaruConsole.DebugWriteLine("Device-Info command", - "GET CONFIGURATION returned no feature descriptors"); + { + if(page.Key == 0x00) + continue; + + evpdPageTitle = $"Page {page.Key:X2}h"; + evpdDecodedPage = "Undecoded"; + + AaruConsole.DebugWriteLine("Device-Info command", "Found undecoded SCSI VPD page 0x{0:X2}", + page.Key); + } + + EvpdPages.Add(new ScsiPageModel + { + Page = evpdPageTitle, + Data = page.Value, + Description = evpdDecodedPage + }); } } - public byte[] InquiryData { get; } - public string ScsiInquiryText { get; } - public ObservableCollection ModeSensePages { get; } - public ObservableCollection EvpdPages { get; } - public ObservableCollection MmcFeatures { get; } - public ReactiveCommand SaveInquiryBinaryCommand { get; } - public ReactiveCommand SaveInquiryTextCommand { get; } - public ReactiveCommand SaveModeSense6Command { get; } - public ReactiveCommand SaveModeSense10Command { get; } - public ReactiveCommand SaveEvpdPageCommand { get; } - public ReactiveCommand SaveMmcFeaturesCommand { get; } - - public object SelectedModeSensePage + if(_configuration != null) { - get => _selectedModeSensePage; - set - { - if(value == _selectedModeSensePage) - return; + Features.SeparatedFeatures ftr = Features.Separate(_configuration); - if(value is ScsiPageModel pageModel) - ModeSensePageText = pageModel.Description; + AaruConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION length is {0} bytes", + ftr.DataLength); - this.RaiseAndSetIfChanged(ref _selectedModeSensePage, value); - } - } + AaruConsole.DebugWriteLine("Device-Info command", "GET CONFIGURATION current profile is {0:X4}h", + ftr.CurrentProfile); - public string ModeSensePageText - { - get => _scsiModeSensePageText; - set => this.RaiseAndSetIfChanged(ref _scsiModeSensePageText, value); - } - - public object SelectedEvpdPage - { - get => _selectedEvpdPage; - set - { - if(value == _selectedEvpdPage) - return; - - if(value is ScsiPageModel pageModel) - EvpdPageText = pageModel.Description; - - this.RaiseAndSetIfChanged(ref _selectedEvpdPage, value); - } - } - - public string EvpdPageText - { - get => _evpdPageText; - set => this.RaiseAndSetIfChanged(ref _evpdPageText, value); - } - - public object SelectedMmcFeature - { - get => _selectedMmcFeature; - set - { - if(value == _selectedMmcFeature) - return; - - if(value is ScsiPageModel pageModel) - MmcFeatureText = pageModel.Description; - - this.RaiseAndSetIfChanged(ref _selectedMmcFeature, value); - } - } - - public string MmcFeatureText - { - get => _mmcFeatureText; - set => this.RaiseAndSetIfChanged(ref _mmcFeatureText, value); - } - - async void ExecuteSaveInquiryBinaryCommand() - { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] + if(ftr.Descriptors != null) + foreach(Features.FeatureDescriptor desc in ftr.Descriptors) { - "*.bin" - }), - Name = "Binary" - }); + string featureNumber = $"Feature {desc.Code:X4}h"; + string featureDescription; + AaruConsole.DebugWriteLine("Device-Info command", "Feature {0:X4}h", desc.Code); - string result = await dlgSaveBinary.ShowAsync(_view); + switch(desc.Code) + { + case 0x0000: + featureDescription = Features.Prettify_0000(desc.Data); - if(result is null) - return; + break; + case 0x0001: + featureDescription = Features.Prettify_0001(desc.Data); - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(InquiryData, 0, InquiryData.Length); + break; + case 0x0002: + featureDescription = Features.Prettify_0002(desc.Data); - saveFs.Close(); - } + break; + case 0x0003: + featureDescription = Features.Prettify_0003(desc.Data); - async void ExecuteSaveInquiryTextCommand() - { - var dlgSaveText = new SaveFileDialog(); + break; + case 0x0004: + featureDescription = Features.Prettify_0004(desc.Data); - dlgSaveText.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.txt" - }), - Name = "Text" - }); + break; + case 0x0010: + featureDescription = Features.Prettify_0010(desc.Data); - string result = await dlgSaveText.ShowAsync(_view); + break; + case 0x001D: + featureDescription = Features.Prettify_001D(desc.Data); - if(result is null) - return; + break; + case 0x001E: + featureDescription = Features.Prettify_001E(desc.Data); - var saveFs = new FileStream(result, FileMode.Create); - var saveSw = new StreamWriter(saveFs); - saveSw.Write(ScsiInquiryText); - saveFs.Close(); - } + break; + case 0x001F: + featureDescription = Features.Prettify_001F(desc.Data); - async void ExecuteSaveModeSense6Command() - { - var dlgSaveBinary = new SaveFileDialog(); + break; + case 0x0020: + featureDescription = Features.Prettify_0020(desc.Data); - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + break; + case 0x0021: + featureDescription = Features.Prettify_0021(desc.Data); - string result = await dlgSaveBinary.ShowAsync(_view); + break; + case 0x0022: + featureDescription = Features.Prettify_0022(desc.Data); - if(result is null) - return; + break; + case 0x0023: + featureDescription = Features.Prettify_0023(desc.Data); - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_scsiModeSense6, 0, _scsiModeSense6.Length); + break; + case 0x0024: + featureDescription = Features.Prettify_0024(desc.Data); - saveFs.Close(); - } + break; + case 0x0025: + featureDescription = Features.Prettify_0025(desc.Data); - async void ExecuteSaveModeSense10Command() - { - var dlgSaveBinary = new SaveFileDialog(); + break; + case 0x0026: + featureDescription = Features.Prettify_0026(desc.Data); - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + break; + case 0x0027: + featureDescription = Features.Prettify_0027(desc.Data); - string result = await dlgSaveBinary.ShowAsync(_view); + break; + case 0x0028: + featureDescription = Features.Prettify_0028(desc.Data); - if(result is null) - return; + break; + case 0x0029: + featureDescription = Features.Prettify_0029(desc.Data); - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_scsiModeSense10, 0, _scsiModeSense10.Length); + break; + case 0x002A: + featureDescription = Features.Prettify_002A(desc.Data); - saveFs.Close(); - } + break; + case 0x002B: + featureDescription = Features.Prettify_002B(desc.Data); - async void ExecuteSaveEvpdPageCommand() - { - if(!(SelectedEvpdPage is ScsiPageModel pageModel)) - return; + break; + case 0x002C: + featureDescription = Features.Prettify_002C(desc.Data); - var dlgSaveBinary = new SaveFileDialog(); + break; + case 0x002D: + featureDescription = Features.Prettify_002D(desc.Data); - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + break; + case 0x002E: + featureDescription = Features.Prettify_002E(desc.Data); - string result = await dlgSaveBinary.ShowAsync(_view); + break; + case 0x002F: + featureDescription = Features.Prettify_002F(desc.Data); - if(result is null) - return; + break; + case 0x0030: + featureDescription = Features.Prettify_0030(desc.Data); - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(pageModel.Data, 0, pageModel.Data.Length); + break; + case 0x0031: + featureDescription = Features.Prettify_0031(desc.Data); - saveFs.Close(); - } + break; + case 0x0032: + featureDescription = Features.Prettify_0032(desc.Data); - async void ExecuteSaveMmcFeaturesCommand() - { - var dlgSaveBinary = new SaveFileDialog(); + break; + case 0x0033: + featureDescription = Features.Prettify_0033(desc.Data); - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); + break; + case 0x0035: + featureDescription = Features.Prettify_0035(desc.Data); - string result = await dlgSaveBinary.ShowAsync(_view); + break; + case 0x0037: + featureDescription = Features.Prettify_0037(desc.Data); - if(result is null) - return; + break; + case 0x0038: + featureDescription = Features.Prettify_0038(desc.Data); - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(_configuration, 0, _configuration.Length); + break; + case 0x003A: + featureDescription = Features.Prettify_003A(desc.Data); - saveFs.Close(); + break; + case 0x003B: + featureDescription = Features.Prettify_003B(desc.Data); + + break; + case 0x0040: + featureDescription = Features.Prettify_0040(desc.Data); + + break; + case 0x0041: + featureDescription = Features.Prettify_0041(desc.Data); + + break; + case 0x0042: + featureDescription = Features.Prettify_0042(desc.Data); + + break; + case 0x0050: + featureDescription = Features.Prettify_0050(desc.Data); + + break; + case 0x0051: + featureDescription = Features.Prettify_0051(desc.Data); + + break; + case 0x0080: + featureDescription = Features.Prettify_0080(desc.Data); + + break; + case 0x0100: + featureDescription = Features.Prettify_0100(desc.Data); + + break; + case 0x0101: + featureDescription = Features.Prettify_0101(desc.Data); + + break; + case 0x0102: + featureDescription = Features.Prettify_0102(desc.Data); + + break; + case 0x0103: + featureDescription = Features.Prettify_0103(desc.Data); + + break; + case 0x0104: + featureDescription = Features.Prettify_0104(desc.Data); + + break; + case 0x0105: + featureDescription = Features.Prettify_0105(desc.Data); + + break; + case 0x0106: + featureDescription = Features.Prettify_0106(desc.Data); + + break; + case 0x0107: + featureDescription = Features.Prettify_0107(desc.Data); + + break; + case 0x0108: + featureDescription = Features.Prettify_0108(desc.Data); + + break; + case 0x0109: + featureDescription = Features.Prettify_0109(desc.Data); + + break; + case 0x010A: + featureDescription = Features.Prettify_010A(desc.Data); + + break; + case 0x010B: + featureDescription = Features.Prettify_010B(desc.Data); + + break; + case 0x010C: + featureDescription = Features.Prettify_010C(desc.Data); + + break; + case 0x010D: + featureDescription = Features.Prettify_010D(desc.Data); + + break; + case 0x010E: + featureDescription = Features.Prettify_010E(desc.Data); + + break; + case 0x0110: + featureDescription = Features.Prettify_0110(desc.Data); + + break; + case 0x0113: + featureDescription = Features.Prettify_0113(desc.Data); + + break; + case 0x0142: + featureDescription = Features.Prettify_0142(desc.Data); + + break; + default: + featureDescription = "Unknown feature"; + + break; + } + + MmcFeatures.Add(new ScsiPageModel + { + Page = featureNumber, + Description = featureDescription + }); + } + else + AaruConsole.DebugWriteLine("Device-Info command", + "GET CONFIGURATION returned no feature descriptors"); } } + + public byte[] InquiryData { get; } + public string ScsiInquiryText { get; } + public ObservableCollection ModeSensePages { get; } + public ObservableCollection EvpdPages { get; } + public ObservableCollection MmcFeatures { get; } + public ReactiveCommand SaveInquiryBinaryCommand { get; } + public ReactiveCommand SaveInquiryTextCommand { get; } + public ReactiveCommand SaveModeSense6Command { get; } + public ReactiveCommand SaveModeSense10Command { get; } + public ReactiveCommand SaveEvpdPageCommand { get; } + public ReactiveCommand SaveMmcFeaturesCommand { get; } + + public object SelectedModeSensePage + { + get => _selectedModeSensePage; + set + { + if(value == _selectedModeSensePage) + return; + + if(value is ScsiPageModel pageModel) + ModeSensePageText = pageModel.Description; + + this.RaiseAndSetIfChanged(ref _selectedModeSensePage, value); + } + } + + public string ModeSensePageText + { + get => _scsiModeSensePageText; + set => this.RaiseAndSetIfChanged(ref _scsiModeSensePageText, value); + } + + public object SelectedEvpdPage + { + get => _selectedEvpdPage; + set + { + if(value == _selectedEvpdPage) + return; + + if(value is ScsiPageModel pageModel) + EvpdPageText = pageModel.Description; + + this.RaiseAndSetIfChanged(ref _selectedEvpdPage, value); + } + } + + public string EvpdPageText + { + get => _evpdPageText; + set => this.RaiseAndSetIfChanged(ref _evpdPageText, value); + } + + public object SelectedMmcFeature + { + get => _selectedMmcFeature; + set + { + if(value == _selectedMmcFeature) + return; + + if(value is ScsiPageModel pageModel) + MmcFeatureText = pageModel.Description; + + this.RaiseAndSetIfChanged(ref _selectedMmcFeature, value); + } + } + + public string MmcFeatureText + { + get => _mmcFeatureText; + set => this.RaiseAndSetIfChanged(ref _mmcFeatureText, value); + } + + async void ExecuteSaveInquiryBinaryCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(InquiryData, 0, InquiryData.Length); + + saveFs.Close(); + } + + async void ExecuteSaveInquiryTextCommand() + { + var dlgSaveText = new SaveFileDialog(); + + dlgSaveText.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.txt" + }), + Name = "Text" + }); + + string result = await dlgSaveText.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + var saveSw = new StreamWriter(saveFs); + saveSw.Write(ScsiInquiryText); + saveFs.Close(); + } + + async void ExecuteSaveModeSense6Command() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_scsiModeSense6, 0, _scsiModeSense6.Length); + + saveFs.Close(); + } + + async void ExecuteSaveModeSense10Command() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_scsiModeSense10, 0, _scsiModeSense10.Length); + + saveFs.Close(); + } + + async void ExecuteSaveEvpdPageCommand() + { + if(!(SelectedEvpdPage is ScsiPageModel pageModel)) + return; + + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(pageModel.Data, 0, pageModel.Data.Length); + + saveFs.Close(); + } + + async void ExecuteSaveMmcFeaturesCommand() + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(_configuration, 0, _configuration.Length); + + saveFs.Close(); + } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/SdMmcInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/SdMmcInfoViewModel.cs index 6777d9a9e..cfc9a182c 100644 --- a/Aaru.Gui/ViewModels/Tabs/SdMmcInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/SdMmcInfoViewModel.cs @@ -33,58 +33,57 @@ using Aaru.CommonTypes.Enums; using JetBrains.Annotations; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public sealed class SdMmcInfoViewModel { - public sealed class SdMmcInfoViewModel + public SdMmcInfoViewModel(DeviceType deviceType, [CanBeNull] byte[] cid, [CanBeNull] byte[] csd, + [CanBeNull] byte[] ocr, [CanBeNull] byte[] extendedCsd, [CanBeNull] byte[] scr) { - public SdMmcInfoViewModel(DeviceType deviceType, [CanBeNull] byte[] cid, [CanBeNull] byte[] csd, - [CanBeNull] byte[] ocr, [CanBeNull] byte[] extendedCsd, [CanBeNull] byte[] scr) + switch(deviceType) { - switch(deviceType) + case DeviceType.MMC: { - case DeviceType.MMC: - { - //Text = "MultiMediaCard"; + //Text = "MultiMediaCard"; - if(cid != null) - CidText = Decoders.MMC.Decoders.PrettifyCID(cid); + if(cid != null) + CidText = Decoders.MMC.Decoders.PrettifyCID(cid); - if(csd != null) - CsdText = Decoders.MMC.Decoders.PrettifyCSD(csd); + if(csd != null) + CsdText = Decoders.MMC.Decoders.PrettifyCSD(csd); - if(ocr != null) - OcrText = Decoders.MMC.Decoders.PrettifyOCR(ocr); + if(ocr != null) + OcrText = Decoders.MMC.Decoders.PrettifyOCR(ocr); - if(extendedCsd != null) - ExtendedCsdText = Decoders.MMC.Decoders.PrettifyExtendedCSD(extendedCsd); - } - - break; - case DeviceType.SecureDigital: - { - //Text = "SecureDigital"; - - if(cid != null) - CidText = Decoders.SecureDigital.Decoders.PrettifyCID(cid); - - if(csd != null) - CsdText = Decoders.SecureDigital.Decoders.PrettifyCSD(csd); - - if(ocr != null) - OcrText = Decoders.SecureDigital.Decoders.PrettifyOCR(ocr); - - if(scr != null) - ScrText = Decoders.SecureDigital.Decoders.PrettifySCR(scr); - } - - break; + if(extendedCsd != null) + ExtendedCsdText = Decoders.MMC.Decoders.PrettifyExtendedCSD(extendedCsd); } - } - public string CidText { get; } - public string CsdText { get; } - public string OcrText { get; } - public string ExtendedCsdText { get; } - public string ScrText { get; } + break; + case DeviceType.SecureDigital: + { + //Text = "SecureDigital"; + + if(cid != null) + CidText = Decoders.SecureDigital.Decoders.PrettifyCID(cid); + + if(csd != null) + CsdText = Decoders.SecureDigital.Decoders.PrettifyCSD(csd); + + if(ocr != null) + OcrText = Decoders.SecureDigital.Decoders.PrettifyOCR(ocr); + + if(scr != null) + ScrText = Decoders.SecureDigital.Decoders.PrettifySCR(scr); + } + + break; + } } + + public string CidText { get; } + public string CsdText { get; } + public string OcrText { get; } + public string ExtendedCsdText { get; } + public string ScrText { get; } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Tabs/XboxInfoViewModel.cs b/Aaru.Gui/ViewModels/Tabs/XboxInfoViewModel.cs index c26d03630..1888559c7 100644 --- a/Aaru.Gui/ViewModels/Tabs/XboxInfoViewModel.cs +++ b/Aaru.Gui/ViewModels/Tabs/XboxInfoViewModel.cs @@ -39,81 +39,80 @@ using Avalonia.Controls; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Tabs +namespace Aaru.Gui.ViewModels.Tabs; + +public sealed class XboxInfoViewModel { - public sealed class XboxInfoViewModel + readonly Window _view; + readonly byte[] _xboxSecuritySector; + + public XboxInfoViewModel([CanBeNull] XgdInfo xgdInfo, [CanBeNull] byte[] dmi, [CanBeNull] byte[] securitySector, + SS.SecuritySector? decodedSecuritySector, Window view) { - readonly Window _view; - readonly byte[] _xboxSecuritySector; + _xboxSecuritySector = securitySector; + _view = view; + SaveXboxSsCommand = ReactiveCommand.Create(ExecuteSaveXboxSsCommand); - public XboxInfoViewModel([CanBeNull] XgdInfo xgdInfo, [CanBeNull] byte[] dmi, [CanBeNull] byte[] securitySector, - SS.SecuritySector? decodedSecuritySector, Window view) + if(xgdInfo != null) { - _xboxSecuritySector = securitySector; - _view = view; - SaveXboxSsCommand = ReactiveCommand.Create(ExecuteSaveXboxSsCommand); - - if(xgdInfo != null) - { - XboxInformationVisible = true; - XboxL0VideoText = $"{xgdInfo.L0Video} sectors"; - XboxL1VideoText = $"{xgdInfo.L1Video} sectors"; - XboxMiddleZoneText = $"{xgdInfo.MiddleZone} sectors"; - XboxGameSizeText = $"{xgdInfo.GameSize} sectors"; - XboxTotalSizeText = $"{xgdInfo.TotalSize} sectors"; - XboxRealBreakText = xgdInfo.LayerBreak.ToString(); - } - - if(dmi != null) - { - if(DMI.IsXbox(dmi)) - XboxDmiText = DMI.PrettifyXbox(dmi); - else if(DMI.IsXbox360(dmi)) - XboxDmiText = DMI.PrettifyXbox360(dmi); - } - - if(decodedSecuritySector.HasValue) - XboxSsText = SS.Prettify(decodedSecuritySector); - - SaveXboxSsVisible = securitySector != null; + XboxInformationVisible = true; + XboxL0VideoText = $"{xgdInfo.L0Video} sectors"; + XboxL1VideoText = $"{xgdInfo.L1Video} sectors"; + XboxMiddleZoneText = $"{xgdInfo.MiddleZone} sectors"; + XboxGameSizeText = $"{xgdInfo.GameSize} sectors"; + XboxTotalSizeText = $"{xgdInfo.TotalSize} sectors"; + XboxRealBreakText = xgdInfo.LayerBreak.ToString(); } - public ReactiveCommand SaveXboxSsCommand { get; } - public bool XboxInformationVisible { get; } - public bool SaveXboxSsVisible { get; } - public string XboxL0VideoText { get; } - public string XboxL1VideoText { get; } - public string XboxMiddleZoneText { get; } - public string XboxGameSizeText { get; } - public string XboxTotalSizeText { get; } - public string XboxRealBreakText { get; } - public string XboxDmiText { get; } - public string XboxSsText { get; } - - async void SaveElement(byte[] data) + if(dmi != null) { - var dlgSaveBinary = new SaveFileDialog(); - - dlgSaveBinary.Filters.Add(new FileDialogFilter - { - Extensions = new List(new[] - { - "*.bin" - }), - Name = "Binary" - }); - - string result = await dlgSaveBinary.ShowAsync(_view); - - if(result is null) - return; - - var saveFs = new FileStream(result, FileMode.Create); - saveFs.Write(data, 0, data.Length); - - saveFs.Close(); + if(DMI.IsXbox(dmi)) + XboxDmiText = DMI.PrettifyXbox(dmi); + else if(DMI.IsXbox360(dmi)) + XboxDmiText = DMI.PrettifyXbox360(dmi); } - public void ExecuteSaveXboxSsCommand() => SaveElement(_xboxSecuritySector); + if(decodedSecuritySector.HasValue) + XboxSsText = SS.Prettify(decodedSecuritySector); + + SaveXboxSsVisible = securitySector != null; } + + public ReactiveCommand SaveXboxSsCommand { get; } + public bool XboxInformationVisible { get; } + public bool SaveXboxSsVisible { get; } + public string XboxL0VideoText { get; } + public string XboxL1VideoText { get; } + public string XboxMiddleZoneText { get; } + public string XboxGameSizeText { get; } + public string XboxTotalSizeText { get; } + public string XboxRealBreakText { get; } + public string XboxDmiText { get; } + public string XboxSsText { get; } + + async void SaveElement(byte[] data) + { + var dlgSaveBinary = new SaveFileDialog(); + + dlgSaveBinary.Filters.Add(new FileDialogFilter + { + Extensions = new List(new[] + { + "*.bin" + }), + Name = "Binary" + }); + + string result = await dlgSaveBinary.ShowAsync(_view); + + if(result is null) + return; + + var saveFs = new FileStream(result, FileMode.Create); + saveFs.Write(data, 0, data.Length); + + saveFs.Close(); + } + + public void ExecuteSaveXboxSsCommand() => SaveElement(_xboxSecuritySector); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/ViewModelBase.cs b/Aaru.Gui/ViewModels/ViewModelBase.cs index 5c8e01bee..e54d944f8 100644 --- a/Aaru.Gui/ViewModels/ViewModelBase.cs +++ b/Aaru.Gui/ViewModels/ViewModelBase.cs @@ -32,7 +32,6 @@ using ReactiveUI; -namespace Aaru.Gui.ViewModels -{ - public class ViewModelBase : ReactiveObject {} -} \ No newline at end of file +namespace Aaru.Gui.ViewModels; + +public class ViewModelBase : ReactiveObject {} \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/DecodeMediaTagsViewModel.cs b/Aaru.Gui/ViewModels/Windows/DecodeMediaTagsViewModel.cs index 534aa2e62..7a344943a 100644 --- a/Aaru.Gui/ViewModels/Windows/DecodeMediaTagsViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/DecodeMediaTagsViewModel.cs @@ -54,199 +54,198 @@ using DMI = Aaru.Decoders.Xbox.DMI; using Inquiry = Aaru.Decoders.SCSI.Inquiry; using Spare = Aaru.Decoders.DVD.Spare; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class DecodeMediaTagsViewModel : ViewModelBase { - public sealed class DecodeMediaTagsViewModel : ViewModelBase + const int HEX_COLUMNS = 32; + readonly MediaType _mediaType; + string _decodedText; + bool _decodedVisible; + string _hexViewText; + MediaTagModel _selectedTag; + + public DecodeMediaTagsViewModel([NotNull] IMediaImage inputFormat) { - const int HEX_COLUMNS = 32; - readonly MediaType _mediaType; - string _decodedText; - bool _decodedVisible; - string _hexViewText; - MediaTagModel _selectedTag; + TagsList = new ObservableCollection(); - public DecodeMediaTagsViewModel([NotNull] IMediaImage inputFormat) + _mediaType = inputFormat.Info.MediaType; + + foreach(MediaTagType tag in inputFormat.Info.ReadableMediaTags) { - TagsList = new ObservableCollection(); + ErrorNumber errno = inputFormat.ReadMediaTag(tag, out byte[] data); - _mediaType = inputFormat.Info.MediaType; - - foreach(MediaTagType tag in inputFormat.Info.ReadableMediaTags) - { - ErrorNumber errno = inputFormat.ReadMediaTag(tag, out byte[] data); - - if(errno == ErrorNumber.NoError) - TagsList.Add(new MediaTagModel - { - Tag = tag, - Data = data - }); - } - } - - public string Title { get; } - public ObservableCollection TagsList { get; } - - public MediaTagModel SelectedTag - { - get => _selectedTag; - set - { - this.RaiseAndSetIfChanged(ref _selectedTag, value); - - if(value is null) - return; - - // TODO: Decoders should be able to handle tags with/without length header - HexViewText = PrintHex.ByteArrayToHexArrayString(value.Data, HEX_COLUMNS); - DecodedVisible = true; - - if(value.Decoded != null) + if(errno == ErrorNumber.NoError) + TagsList.Add(new MediaTagModel { - DecodedText = value.Decoded; - - return; - } - - switch(value.Tag) - { - case MediaTagType.CD_TOC: - DecodedText = TOC.Prettify(value.Data); - - break; - case MediaTagType.CD_SessionInfo: - DecodedText = Session.Prettify(value.Data); - - break; - case MediaTagType.CD_FullTOC: - DecodedText = FullTOC.Prettify(value.Data); - - break; - case MediaTagType.CD_PMA: - DecodedText = PMA.Prettify(value.Data); - - break; - case MediaTagType.CD_ATIP: - DecodedText = ATIP.Prettify(value.Data); - - break; - case MediaTagType.CD_TEXT: - DecodedText = CDTextOnLeadIn.Prettify(value.Data); - - break; - case MediaTagType.CD_MCN: - DecodedText = Encoding.ASCII.GetString(value.Data); - - break; - case MediaTagType.DVD_PFI: - DecodedText = PFI.Prettify(value.Data, _mediaType); - - break; - case MediaTagType.DVD_CMI: - DecodedText = CSS_CPRM.PrettifyLeadInCopyright(value.Data); - - break; - case MediaTagType.DVDRAM_DDS: - DecodedText = DDS.Prettify(value.Data); - - break; - case MediaTagType.DVDRAM_SpareArea: - DecodedText = Spare.Prettify(value.Data); - - break; - case MediaTagType.DVDR_PFI: - DecodedText = PFI.Prettify(value.Data, _mediaType); - - break; - case MediaTagType.HDDVD_MediumStatus: - DecodedText = PFI.Prettify(value.Data, _mediaType); - - break; - case MediaTagType.BD_DI: - DecodedText = DI.Prettify(value.Data); - - break; - case MediaTagType.BD_BCA: - DecodedText = BCA.Prettify(value.Data); - - break; - case MediaTagType.BD_DDS: - DecodedText = Decoders.Bluray.DDS.Prettify(value.Data); - - break; - case MediaTagType.BD_CartridgeStatus: - DecodedText = Cartridge.Prettify(value.Data); - - break; - case MediaTagType.BD_SpareArea: - DecodedText = Decoders.Bluray.Spare.Prettify(value.Data); - - break; - case MediaTagType.MMC_WriteProtection: - DecodedText = WriteProtect.PrettifyWriteProtectionStatus(value.Data); - - break; - case MediaTagType.MMC_DiscInformation: - DecodedText = DiscInformation.Prettify(value.Data); - - break; - case MediaTagType.SCSI_INQUIRY: - DecodedText = Inquiry.Prettify(value.Data); - - break; - case MediaTagType.SCSI_MODEPAGE_2A: - DecodedText = Modes.PrettifyModePage_2A(value.Data); - - break; - case MediaTagType.ATA_IDENTIFY: - case MediaTagType.ATAPI_IDENTIFY: - DecodedText = Identify.Prettify(value.Data); - - break; - case MediaTagType.Xbox_SecuritySector: - DecodedText = SS.Prettify(value.Data); - - break; - case MediaTagType.SCSI_MODESENSE_6: - DecodedText = Modes.PrettifyModeHeader6(value.Data, PeripheralDeviceTypes.DirectAccess); - - break; - case MediaTagType.SCSI_MODESENSE_10: - DecodedText = Modes.PrettifyModeHeader10(value.Data, PeripheralDeviceTypes.DirectAccess); - - break; - case MediaTagType.Xbox_DMI: - DecodedText = DMI.IsXbox360(value.Data) ? DMI.PrettifyXbox360(value.Data) - : DMI.PrettifyXbox(value.Data); - - break; - default: - DecodedVisible = false; - - break; - } - - if(DecodedText != null) - value.Decoded = DecodedText; - } - } - - public string HexViewText - { - get => _hexViewText; - set => this.RaiseAndSetIfChanged(ref _hexViewText, value); - } - - public bool DecodedVisible - { - get => _decodedVisible; - set => this.RaiseAndSetIfChanged(ref _decodedVisible, value); - } - - public string DecodedText - { - get => _decodedText; - set => this.RaiseAndSetIfChanged(ref _decodedText, value); + Tag = tag, + Data = data + }); } } + + public string Title { get; } + public ObservableCollection TagsList { get; } + + public MediaTagModel SelectedTag + { + get => _selectedTag; + set + { + this.RaiseAndSetIfChanged(ref _selectedTag, value); + + if(value is null) + return; + + // TODO: Decoders should be able to handle tags with/without length header + HexViewText = PrintHex.ByteArrayToHexArrayString(value.Data, HEX_COLUMNS); + DecodedVisible = true; + + if(value.Decoded != null) + { + DecodedText = value.Decoded; + + return; + } + + switch(value.Tag) + { + case MediaTagType.CD_TOC: + DecodedText = TOC.Prettify(value.Data); + + break; + case MediaTagType.CD_SessionInfo: + DecodedText = Session.Prettify(value.Data); + + break; + case MediaTagType.CD_FullTOC: + DecodedText = FullTOC.Prettify(value.Data); + + break; + case MediaTagType.CD_PMA: + DecodedText = PMA.Prettify(value.Data); + + break; + case MediaTagType.CD_ATIP: + DecodedText = ATIP.Prettify(value.Data); + + break; + case MediaTagType.CD_TEXT: + DecodedText = CDTextOnLeadIn.Prettify(value.Data); + + break; + case MediaTagType.CD_MCN: + DecodedText = Encoding.ASCII.GetString(value.Data); + + break; + case MediaTagType.DVD_PFI: + DecodedText = PFI.Prettify(value.Data, _mediaType); + + break; + case MediaTagType.DVD_CMI: + DecodedText = CSS_CPRM.PrettifyLeadInCopyright(value.Data); + + break; + case MediaTagType.DVDRAM_DDS: + DecodedText = DDS.Prettify(value.Data); + + break; + case MediaTagType.DVDRAM_SpareArea: + DecodedText = Spare.Prettify(value.Data); + + break; + case MediaTagType.DVDR_PFI: + DecodedText = PFI.Prettify(value.Data, _mediaType); + + break; + case MediaTagType.HDDVD_MediumStatus: + DecodedText = PFI.Prettify(value.Data, _mediaType); + + break; + case MediaTagType.BD_DI: + DecodedText = DI.Prettify(value.Data); + + break; + case MediaTagType.BD_BCA: + DecodedText = BCA.Prettify(value.Data); + + break; + case MediaTagType.BD_DDS: + DecodedText = Decoders.Bluray.DDS.Prettify(value.Data); + + break; + case MediaTagType.BD_CartridgeStatus: + DecodedText = Cartridge.Prettify(value.Data); + + break; + case MediaTagType.BD_SpareArea: + DecodedText = Decoders.Bluray.Spare.Prettify(value.Data); + + break; + case MediaTagType.MMC_WriteProtection: + DecodedText = WriteProtect.PrettifyWriteProtectionStatus(value.Data); + + break; + case MediaTagType.MMC_DiscInformation: + DecodedText = DiscInformation.Prettify(value.Data); + + break; + case MediaTagType.SCSI_INQUIRY: + DecodedText = Inquiry.Prettify(value.Data); + + break; + case MediaTagType.SCSI_MODEPAGE_2A: + DecodedText = Modes.PrettifyModePage_2A(value.Data); + + break; + case MediaTagType.ATA_IDENTIFY: + case MediaTagType.ATAPI_IDENTIFY: + DecodedText = Identify.Prettify(value.Data); + + break; + case MediaTagType.Xbox_SecuritySector: + DecodedText = SS.Prettify(value.Data); + + break; + case MediaTagType.SCSI_MODESENSE_6: + DecodedText = Modes.PrettifyModeHeader6(value.Data, PeripheralDeviceTypes.DirectAccess); + + break; + case MediaTagType.SCSI_MODESENSE_10: + DecodedText = Modes.PrettifyModeHeader10(value.Data, PeripheralDeviceTypes.DirectAccess); + + break; + case MediaTagType.Xbox_DMI: + DecodedText = DMI.IsXbox360(value.Data) ? DMI.PrettifyXbox360(value.Data) + : DMI.PrettifyXbox(value.Data); + + break; + default: + DecodedVisible = false; + + break; + } + + if(DecodedText != null) + value.Decoded = DecodedText; + } + } + + public string HexViewText + { + get => _hexViewText; + set => this.RaiseAndSetIfChanged(ref _hexViewText, value); + } + + public bool DecodedVisible + { + get => _decodedVisible; + set => this.RaiseAndSetIfChanged(ref _decodedVisible, value); + } + + public string DecodedText + { + get => _decodedText; + set => this.RaiseAndSetIfChanged(ref _decodedText, value); + } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/ImageChecksumViewModel.cs b/Aaru.Gui/ViewModels/Windows/ImageChecksumViewModel.cs index 0c3dca47b..9c6008d03 100644 --- a/Aaru.Gui/ViewModels/Windows/ImageChecksumViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/ImageChecksumViewModel.cs @@ -45,554 +45,425 @@ using Avalonia.Threading; using ReactiveUI; using Schemas; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class ImageChecksumViewModel : ViewModelBase { - public sealed class ImageChecksumViewModel : ViewModelBase + // How many sectors to read at once + const uint SECTORS_TO_READ = 256; + readonly IMediaImage _inputFormat; + readonly Window _view; + bool _adler32Checked; + bool _cancel; + bool _checksumMediaChecked; + bool _checksumTracksChecked; + bool _checksumTracksVisible; + bool _closeCommandEnabled; + bool _closeCommandVisible; + bool _crc16Checked; + bool _crc32Checked; + bool _crc64Checked; + bool _fletcher16Checked; + bool _fletcher32Checked; + bool _md5Checked; + bool _mediaChecksumsVisible; + bool _optionsEnabled; + bool _progress1Visible; + double _progress2Max; + string _progress2Text; + double _progress2Value; + bool _progress2Visible; + double _progressMax; + string _progressText; + double _progressValue; + bool _progressVisible; + bool _resultsVisible; + bool _sha1Checked; + bool _sha256Checked; + bool _sha384Checked; + bool _sha512Checked; + bool _spamsumChecked; + bool _startCommandEnabled; + bool _startCommandVisible; + bool _stopCommandEnabled; + bool _stopCommandVisible; + string _title; + bool _trackChecksumsVisible; + + public ImageChecksumViewModel(IMediaImage inputFormat, Window view) { - // How many sectors to read at once - const uint SECTORS_TO_READ = 256; - readonly IMediaImage _inputFormat; - readonly Window _view; - bool _adler32Checked; - bool _cancel; - bool _checksumMediaChecked; - bool _checksumTracksChecked; - bool _checksumTracksVisible; - bool _closeCommandEnabled; - bool _closeCommandVisible; - bool _crc16Checked; - bool _crc32Checked; - bool _crc64Checked; - bool _fletcher16Checked; - bool _fletcher32Checked; - bool _md5Checked; - bool _mediaChecksumsVisible; - bool _optionsEnabled; - bool _progress1Visible; - double _progress2Max; - string _progress2Text; - double _progress2Value; - bool _progress2Visible; - double _progressMax; - string _progressText; - double _progressValue; - bool _progressVisible; - bool _resultsVisible; - bool _sha1Checked; - bool _sha256Checked; - bool _sha384Checked; - bool _sha512Checked; - bool _spamsumChecked; - bool _startCommandEnabled; - bool _startCommandVisible; - bool _stopCommandEnabled; - bool _stopCommandVisible; - string _title; - bool _trackChecksumsVisible; + _view = view; + _cancel = false; + _inputFormat = inputFormat; + ChecksumTracksChecked = ChecksumTracksVisible; + OptionsEnabled = true; + ChecksumMediaChecked = true; + ChecksumTracksChecked = true; + Adler32Checked = true; + Crc16Checked = true; + Crc32Checked = true; + Md5Checked = true; + Sha1Checked = true; + SpamsumChecked = true; + TrackChecksums = new ObservableCollection(); + MediaChecksums = new ObservableCollection(); + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StopCommand = ReactiveCommand.Create(ExecuteStopCommand); + StopCommandVisible = false; + StartCommandVisible = true; + CloseCommandVisible = true; + StopCommandEnabled = true; + StartCommandEnabled = true; + CloseCommandEnabled = true; - public ImageChecksumViewModel(IMediaImage inputFormat, Window view) + try { - _view = view; - _cancel = false; - _inputFormat = inputFormat; - ChecksumTracksChecked = ChecksumTracksVisible; - OptionsEnabled = true; - ChecksumMediaChecked = true; - ChecksumTracksChecked = true; - Adler32Checked = true; - Crc16Checked = true; - Crc32Checked = true; - Md5Checked = true; - Sha1Checked = true; - SpamsumChecked = true; - TrackChecksums = new ObservableCollection(); - MediaChecksums = new ObservableCollection(); - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StopCommand = ReactiveCommand.Create(ExecuteStopCommand); - StopCommandVisible = false; - StartCommandVisible = true; - CloseCommandVisible = true; - StopCommandEnabled = true; - StartCommandEnabled = true; - CloseCommandEnabled = true; + ChecksumTracksVisible = (inputFormat as IOpticalMediaImage)?.Tracks?.Count > 0; + } + catch + { + ChecksumTracksVisible = false; + } + } + public string Title + { + get => _title; + set => this.RaiseAndSetIfChanged(ref _title, value); + } + + public bool OptionsEnabled + { + get => _optionsEnabled; + set => this.RaiseAndSetIfChanged(ref _optionsEnabled, value); + } + + public bool ChecksumMediaChecked + { + get => _checksumMediaChecked; + set => this.RaiseAndSetIfChanged(ref _checksumMediaChecked, value); + } + + public bool ChecksumTracksChecked + { + get => _checksumTracksChecked; + set => this.RaiseAndSetIfChanged(ref _checksumTracksChecked, value); + } + + public bool Adler32Checked + { + get => _adler32Checked; + set => this.RaiseAndSetIfChanged(ref _adler32Checked, value); + } + + public bool Crc16Checked + { + get => _crc16Checked; + set => this.RaiseAndSetIfChanged(ref _crc16Checked, value); + } + + public bool Crc32Checked + { + get => _crc32Checked; + set => this.RaiseAndSetIfChanged(ref _crc32Checked, value); + } + + public bool Crc64Checked + { + get => _crc64Checked; + set => this.RaiseAndSetIfChanged(ref _crc64Checked, value); + } + + public bool Fletcher16Checked + { + get => _fletcher16Checked; + set => this.RaiseAndSetIfChanged(ref _fletcher16Checked, value); + } + + public bool Fletcher32Checked + { + get => _fletcher32Checked; + set => this.RaiseAndSetIfChanged(ref _fletcher32Checked, value); + } + + public bool Md5Checked + { + get => _md5Checked; + set => this.RaiseAndSetIfChanged(ref _md5Checked, value); + } + + public bool Sha1Checked + { + get => _sha1Checked; + set => this.RaiseAndSetIfChanged(ref _sha1Checked, value); + } + + public bool Sha256Checked + { + get => _sha256Checked; + set => this.RaiseAndSetIfChanged(ref _sha256Checked, value); + } + + public bool Sha384Checked + { + get => _sha384Checked; + set => this.RaiseAndSetIfChanged(ref _sha384Checked, value); + } + + public bool Sha512Checked + { + get => _sha512Checked; + set => this.RaiseAndSetIfChanged(ref _sha512Checked, value); + } + + public bool SpamsumChecked + { + get => _spamsumChecked; + set => this.RaiseAndSetIfChanged(ref _spamsumChecked, value); + } + + public bool ResultsVisible + { + get => _resultsVisible; + set => this.RaiseAndSetIfChanged(ref _resultsVisible, value); + } + + public bool TrackChecksumsVisible + { + get => _trackChecksumsVisible; + set => this.RaiseAndSetIfChanged(ref _trackChecksumsVisible, value); + } + + public bool MediaChecksumsVisible + { + get => _mediaChecksumsVisible; + set => this.RaiseAndSetIfChanged(ref _mediaChecksumsVisible, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public bool Progress1Visible + { + get => _progress1Visible; + set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); + } + + public string ProgressText + { + get => _progressText; + set => this.RaiseAndSetIfChanged(ref _progressText, value); + } + + public double ProgressMax + { + get => _progressMax; + set => this.RaiseAndSetIfChanged(ref _progressMax, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } + + public string Progress2Text + { + get => _progress2Text; + set => this.RaiseAndSetIfChanged(ref _progress2Text, value); + } + + public double Progress2Max + { + get => _progress2Max; + set => this.RaiseAndSetIfChanged(ref _progress2Max, value); + } + + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } + + public bool StartCommandEnabled + { + get => _startCommandEnabled; + set => this.RaiseAndSetIfChanged(ref _startCommandEnabled, value); + } + + public bool StartCommandVisible + { + get => _startCommandVisible; + set => this.RaiseAndSetIfChanged(ref _startCommandVisible, value); + } + + public bool CloseCommandEnabled + { + get => _closeCommandEnabled; + set => this.RaiseAndSetIfChanged(ref _closeCommandEnabled, value); + } + + public bool CloseCommandVisible + { + get => _closeCommandVisible; + set => this.RaiseAndSetIfChanged(ref _closeCommandVisible, value); + } + + public bool StopCommandEnabled + { + get => _stopCommandEnabled; + set => this.RaiseAndSetIfChanged(ref _stopCommandEnabled, value); + } + + public bool StopCommandVisible + { + get => _stopCommandVisible; + set => this.RaiseAndSetIfChanged(ref _stopCommandVisible, value); + } + + public bool ChecksumTracksVisible + { + get => _checksumTracksVisible; + set => this.RaiseAndSetIfChanged(ref _stopCommandVisible, value); + } + + public ObservableCollection TrackChecksums { get; } + public ObservableCollection MediaChecksums { get; } + public ReactiveCommand StartCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StopCommand { get; } + + void ExecuteStartCommand() + { + OptionsEnabled = false; + CloseCommandVisible = false; + StartCommandVisible = false; + StopCommandVisible = true; + ProgressVisible = true; + Progress1Visible = true; + Progress2Visible = false; + + new Thread(DoWork) + { + Priority = ThreadPriority.BelowNormal + }.Start(); + } + + void ExecuteCloseCommand() => _view.Close(); + + internal void ExecuteStopCommand() + { + _cancel = true; + StopCommandEnabled = false; + } + + async void DoWork() + { + var opticalMediaImage = _inputFormat as IOpticalMediaImage; + bool formatHasTracks = false; + + if(opticalMediaImage != null) try { - ChecksumTracksVisible = (inputFormat as IOpticalMediaImage)?.Tracks?.Count > 0; + formatHasTracks = opticalMediaImage.Tracks?.Count > 0; } catch { - ChecksumTracksVisible = false; + formatHasTracks = false; } - } - public string Title + // Setup progress bars + await Dispatcher.UIThread.InvokeAsync(() => { - get => _title; - set => this.RaiseAndSetIfChanged(ref _title, value); - } + ProgressVisible = true; + Progress1Visible = true; + Progress2Visible = true; + ProgressMax = 1; + Progress2Max = (int)(_inputFormat.Info.Sectors / SECTORS_TO_READ); - public bool OptionsEnabled - { - get => _optionsEnabled; - set => this.RaiseAndSetIfChanged(ref _optionsEnabled, value); - } - - public bool ChecksumMediaChecked - { - get => _checksumMediaChecked; - set => this.RaiseAndSetIfChanged(ref _checksumMediaChecked, value); - } - - public bool ChecksumTracksChecked - { - get => _checksumTracksChecked; - set => this.RaiseAndSetIfChanged(ref _checksumTracksChecked, value); - } - - public bool Adler32Checked - { - get => _adler32Checked; - set => this.RaiseAndSetIfChanged(ref _adler32Checked, value); - } - - public bool Crc16Checked - { - get => _crc16Checked; - set => this.RaiseAndSetIfChanged(ref _crc16Checked, value); - } - - public bool Crc32Checked - { - get => _crc32Checked; - set => this.RaiseAndSetIfChanged(ref _crc32Checked, value); - } - - public bool Crc64Checked - { - get => _crc64Checked; - set => this.RaiseAndSetIfChanged(ref _crc64Checked, value); - } - - public bool Fletcher16Checked - { - get => _fletcher16Checked; - set => this.RaiseAndSetIfChanged(ref _fletcher16Checked, value); - } - - public bool Fletcher32Checked - { - get => _fletcher32Checked; - set => this.RaiseAndSetIfChanged(ref _fletcher32Checked, value); - } - - public bool Md5Checked - { - get => _md5Checked; - set => this.RaiseAndSetIfChanged(ref _md5Checked, value); - } - - public bool Sha1Checked - { - get => _sha1Checked; - set => this.RaiseAndSetIfChanged(ref _sha1Checked, value); - } - - public bool Sha256Checked - { - get => _sha256Checked; - set => this.RaiseAndSetIfChanged(ref _sha256Checked, value); - } - - public bool Sha384Checked - { - get => _sha384Checked; - set => this.RaiseAndSetIfChanged(ref _sha384Checked, value); - } - - public bool Sha512Checked - { - get => _sha512Checked; - set => this.RaiseAndSetIfChanged(ref _sha512Checked, value); - } - - public bool SpamsumChecked - { - get => _spamsumChecked; - set => this.RaiseAndSetIfChanged(ref _spamsumChecked, value); - } - - public bool ResultsVisible - { - get => _resultsVisible; - set => this.RaiseAndSetIfChanged(ref _resultsVisible, value); - } - - public bool TrackChecksumsVisible - { - get => _trackChecksumsVisible; - set => this.RaiseAndSetIfChanged(ref _trackChecksumsVisible, value); - } - - public bool MediaChecksumsVisible - { - get => _mediaChecksumsVisible; - set => this.RaiseAndSetIfChanged(ref _mediaChecksumsVisible, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public bool Progress1Visible - { - get => _progress1Visible; - set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); - } - - public string ProgressText - { - get => _progressText; - set => this.RaiseAndSetIfChanged(ref _progressText, value); - } - - public double ProgressMax - { - get => _progressMax; - set => this.RaiseAndSetIfChanged(ref _progressMax, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } - - public string Progress2Text - { - get => _progress2Text; - set => this.RaiseAndSetIfChanged(ref _progress2Text, value); - } - - public double Progress2Max - { - get => _progress2Max; - set => this.RaiseAndSetIfChanged(ref _progress2Max, value); - } - - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } - - public bool StartCommandEnabled - { - get => _startCommandEnabled; - set => this.RaiseAndSetIfChanged(ref _startCommandEnabled, value); - } - - public bool StartCommandVisible - { - get => _startCommandVisible; - set => this.RaiseAndSetIfChanged(ref _startCommandVisible, value); - } - - public bool CloseCommandEnabled - { - get => _closeCommandEnabled; - set => this.RaiseAndSetIfChanged(ref _closeCommandEnabled, value); - } - - public bool CloseCommandVisible - { - get => _closeCommandVisible; - set => this.RaiseAndSetIfChanged(ref _closeCommandVisible, value); - } - - public bool StopCommandEnabled - { - get => _stopCommandEnabled; - set => this.RaiseAndSetIfChanged(ref _stopCommandEnabled, value); - } - - public bool StopCommandVisible - { - get => _stopCommandVisible; - set => this.RaiseAndSetIfChanged(ref _stopCommandVisible, value); - } - - public bool ChecksumTracksVisible - { - get => _checksumTracksVisible; - set => this.RaiseAndSetIfChanged(ref _stopCommandVisible, value); - } - - public ObservableCollection TrackChecksums { get; } - public ObservableCollection MediaChecksums { get; } - public ReactiveCommand StartCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StopCommand { get; } - - void ExecuteStartCommand() - { - OptionsEnabled = false; - CloseCommandVisible = false; - StartCommandVisible = false; - StopCommandVisible = true; - ProgressVisible = true; - Progress1Visible = true; - Progress2Visible = false; - - new Thread(DoWork) + if(formatHasTracks && + ChecksumTracksChecked && + opticalMediaImage != null) + ProgressMax += opticalMediaImage.Tracks.Count; + else { - Priority = ThreadPriority.BelowNormal - }.Start(); - } + ProgressMax = 2; + Progress2Visible = false; + } + }); - void ExecuteCloseCommand() => _view.Close(); + var enabledChecksums = new EnableChecksum(); - internal void ExecuteStopCommand() - { - _cancel = true; - StopCommandEnabled = false; - } + if(Adler32Checked) + enabledChecksums |= EnableChecksum.Adler32; - async void DoWork() - { - var opticalMediaImage = _inputFormat as IOpticalMediaImage; - bool formatHasTracks = false; + if(Crc16Checked) + enabledChecksums |= EnableChecksum.Crc16; - if(opticalMediaImage != null) - try - { - formatHasTracks = opticalMediaImage.Tracks?.Count > 0; - } - catch - { - formatHasTracks = false; - } + if(Crc32Checked) + enabledChecksums |= EnableChecksum.Crc32; - // Setup progress bars - await Dispatcher.UIThread.InvokeAsync(() => + if(Crc64Checked) + enabledChecksums |= EnableChecksum.Crc64; + + if(Md5Checked) + enabledChecksums |= EnableChecksum.Md5; + + if(Sha1Checked) + enabledChecksums |= EnableChecksum.Sha1; + + if(Sha256Checked) + enabledChecksums |= EnableChecksum.Sha256; + + if(Sha384Checked) + enabledChecksums |= EnableChecksum.Sha384; + + if(Sha512Checked) + enabledChecksums |= EnableChecksum.Sha512; + + if(SpamsumChecked) + enabledChecksums |= EnableChecksum.SpamSum; + + if(Fletcher16Checked) + enabledChecksums |= EnableChecksum.Fletcher16; + + if(Fletcher32Checked) + enabledChecksums |= EnableChecksum.Fletcher32; + + Checksum mediaChecksum = null; + ErrorNumber errno; + + if(opticalMediaImage?.Tracks != null) + try { - ProgressVisible = true; - Progress1Visible = true; - Progress2Visible = true; - ProgressMax = 1; - Progress2Max = (int)(_inputFormat.Info.Sectors / SECTORS_TO_READ); + Checksum trackChecksum = null; - if(formatHasTracks && - ChecksumTracksChecked && - opticalMediaImage != null) - ProgressMax += opticalMediaImage.Tracks.Count; - else + if(ChecksumMediaChecked) + mediaChecksum = new Checksum(enabledChecksums); + + ulong previousTrackEnd = 0; + + foreach(Track currentTrack in opticalMediaImage.Tracks) { - ProgressMax = 2; - Progress2Visible = false; - } - }); - - var enabledChecksums = new EnableChecksum(); - - if(Adler32Checked) - enabledChecksums |= EnableChecksum.Adler32; - - if(Crc16Checked) - enabledChecksums |= EnableChecksum.Crc16; - - if(Crc32Checked) - enabledChecksums |= EnableChecksum.Crc32; - - if(Crc64Checked) - enabledChecksums |= EnableChecksum.Crc64; - - if(Md5Checked) - enabledChecksums |= EnableChecksum.Md5; - - if(Sha1Checked) - enabledChecksums |= EnableChecksum.Sha1; - - if(Sha256Checked) - enabledChecksums |= EnableChecksum.Sha256; - - if(Sha384Checked) - enabledChecksums |= EnableChecksum.Sha384; - - if(Sha512Checked) - enabledChecksums |= EnableChecksum.Sha512; - - if(SpamsumChecked) - enabledChecksums |= EnableChecksum.SpamSum; - - if(Fletcher16Checked) - enabledChecksums |= EnableChecksum.Fletcher16; - - if(Fletcher32Checked) - enabledChecksums |= EnableChecksum.Fletcher32; - - Checksum mediaChecksum = null; - ErrorNumber errno; - - if(opticalMediaImage?.Tracks != null) - try - { - Checksum trackChecksum = null; - - if(ChecksumMediaChecked) - mediaChecksum = new Checksum(enabledChecksums); - - ulong previousTrackEnd = 0; - - foreach(Track currentTrack in opticalMediaImage.Tracks) + await Dispatcher.UIThread.InvokeAsync(() => { - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = $"Hashing track {currentTrack.Sequence} of {opticalMediaImage.Tracks.Count}"; + ProgressText = $"Hashing track {currentTrack.Sequence} of {opticalMediaImage.Tracks.Count}"; - ProgressValue++; - }); + ProgressValue++; + }); - if(currentTrack.StartSector - previousTrackEnd != 0 && ChecksumMediaChecked) - for(ulong i = previousTrackEnd + 1; i < currentTrack.StartSector; i++) - { - ulong sector = i; - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Value = (int)(sector / SECTORS_TO_READ); - Progress2Text = $"Hashing track-less sector {sector}"; - }); - - errno = opticalMediaImage.ReadSector(i, out byte[] hiddenSector); - - if(errno != ErrorNumber.NoError) - { - AaruConsole.ErrorWriteLine($"Error {errno} reading sector {i}"); - _cancel = true; - - break; - } - - mediaChecksum?.Update(hiddenSector); - } - - AaruConsole.DebugWriteLine("Checksum command", - "Track {0} starts at sector {1} and ends at sector {2}", - currentTrack.Sequence, currentTrack.StartSector, - currentTrack.EndSector); - - if(ChecksumTracksChecked) - trackChecksum = new Checksum(enabledChecksums); - - ulong sectors = currentTrack.EndSector - currentTrack.StartSector + 1; - ulong doneSectors = 0; - - while(doneSectors < sectors) - { - if(_cancel) - { - await Dispatcher.UIThread.InvokeAsync(() => - { - CloseCommandVisible = true; - StartCommandVisible = false; - StopCommandVisible = false; - }); - - return; - } - - byte[] sector; - - if(sectors - doneSectors >= SECTORS_TO_READ) - { - errno = opticalMediaImage.ReadSectors(doneSectors, SECTORS_TO_READ, - currentTrack.Sequence, out sector); - - if(errno != ErrorNumber.NoError) - { - AaruConsole.ErrorWriteLine($"Error {errno} reading sector {doneSectors}"); - _cancel = true; - - continue; - } - - ulong doneSectorsToInvoke = doneSectors; - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); - - Progress2Text = - $"Hashing sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + SECTORS_TO_READ} of track {currentTrack.Sequence}"; - }); - - doneSectors += SECTORS_TO_READ; - } - else - { - errno = opticalMediaImage.ReadSectors(doneSectors, (uint)(sectors - doneSectors), - currentTrack.Sequence, out sector); - - if(errno != ErrorNumber.NoError) - { - AaruConsole.ErrorWriteLine($"Error {errno} reading sector {doneSectors}"); - _cancel = true; - - continue; - } - - ulong doneSectorsToInvoke = doneSectors; - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); - - Progress2Text = - $"Hashing sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + (sectors - doneSectorsToInvoke)} of track {currentTrack.Sequence}"; - }); - - doneSectors += sectors - doneSectors; - } - - if(ChecksumMediaChecked) - mediaChecksum?.Update(sector); - - if(ChecksumTracksChecked) - trackChecksum?.Update(sector); - } - - await Dispatcher.UIThread.InvokeAsync(() => - { - if(ChecksumTracksChecked != true) - return; - - if(trackChecksum == null) - return; - - foreach(ChecksumType chk in trackChecksum.End()) - TrackChecksums.Add(new ChecksumModel - { - Track = currentTrack.Sequence.ToString(), - Algorithm = chk.type.ToString(), - Hash = chk.Value - }); - }); - - previousTrackEnd = currentTrack.EndSector; - } - - if(opticalMediaImage.Info.Sectors - previousTrackEnd != 0 && ChecksumMediaChecked) - for(ulong i = previousTrackEnd + 1; i < opticalMediaImage.Info.Sectors; i++) + if(currentTrack.StartSector - previousTrackEnd != 0 && ChecksumMediaChecked) + for(ulong i = previousTrackEnd + 1; i < currentTrack.StartSector; i++) { ulong sector = i; @@ -615,106 +486,140 @@ namespace Aaru.Gui.ViewModels.Windows mediaChecksum?.Update(hiddenSector); } + AaruConsole.DebugWriteLine("Checksum command", + "Track {0} starts at sector {1} and ends at sector {2}", + currentTrack.Sequence, currentTrack.StartSector, + currentTrack.EndSector); + + if(ChecksumTracksChecked) + trackChecksum = new Checksum(enabledChecksums); + + ulong sectors = currentTrack.EndSector - currentTrack.StartSector + 1; + ulong doneSectors = 0; + + while(doneSectors < sectors) + { + if(_cancel) + { + await Dispatcher.UIThread.InvokeAsync(() => + { + CloseCommandVisible = true; + StartCommandVisible = false; + StopCommandVisible = false; + }); + + return; + } + + byte[] sector; + + if(sectors - doneSectors >= SECTORS_TO_READ) + { + errno = opticalMediaImage.ReadSectors(doneSectors, SECTORS_TO_READ, + currentTrack.Sequence, out sector); + + if(errno != ErrorNumber.NoError) + { + AaruConsole.ErrorWriteLine($"Error {errno} reading sector {doneSectors}"); + _cancel = true; + + continue; + } + + ulong doneSectorsToInvoke = doneSectors; + + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); + + Progress2Text = + $"Hashing sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + SECTORS_TO_READ} of track {currentTrack.Sequence}"; + }); + + doneSectors += SECTORS_TO_READ; + } + else + { + errno = opticalMediaImage.ReadSectors(doneSectors, (uint)(sectors - doneSectors), + currentTrack.Sequence, out sector); + + if(errno != ErrorNumber.NoError) + { + AaruConsole.ErrorWriteLine($"Error {errno} reading sector {doneSectors}"); + _cancel = true; + + continue; + } + + ulong doneSectorsToInvoke = doneSectors; + + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); + + Progress2Text = + $"Hashing sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + (sectors - doneSectorsToInvoke)} of track {currentTrack.Sequence}"; + }); + + doneSectors += sectors - doneSectors; + } + + if(ChecksumMediaChecked) + mediaChecksum?.Update(sector); + + if(ChecksumTracksChecked) + trackChecksum?.Update(sector); + } + await Dispatcher.UIThread.InvokeAsync(() => { - if(mediaChecksum == null) + if(ChecksumTracksChecked != true) return; - foreach(ChecksumType chk in mediaChecksum.End()) - MediaChecksums.Add(new ChecksumModel + if(trackChecksum == null) + return; + + foreach(ChecksumType chk in trackChecksum.End()) + TrackChecksums.Add(new ChecksumModel { + Track = currentTrack.Sequence.ToString(), Algorithm = chk.type.ToString(), Hash = chk.Value }); }); + + previousTrackEnd = currentTrack.EndSector; } - catch(Exception ex) - { - AaruConsole.DebugWriteLine("Could not get tracks because {0}", ex.Message); - AaruConsole.WriteLine("Unable to get separate tracks, not checksumming them"); - } - else - { - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress1Visible = false; - }); - mediaChecksum = new Checksum(enabledChecksums); - - ulong doneSectors = 0; - - while(doneSectors < _inputFormat.Info.Sectors) - { - if(_cancel) + if(opticalMediaImage.Info.Sectors - previousTrackEnd != 0 && ChecksumMediaChecked) + for(ulong i = previousTrackEnd + 1; i < opticalMediaImage.Info.Sectors; i++) { + ulong sector = i; + await Dispatcher.UIThread.InvokeAsync(() => { - CloseCommandVisible = true; - StartCommandVisible = false; - StopCommandVisible = false; + Progress2Value = (int)(sector / SECTORS_TO_READ); + Progress2Text = $"Hashing track-less sector {sector}"; }); + errno = opticalMediaImage.ReadSector(i, out byte[] hiddenSector); + + if(errno != ErrorNumber.NoError) + { + AaruConsole.ErrorWriteLine($"Error {errno} reading sector {i}"); + _cancel = true; + + break; + } + + mediaChecksum?.Update(hiddenSector); + } + + await Dispatcher.UIThread.InvokeAsync(() => + { + if(mediaChecksum == null) return; - } - byte[] sector; - - if(_inputFormat.Info.Sectors - doneSectors >= SECTORS_TO_READ) - { - errno = _inputFormat.ReadSectors(doneSectors, SECTORS_TO_READ, out sector); - - if(errno != ErrorNumber.NoError) - { - AaruConsole.ErrorWriteLine($"Error {errno} reading sector {doneSectors}"); - _cancel = true; - - continue; - } - - ulong doneSectorsToInvoke = doneSectors; - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); - - Progress2Text = - $"Hashing sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + SECTORS_TO_READ}"; - }); - - doneSectors += SECTORS_TO_READ; - } - else - { - errno = _inputFormat.ReadSectors(doneSectors, (uint)(_inputFormat.Info.Sectors - doneSectors), - out sector); - - if(errno != ErrorNumber.NoError) - { - AaruConsole.ErrorWriteLine($"Error {errno} reading sector {doneSectors}"); - _cancel = true; - - continue; - } - - ulong doneSectorsToInvoke = doneSectors; - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); - - Progress2Text = - $"Hashing sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + (_inputFormat.Info.Sectors - doneSectorsToInvoke)}"; - }); - - doneSectors += _inputFormat.Info.Sectors - doneSectors; - } - - mediaChecksum.Update(sector); - } - - await Dispatcher.UIThread.InvokeAsync(() => - { foreach(ChecksumType chk in mediaChecksum.End()) MediaChecksums.Add(new ChecksumModel { @@ -723,30 +628,124 @@ namespace Aaru.Gui.ViewModels.Windows }); }); } + catch(Exception ex) + { + AaruConsole.DebugWriteLine("Could not get tracks because {0}", ex.Message); + AaruConsole.WriteLine("Unable to get separate tracks, not checksumming them"); + } + else + { + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress1Visible = false; + }); - if(ChecksumTracksChecked) - await Dispatcher.UIThread.InvokeAsync(() => + mediaChecksum = new Checksum(enabledChecksums); + + ulong doneSectors = 0; + + while(doneSectors < _inputFormat.Info.Sectors) + { + if(_cancel) { - TrackChecksumsVisible = true; - }); + await Dispatcher.UIThread.InvokeAsync(() => + { + CloseCommandVisible = true; + StartCommandVisible = false; + StopCommandVisible = false; + }); - if(ChecksumMediaChecked) - await Dispatcher.UIThread.InvokeAsync(() => + return; + } + + byte[] sector; + + if(_inputFormat.Info.Sectors - doneSectors >= SECTORS_TO_READ) { - MediaChecksumsVisible = true; - }); + errno = _inputFormat.ReadSectors(doneSectors, SECTORS_TO_READ, out sector); - Statistics.AddCommand("checksum"); + if(errno != ErrorNumber.NoError) + { + AaruConsole.ErrorWriteLine($"Error {errno} reading sector {doneSectors}"); + _cancel = true; + + continue; + } + + ulong doneSectorsToInvoke = doneSectors; + + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); + + Progress2Text = + $"Hashing sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + SECTORS_TO_READ}"; + }); + + doneSectors += SECTORS_TO_READ; + } + else + { + errno = _inputFormat.ReadSectors(doneSectors, (uint)(_inputFormat.Info.Sectors - doneSectors), + out sector); + + if(errno != ErrorNumber.NoError) + { + AaruConsole.ErrorWriteLine($"Error {errno} reading sector {doneSectors}"); + _cancel = true; + + continue; + } + + ulong doneSectorsToInvoke = doneSectors; + + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Value = (int)(doneSectorsToInvoke / SECTORS_TO_READ); + + Progress2Text = + $"Hashing sectors {doneSectorsToInvoke} to {doneSectorsToInvoke + (_inputFormat.Info.Sectors - doneSectorsToInvoke)}"; + }); + + doneSectors += _inputFormat.Info.Sectors - doneSectors; + } + + mediaChecksum.Update(sector); + } await Dispatcher.UIThread.InvokeAsync(() => { - OptionsEnabled = false; - ResultsVisible = true; - ProgressVisible = false; - StartCommandVisible = false; - StopCommandVisible = false; - CloseCommandVisible = true; + foreach(ChecksumType chk in mediaChecksum.End()) + MediaChecksums.Add(new ChecksumModel + { + Algorithm = chk.type.ToString(), + Hash = chk.Value + }); }); } + + if(ChecksumTracksChecked) + await Dispatcher.UIThread.InvokeAsync(() => + { + TrackChecksumsVisible = true; + }); + + if(ChecksumMediaChecked) + await Dispatcher.UIThread.InvokeAsync(() => + { + MediaChecksumsVisible = true; + }); + + Statistics.AddCommand("checksum"); + + await Dispatcher.UIThread.InvokeAsync(() => + { + OptionsEnabled = false; + ResultsVisible = true; + ProgressVisible = false; + StartCommandVisible = false; + StopCommandVisible = false; + CloseCommandVisible = true; + }); } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/ImageConvertViewModel.cs b/Aaru.Gui/ViewModels/Windows/ImageConvertViewModel.cs index ca491666d..9917f92f8 100644 --- a/Aaru.Gui/ViewModels/Windows/ImageConvertViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/ImageConvertViewModel.cs @@ -59,881 +59,1080 @@ using Schemas; using ImageInfo = Aaru.CommonTypes.Structs.ImageInfo; using Version = Aaru.CommonTypes.Interop.Version; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class ImageConvertViewModel : ViewModelBase { - public sealed class ImageConvertViewModel : ViewModelBase + readonly IMediaImage _inputFormat; + readonly Window _view; + bool _cancel; + CICMMetadataType _cicmMetadata; + bool _cicmXmlFromImageVisible; + string _cicmXmlText; + bool _closeVisible; + string _commentsText; + bool _commentsVisible; + string _creatorText; + bool _creatorVisible; + bool _destinationEnabled; + string _destinationText; + bool _destinationVisible; + string _driveFirmwareRevisionText; + bool _driveFirmwareRevisionVisible; + string _driveManufacturerText; + bool _driveManufacturerVisible; + string _driveModelText; + bool _driveModelVisible; + string _driveSerialNumberText; + bool _driveSerialNumberVisible; + List _dumpHardware; + bool _forceChecked; + bool _formatReadOnly; + double _lastMediaSequenceValue; + bool _lastMediaSequenceVisible; + string _mediaBarcodeText; + bool _mediaBarcodeVisible; + string _mediaManufacturerText; + bool _mediaManufacturerVisible; + string _mediaModelText; + bool _mediaModelVisible; + string _mediaPartNumberText; + bool _mediaPartNumberVisible; + double _mediaSequenceValue; + bool _mediaSequenceVisible; + string _mediaSerialNumberText; + bool _mediaSerialNumberVisible; + string _mediaTitleText; + bool _mediaTitleVisible; + bool _optionsVisible; + bool _progress1Visible; + bool _progress2Indeterminate; + double _progress2MaxValue; + string _progress2Text; + double _progress2Value; + bool _progress2Visible; + bool _progressIndeterminate; + double _progressMaxValue; + string _progressText; + double _progressValue; + bool _progressVisible; + bool _resumeFileFromImageVisible; + string _resumeFileText; + double _sectorsValue; + ImagePluginModel _selectedPlugin; + string _sourceText; + bool _startVisible; + bool _stopEnabled; + bool _stopVisible; + string _title; + + public ImageConvertViewModel([NotNull] IMediaImage inputFormat, string imageSource, Window view) { - readonly IMediaImage _inputFormat; - readonly Window _view; - bool _cancel; - CICMMetadataType _cicmMetadata; - bool _cicmXmlFromImageVisible; - string _cicmXmlText; - bool _closeVisible; - string _commentsText; - bool _commentsVisible; - string _creatorText; - bool _creatorVisible; - bool _destinationEnabled; - string _destinationText; - bool _destinationVisible; - string _driveFirmwareRevisionText; - bool _driveFirmwareRevisionVisible; - string _driveManufacturerText; - bool _driveManufacturerVisible; - string _driveModelText; - bool _driveModelVisible; - string _driveSerialNumberText; - bool _driveSerialNumberVisible; - List _dumpHardware; - bool _forceChecked; - bool _formatReadOnly; - double _lastMediaSequenceValue; - bool _lastMediaSequenceVisible; - string _mediaBarcodeText; - bool _mediaBarcodeVisible; - string _mediaManufacturerText; - bool _mediaManufacturerVisible; - string _mediaModelText; - bool _mediaModelVisible; - string _mediaPartNumberText; - bool _mediaPartNumberVisible; - double _mediaSequenceValue; - bool _mediaSequenceVisible; - string _mediaSerialNumberText; - bool _mediaSerialNumberVisible; - string _mediaTitleText; - bool _mediaTitleVisible; - bool _optionsVisible; - bool _progress1Visible; - bool _progress2Indeterminate; - double _progress2MaxValue; - string _progress2Text; - double _progress2Value; - bool _progress2Visible; - bool _progressIndeterminate; - double _progressMaxValue; - string _progressText; - double _progressValue; - bool _progressVisible; - bool _resumeFileFromImageVisible; - string _resumeFileText; - double _sectorsValue; - ImagePluginModel _selectedPlugin; - string _sourceText; - bool _startVisible; - bool _stopEnabled; - bool _stopVisible; - string _title; + _view = view; + _inputFormat = inputFormat; + _cancel = false; + DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand); + CreatorCommand = ReactiveCommand.Create(ExecuteCreatorCommand); + MediaTitleCommand = ReactiveCommand.Create(ExecuteMediaTitleCommand); + MediaManufacturerCommand = ReactiveCommand.Create(ExecuteMediaManufacturerCommand); + MediaModelCommand = ReactiveCommand.Create(ExecuteMediaModelCommand); + MediaSerialNumberCommand = ReactiveCommand.Create(ExecuteMediaSerialNumberCommand); + MediaBarcodeCommand = ReactiveCommand.Create(ExecuteMediaBarcodeCommand); + MediaPartNumberCommand = ReactiveCommand.Create(ExecuteMediaPartNumberCommand); + MediaSequenceCommand = ReactiveCommand.Create(ExecuteMediaSequenceCommand); + LastMediaSequenceCommand = ReactiveCommand.Create(ExecuteLastMediaSequenceCommand); + DriveManufacturerCommand = ReactiveCommand.Create(ExecuteDriveManufacturerCommand); + DriveModelCommand = ReactiveCommand.Create(ExecuteDriveModelCommand); + DriveSerialNumberCommand = ReactiveCommand.Create(ExecuteDriveSerialNumberCommand); + DriveFirmwareRevisionCommand = ReactiveCommand.Create(ExecuteDriveFirmwareRevisionCommand); + CommentsCommand = ReactiveCommand.Create(ExecuteCommentsCommand); + CicmXmlFromImageCommand = ReactiveCommand.Create(ExecuteCicmXmlFromImageCommand); + CicmXmlCommand = ReactiveCommand.Create(ExecuteCicmXmlCommand); + ResumeFileFromImageCommand = ReactiveCommand.Create(ExecuteResumeFileFromImageCommand); + ResumeFileCommand = ReactiveCommand.Create(ExecuteResumeFileCommand); + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StopCommand = ReactiveCommand.Create(ExecuteStopCommand); + SourceText = imageSource; + CreatorVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.Creator); + MediaTitleVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaTitle); + CommentsVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.Comments); + MediaManufacturerVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaManufacturer); + MediaModelVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaModel); + MediaSerialNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaSerialNumber); + MediaBarcodeVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaBarcode); + MediaPartNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaPartNumber); + MediaSequenceVisible = inputFormat.Info.MediaSequence != 0 && inputFormat.Info.LastMediaSequence != 0; + LastMediaSequenceVisible = inputFormat.Info.MediaSequence != 0 && inputFormat.Info.LastMediaSequence != 0; + DriveManufacturerVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveManufacturer); + DriveModelVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveModel); + DriveSerialNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveSerialNumber); + DriveFirmwareRevisionVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveFirmwareRevision); - public ImageConvertViewModel([NotNull] IMediaImage inputFormat, string imageSource, Window view) - { - _view = view; - _inputFormat = inputFormat; - _cancel = false; - DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand); - CreatorCommand = ReactiveCommand.Create(ExecuteCreatorCommand); - MediaTitleCommand = ReactiveCommand.Create(ExecuteMediaTitleCommand); - MediaManufacturerCommand = ReactiveCommand.Create(ExecuteMediaManufacturerCommand); - MediaModelCommand = ReactiveCommand.Create(ExecuteMediaModelCommand); - MediaSerialNumberCommand = ReactiveCommand.Create(ExecuteMediaSerialNumberCommand); - MediaBarcodeCommand = ReactiveCommand.Create(ExecuteMediaBarcodeCommand); - MediaPartNumberCommand = ReactiveCommand.Create(ExecuteMediaPartNumberCommand); - MediaSequenceCommand = ReactiveCommand.Create(ExecuteMediaSequenceCommand); - LastMediaSequenceCommand = ReactiveCommand.Create(ExecuteLastMediaSequenceCommand); - DriveManufacturerCommand = ReactiveCommand.Create(ExecuteDriveManufacturerCommand); - DriveModelCommand = ReactiveCommand.Create(ExecuteDriveModelCommand); - DriveSerialNumberCommand = ReactiveCommand.Create(ExecuteDriveSerialNumberCommand); - DriveFirmwareRevisionCommand = ReactiveCommand.Create(ExecuteDriveFirmwareRevisionCommand); - CommentsCommand = ReactiveCommand.Create(ExecuteCommentsCommand); - CicmXmlFromImageCommand = ReactiveCommand.Create(ExecuteCicmXmlFromImageCommand); - CicmXmlCommand = ReactiveCommand.Create(ExecuteCicmXmlCommand); - ResumeFileFromImageCommand = ReactiveCommand.Create(ExecuteResumeFileFromImageCommand); - ResumeFileCommand = ReactiveCommand.Create(ExecuteResumeFileCommand); - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StopCommand = ReactiveCommand.Create(ExecuteStopCommand); - SourceText = imageSource; - CreatorVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.Creator); - MediaTitleVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaTitle); - CommentsVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.Comments); - MediaManufacturerVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaManufacturer); - MediaModelVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaModel); - MediaSerialNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaSerialNumber); - MediaBarcodeVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaBarcode); - MediaPartNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.MediaPartNumber); - MediaSequenceVisible = inputFormat.Info.MediaSequence != 0 && inputFormat.Info.LastMediaSequence != 0; - LastMediaSequenceVisible = inputFormat.Info.MediaSequence != 0 && inputFormat.Info.LastMediaSequence != 0; - DriveManufacturerVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveManufacturer); - DriveModelVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveModel); - DriveSerialNumberVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveSerialNumber); - DriveFirmwareRevisionVisible = !string.IsNullOrWhiteSpace(inputFormat.Info.DriveFirmwareRevision); + PluginBase plugins = GetPluginBase.Instance; - PluginBase plugins = GetPluginBase.Instance; - - foreach(IWritableImage plugin in + foreach(IWritableImage plugin in plugins.WritableImages.Values.Where(p => p.SupportedMediaTypes.Contains(inputFormat.Info.MediaType))) - PluginsList.Add(new ImagePluginModel - { - Plugin = plugin - }); - - CicmXmlFromImageVisible = inputFormat.CicmMetadata != null; - ResumeFileFromImageVisible = inputFormat.DumpHardware?.Any() == true; - _cicmMetadata = inputFormat.CicmMetadata; - - _dumpHardware = inputFormat.DumpHardware?.Any() == true ? inputFormat.DumpHardware : null; - - CicmXmlText = _cicmMetadata == null ? "" : ""; - ResumeFileText = _dumpHardware == null ? "" : ""; - } - - public string Title - { - get => _title; - set => this.RaiseAndSetIfChanged(ref _title, value); - } - - public string SourceText - { - get => _sourceText; - set => this.RaiseAndSetIfChanged(ref _sourceText, value); - } - - public ObservableCollection PluginsList { get; } - - public ImagePluginModel SelectedPlugin - { - get => _selectedPlugin; - set => this.RaiseAndSetIfChanged(ref _selectedPlugin, value); - } - - public string DestinationText - { - get => _destinationText; - set => this.RaiseAndSetIfChanged(ref _destinationText, value); - } - - public bool OptionsVisible - { - get => _optionsVisible; - set => this.RaiseAndSetIfChanged(ref _optionsVisible, value); - } - - public double SectorsValue - { - get => _sectorsValue; - set => this.RaiseAndSetIfChanged(ref _sectorsValue, value); - } - - public bool ForceChecked - { - get => _forceChecked; - set => this.RaiseAndSetIfChanged(ref _forceChecked, value); - } - - public string CreatorText - { - get => _creatorText; - set => this.RaiseAndSetIfChanged(ref _creatorText, value); - } - - public string MediaTitleText - { - get => _mediaTitleText; - set => this.RaiseAndSetIfChanged(ref _mediaTitleText, value); - } - - public string MediaManufacturerText - { - get => _mediaManufacturerText; - set => this.RaiseAndSetIfChanged(ref _mediaManufacturerText, value); - } - - public string MediaModelText - { - get => _mediaModelText; - set => this.RaiseAndSetIfChanged(ref _mediaModelText, value); - } - - public string MediaSerialNumberText - { - get => _mediaSerialNumberText; - set => this.RaiseAndSetIfChanged(ref _mediaSerialNumberText, value); - } - - public string MediaBarcodeText - { - get => _mediaBarcodeText; - set => this.RaiseAndSetIfChanged(ref _mediaBarcodeText, value); - } - - public string MediaPartNumberText - { - get => _mediaPartNumberText; - set => this.RaiseAndSetIfChanged(ref _mediaPartNumberText, value); - } - - public double MediaSequenceValue - { - get => _mediaSequenceValue; - set => this.RaiseAndSetIfChanged(ref _mediaSequenceValue, value); - } - - public double LastMediaSequenceValue - { - get => _lastMediaSequenceValue; - set => this.RaiseAndSetIfChanged(ref _lastMediaSequenceValue, value); - } - - public string DriveManufacturerText - { - get => _driveManufacturerText; - set => this.RaiseAndSetIfChanged(ref _driveManufacturerText, value); - } - - public string DriveModelText - { - get => _driveModelText; - set => this.RaiseAndSetIfChanged(ref _driveModelText, value); - } - - public string DriveSerialNumberText - { - get => _driveSerialNumberText; - set => this.RaiseAndSetIfChanged(ref _driveSerialNumberText, value); - } - - public string DriveFirmwareRevisionText - { - get => _driveFirmwareRevisionText; - set => this.RaiseAndSetIfChanged(ref _driveFirmwareRevisionText, value); - } - - public string CommentsText - { - get => _commentsText; - set => this.RaiseAndSetIfChanged(ref _commentsText, value); - } - - public string CicmXmlText - { - get => _cicmXmlText; - set => this.RaiseAndSetIfChanged(ref _cicmXmlText, value); - } - - public string ResumeFileText - { - get => _resumeFileText; - set => this.RaiseAndSetIfChanged(ref _resumeFileText, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public bool Progress1Visible - { - get => _progress1Visible; - set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); - } - - public string ProgressText - { - get => _progressText; - set => this.RaiseAndSetIfChanged(ref _progressText, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public double ProgressMaxValue - { - get => _progressMaxValue; - set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); - } - - public bool ProgressIndeterminate - { - get => _progressIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); - } - - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } - - public string Progress2Text - { - get => _progress2Text; - set => this.RaiseAndSetIfChanged(ref _progress2Text, value); - } - - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } - - public double Progress2MaxValue - { - get => _progress2MaxValue; - set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value); - } - - public bool Progress2Indeterminate - { - get => _progress2Indeterminate; - set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); - } - - public bool StartVisible - { - get => _startVisible; - set => this.RaiseAndSetIfChanged(ref _startVisible, value); - } - - public bool CloseVisible - { - get => _closeVisible; - set => this.RaiseAndSetIfChanged(ref _closeVisible, value); - } - - public bool StopVisible - { - get => _stopVisible; - set => this.RaiseAndSetIfChanged(ref _stopVisible, value); - } - - public bool StopEnabled - { - get => _stopEnabled; - set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); - } - - public bool CreatorVisible - { - get => _creatorVisible; - set => this.RaiseAndSetIfChanged(ref _creatorVisible, value); - } - - public bool MediaTitleVisible - { - get => _mediaTitleVisible; - set => this.RaiseAndSetIfChanged(ref _mediaTitleVisible, value); - } - - public bool CommentsVisible - { - get => _commentsVisible; - set => this.RaiseAndSetIfChanged(ref _commentsVisible, value); - } - - public bool MediaManufacturerVisible - { - get => _mediaManufacturerVisible; - set => this.RaiseAndSetIfChanged(ref _mediaManufacturerVisible, value); - } - - public bool MediaModelVisible - { - get => _mediaModelVisible; - set => this.RaiseAndSetIfChanged(ref _mediaModelVisible, value); - } - - public bool MediaSerialNumberVisible - { - get => _mediaSerialNumberVisible; - set => this.RaiseAndSetIfChanged(ref _mediaSerialNumberVisible, value); - } - - public bool MediaBarcodeVisible - { - get => _mediaBarcodeVisible; - set => this.RaiseAndSetIfChanged(ref _mediaBarcodeVisible, value); - } - - public bool MediaPartNumberVisible - { - get => _mediaPartNumberVisible; - set => this.RaiseAndSetIfChanged(ref _mediaPartNumberVisible, value); - } - - public bool MediaSequenceVisible - { - get => _mediaSequenceVisible; - set => this.RaiseAndSetIfChanged(ref _mediaSequenceVisible, value); - } - - public bool LastMediaSequenceVisible - { - get => _lastMediaSequenceVisible; - set => this.RaiseAndSetIfChanged(ref _lastMediaSequenceVisible, value); - } - - public bool DriveManufacturerVisible - { - get => _driveManufacturerVisible; - set => this.RaiseAndSetIfChanged(ref _driveManufacturerVisible, value); - } - - public bool DriveModelVisible - { - get => _driveModelVisible; - set => this.RaiseAndSetIfChanged(ref _driveModelVisible, value); - } - - public bool DriveSerialNumberVisible - { - get => _driveSerialNumberVisible; - set => this.RaiseAndSetIfChanged(ref _driveSerialNumberVisible, value); - } - - public bool DriveFirmwareRevisionVisible - { - get => _driveFirmwareRevisionVisible; - set => this.RaiseAndSetIfChanged(ref _driveFirmwareRevisionVisible, value); - } - - public bool CicmXmlFromImageVisible - { - get => _cicmXmlFromImageVisible; - set => this.RaiseAndSetIfChanged(ref _cicmXmlFromImageVisible, value); - } - - public bool ResumeFileFromImageVisible - { - get => _resumeFileFromImageVisible; - set => this.RaiseAndSetIfChanged(ref _resumeFileFromImageVisible, value); - } - - public bool DestinationEnabled - { - get => _destinationEnabled; - set => this.RaiseAndSetIfChanged(ref _destinationEnabled, value); - } - - public ReactiveCommand DestinationCommand { get; } - public ReactiveCommand CreatorCommand { get; } - public ReactiveCommand MediaTitleCommand { get; } - public ReactiveCommand MediaManufacturerCommand { get; } - public ReactiveCommand MediaModelCommand { get; } - public ReactiveCommand MediaSerialNumberCommand { get; } - public ReactiveCommand MediaBarcodeCommand { get; } - public ReactiveCommand MediaPartNumberCommand { get; } - public ReactiveCommand MediaSequenceCommand { get; } - public ReactiveCommand LastMediaSequenceCommand { get; } - public ReactiveCommand DriveManufacturerCommand { get; } - public ReactiveCommand DriveModelCommand { get; } - public ReactiveCommand DriveSerialNumberCommand { get; } - public ReactiveCommand DriveFirmwareRevisionCommand { get; } - public ReactiveCommand CommentsCommand { get; } - public ReactiveCommand CicmXmlFromImageCommand { get; } - public ReactiveCommand CicmXmlCommand { get; } - public ReactiveCommand ResumeFileFromImageCommand { get; } - public ReactiveCommand ResumeFileCommand { get; } - public ReactiveCommand StartCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StopCommand { get; } - - public bool FormatReadOnly - { - get => _formatReadOnly; - set => this.RaiseAndSetIfChanged(ref _formatReadOnly, value); - } - - public bool DestinationVisible - { - get => _destinationVisible; - set => this.RaiseAndSetIfChanged(ref _destinationVisible, value); - } - - async void ExecuteStartCommand() - { - if(SelectedPlugin is null) + PluginsList.Add(new ImagePluginModel { - await MessageBoxManager.GetMessageBoxStandardWindow("Error", "Error trying to find selected plugin", - icon: Icon.Error).ShowDialog(_view); + Plugin = plugin + }); - return; - } + CicmXmlFromImageVisible = inputFormat.CicmMetadata != null; + ResumeFileFromImageVisible = inputFormat.DumpHardware?.Any() == true; + _cicmMetadata = inputFormat.CicmMetadata; - new Thread(DoWork).Start(SelectedPlugin.Plugin); + _dumpHardware = inputFormat.DumpHardware?.Any() == true ? inputFormat.DumpHardware : null; + + CicmXmlText = _cicmMetadata == null ? "" : ""; + ResumeFileText = _dumpHardware == null ? "" : ""; + } + + public string Title + { + get => _title; + set => this.RaiseAndSetIfChanged(ref _title, value); + } + + public string SourceText + { + get => _sourceText; + set => this.RaiseAndSetIfChanged(ref _sourceText, value); + } + + public ObservableCollection PluginsList { get; } + + public ImagePluginModel SelectedPlugin + { + get => _selectedPlugin; + set => this.RaiseAndSetIfChanged(ref _selectedPlugin, value); + } + + public string DestinationText + { + get => _destinationText; + set => this.RaiseAndSetIfChanged(ref _destinationText, value); + } + + public bool OptionsVisible + { + get => _optionsVisible; + set => this.RaiseAndSetIfChanged(ref _optionsVisible, value); + } + + public double SectorsValue + { + get => _sectorsValue; + set => this.RaiseAndSetIfChanged(ref _sectorsValue, value); + } + + public bool ForceChecked + { + get => _forceChecked; + set => this.RaiseAndSetIfChanged(ref _forceChecked, value); + } + + public string CreatorText + { + get => _creatorText; + set => this.RaiseAndSetIfChanged(ref _creatorText, value); + } + + public string MediaTitleText + { + get => _mediaTitleText; + set => this.RaiseAndSetIfChanged(ref _mediaTitleText, value); + } + + public string MediaManufacturerText + { + get => _mediaManufacturerText; + set => this.RaiseAndSetIfChanged(ref _mediaManufacturerText, value); + } + + public string MediaModelText + { + get => _mediaModelText; + set => this.RaiseAndSetIfChanged(ref _mediaModelText, value); + } + + public string MediaSerialNumberText + { + get => _mediaSerialNumberText; + set => this.RaiseAndSetIfChanged(ref _mediaSerialNumberText, value); + } + + public string MediaBarcodeText + { + get => _mediaBarcodeText; + set => this.RaiseAndSetIfChanged(ref _mediaBarcodeText, value); + } + + public string MediaPartNumberText + { + get => _mediaPartNumberText; + set => this.RaiseAndSetIfChanged(ref _mediaPartNumberText, value); + } + + public double MediaSequenceValue + { + get => _mediaSequenceValue; + set => this.RaiseAndSetIfChanged(ref _mediaSequenceValue, value); + } + + public double LastMediaSequenceValue + { + get => _lastMediaSequenceValue; + set => this.RaiseAndSetIfChanged(ref _lastMediaSequenceValue, value); + } + + public string DriveManufacturerText + { + get => _driveManufacturerText; + set => this.RaiseAndSetIfChanged(ref _driveManufacturerText, value); + } + + public string DriveModelText + { + get => _driveModelText; + set => this.RaiseAndSetIfChanged(ref _driveModelText, value); + } + + public string DriveSerialNumberText + { + get => _driveSerialNumberText; + set => this.RaiseAndSetIfChanged(ref _driveSerialNumberText, value); + } + + public string DriveFirmwareRevisionText + { + get => _driveFirmwareRevisionText; + set => this.RaiseAndSetIfChanged(ref _driveFirmwareRevisionText, value); + } + + public string CommentsText + { + get => _commentsText; + set => this.RaiseAndSetIfChanged(ref _commentsText, value); + } + + public string CicmXmlText + { + get => _cicmXmlText; + set => this.RaiseAndSetIfChanged(ref _cicmXmlText, value); + } + + public string ResumeFileText + { + get => _resumeFileText; + set => this.RaiseAndSetIfChanged(ref _resumeFileText, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public bool Progress1Visible + { + get => _progress1Visible; + set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); + } + + public string ProgressText + { + get => _progressText; + set => this.RaiseAndSetIfChanged(ref _progressText, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public double ProgressMaxValue + { + get => _progressMaxValue; + set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); + } + + public bool ProgressIndeterminate + { + get => _progressIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); + } + + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } + + public string Progress2Text + { + get => _progress2Text; + set => this.RaiseAndSetIfChanged(ref _progress2Text, value); + } + + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } + + public double Progress2MaxValue + { + get => _progress2MaxValue; + set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value); + } + + public bool Progress2Indeterminate + { + get => _progress2Indeterminate; + set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); + } + + public bool StartVisible + { + get => _startVisible; + set => this.RaiseAndSetIfChanged(ref _startVisible, value); + } + + public bool CloseVisible + { + get => _closeVisible; + set => this.RaiseAndSetIfChanged(ref _closeVisible, value); + } + + public bool StopVisible + { + get => _stopVisible; + set => this.RaiseAndSetIfChanged(ref _stopVisible, value); + } + + public bool StopEnabled + { + get => _stopEnabled; + set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); + } + + public bool CreatorVisible + { + get => _creatorVisible; + set => this.RaiseAndSetIfChanged(ref _creatorVisible, value); + } + + public bool MediaTitleVisible + { + get => _mediaTitleVisible; + set => this.RaiseAndSetIfChanged(ref _mediaTitleVisible, value); + } + + public bool CommentsVisible + { + get => _commentsVisible; + set => this.RaiseAndSetIfChanged(ref _commentsVisible, value); + } + + public bool MediaManufacturerVisible + { + get => _mediaManufacturerVisible; + set => this.RaiseAndSetIfChanged(ref _mediaManufacturerVisible, value); + } + + public bool MediaModelVisible + { + get => _mediaModelVisible; + set => this.RaiseAndSetIfChanged(ref _mediaModelVisible, value); + } + + public bool MediaSerialNumberVisible + { + get => _mediaSerialNumberVisible; + set => this.RaiseAndSetIfChanged(ref _mediaSerialNumberVisible, value); + } + + public bool MediaBarcodeVisible + { + get => _mediaBarcodeVisible; + set => this.RaiseAndSetIfChanged(ref _mediaBarcodeVisible, value); + } + + public bool MediaPartNumberVisible + { + get => _mediaPartNumberVisible; + set => this.RaiseAndSetIfChanged(ref _mediaPartNumberVisible, value); + } + + public bool MediaSequenceVisible + { + get => _mediaSequenceVisible; + set => this.RaiseAndSetIfChanged(ref _mediaSequenceVisible, value); + } + + public bool LastMediaSequenceVisible + { + get => _lastMediaSequenceVisible; + set => this.RaiseAndSetIfChanged(ref _lastMediaSequenceVisible, value); + } + + public bool DriveManufacturerVisible + { + get => _driveManufacturerVisible; + set => this.RaiseAndSetIfChanged(ref _driveManufacturerVisible, value); + } + + public bool DriveModelVisible + { + get => _driveModelVisible; + set => this.RaiseAndSetIfChanged(ref _driveModelVisible, value); + } + + public bool DriveSerialNumberVisible + { + get => _driveSerialNumberVisible; + set => this.RaiseAndSetIfChanged(ref _driveSerialNumberVisible, value); + } + + public bool DriveFirmwareRevisionVisible + { + get => _driveFirmwareRevisionVisible; + set => this.RaiseAndSetIfChanged(ref _driveFirmwareRevisionVisible, value); + } + + public bool CicmXmlFromImageVisible + { + get => _cicmXmlFromImageVisible; + set => this.RaiseAndSetIfChanged(ref _cicmXmlFromImageVisible, value); + } + + public bool ResumeFileFromImageVisible + { + get => _resumeFileFromImageVisible; + set => this.RaiseAndSetIfChanged(ref _resumeFileFromImageVisible, value); + } + + public bool DestinationEnabled + { + get => _destinationEnabled; + set => this.RaiseAndSetIfChanged(ref _destinationEnabled, value); + } + + public ReactiveCommand DestinationCommand { get; } + public ReactiveCommand CreatorCommand { get; } + public ReactiveCommand MediaTitleCommand { get; } + public ReactiveCommand MediaManufacturerCommand { get; } + public ReactiveCommand MediaModelCommand { get; } + public ReactiveCommand MediaSerialNumberCommand { get; } + public ReactiveCommand MediaBarcodeCommand { get; } + public ReactiveCommand MediaPartNumberCommand { get; } + public ReactiveCommand MediaSequenceCommand { get; } + public ReactiveCommand LastMediaSequenceCommand { get; } + public ReactiveCommand DriveManufacturerCommand { get; } + public ReactiveCommand DriveModelCommand { get; } + public ReactiveCommand DriveSerialNumberCommand { get; } + public ReactiveCommand DriveFirmwareRevisionCommand { get; } + public ReactiveCommand CommentsCommand { get; } + public ReactiveCommand CicmXmlFromImageCommand { get; } + public ReactiveCommand CicmXmlCommand { get; } + public ReactiveCommand ResumeFileFromImageCommand { get; } + public ReactiveCommand ResumeFileCommand { get; } + public ReactiveCommand StartCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StopCommand { get; } + + public bool FormatReadOnly + { + get => _formatReadOnly; + set => this.RaiseAndSetIfChanged(ref _formatReadOnly, value); + } + + public bool DestinationVisible + { + get => _destinationVisible; + set => this.RaiseAndSetIfChanged(ref _destinationVisible, value); + } + + async void ExecuteStartCommand() + { + if(SelectedPlugin is null) + { + await MessageBoxManager.GetMessageBoxStandardWindow("Error", "Error trying to find selected plugin", + icon: Icon.Error).ShowDialog(_view); + + return; } - async void DoWork(object plugin) + new Thread(DoWork).Start(SelectedPlugin.Plugin); + } + + async void DoWork(object plugin) + { + bool warning = false; + + if(!(plugin is IWritableImage outputFormat)) { - bool warning = false; + await MessageBoxManager.GetMessageBoxStandardWindow("Error", "Error trying to find selected plugin", + icon: Icon.Error).ShowDialog(_view); - if(!(plugin is IWritableImage outputFormat)) - { - await MessageBoxManager.GetMessageBoxStandardWindow("Error", "Error trying to find selected plugin", - icon: Icon.Error).ShowDialog(_view); + return; + } - return; - } + var inputOptical = _inputFormat as IOpticalMediaImage; + var outputOptical = outputFormat as IWritableOpticalImage; - var inputOptical = _inputFormat as IOpticalMediaImage; - var outputOptical = outputFormat as IWritableOpticalImage; + List tracks; - List tracks; + try + { + tracks = inputOptical?.Tracks; + } + catch(Exception) + { + tracks = null; + } - try - { - tracks = inputOptical?.Tracks; - } - catch(Exception) - { - tracks = null; - } + // Prepare UI + await Dispatcher.UIThread.InvokeAsync(() => + { + CloseVisible = false; + StartVisible = false; + StopVisible = true; + ProgressVisible = true; + OptionsVisible = false; + StopEnabled = true; + FormatReadOnly = true; + DestinationVisible = false; - // Prepare UI - await Dispatcher.UIThread.InvokeAsync(() => - { - CloseVisible = false; - StartVisible = false; - StopVisible = true; - ProgressVisible = true; - OptionsVisible = false; - StopEnabled = true; - FormatReadOnly = true; - DestinationVisible = false; + ProgressMaxValue = 1d; + ProgressMaxValue += _inputFormat.Info.ReadableMediaTags.Count; + ProgressMaxValue++; - ProgressMaxValue = 1d; - ProgressMaxValue += _inputFormat.Info.ReadableMediaTags.Count; + if(tracks != null) ProgressMaxValue++; - if(tracks != null) - ProgressMaxValue++; + if(tracks == null) + { + ProgressMaxValue += 2; - if(tracks == null) + foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags) { - ProgressMaxValue += 2; - - foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags) + switch(tag) { - switch(tag) - { - case SectorTagType.AppleSectorTag: - case SectorTagType.CdSectorSync: - case SectorTagType.CdSectorHeader: - case SectorTagType.CdSectorSubHeader: - case SectorTagType.CdSectorEdc: - case SectorTagType.CdSectorEccP: - case SectorTagType.CdSectorEccQ: - case SectorTagType.CdSectorEcc: - // This tags are inline in long sector - continue; - } - - if(ForceChecked && !outputFormat.SupportedSectorTags.Contains(tag)) + case SectorTagType.AppleSectorTag: + case SectorTagType.CdSectorSync: + case SectorTagType.CdSectorHeader: + case SectorTagType.CdSectorSubHeader: + case SectorTagType.CdSectorEdc: + case SectorTagType.CdSectorEccP: + case SectorTagType.CdSectorEccQ: + case SectorTagType.CdSectorEcc: + // This tags are inline in long sector continue; - - ProgressMaxValue++; } + + if(ForceChecked && !outputFormat.SupportedSectorTags.Contains(tag)) + continue; + + ProgressMaxValue++; } - else + } + else + { + ProgressMaxValue += tracks.Count; + + foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags.OrderBy(t => t)) { + switch(tag) + { + case SectorTagType.AppleSectorTag: + case SectorTagType.CdSectorSync: + case SectorTagType.CdSectorHeader: + case SectorTagType.CdSectorSubHeader: + case SectorTagType.CdSectorEdc: + case SectorTagType.CdSectorEccP: + case SectorTagType.CdSectorEccQ: + case SectorTagType.CdSectorEcc: + // This tags are inline in long sector + continue; + } + + if(ForceChecked && !outputFormat.SupportedSectorTags.Contains(tag)) + continue; + ProgressMaxValue += tracks.Count; - - foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags.OrderBy(t => t)) - { - switch(tag) - { - case SectorTagType.AppleSectorTag: - case SectorTagType.CdSectorSync: - case SectorTagType.CdSectorHeader: - case SectorTagType.CdSectorSubHeader: - case SectorTagType.CdSectorEdc: - case SectorTagType.CdSectorEccP: - case SectorTagType.CdSectorEccQ: - case SectorTagType.CdSectorEcc: - // This tags are inline in long sector - continue; - } - - if(ForceChecked && !outputFormat.SupportedSectorTags.Contains(tag)) - continue; - - ProgressMaxValue += tracks.Count; - } } + } - if(_dumpHardware != null) - ProgressMaxValue++; - - if(_cicmMetadata != null) - ProgressMaxValue++; - + if(_dumpHardware != null) ProgressMaxValue++; - }); - foreach(MediaTagType mediaTag in _inputFormat.Info.ReadableMediaTags.Where(mediaTag => - !outputFormat.SupportedMediaTags.Contains(mediaTag) && !ForceChecked)) + if(_cicmMetadata != null) + ProgressMaxValue++; + + ProgressMaxValue++; + }); + + foreach(MediaTagType mediaTag in _inputFormat.Info.ReadableMediaTags.Where(mediaTag => + !outputFormat.SupportedMediaTags.Contains(mediaTag) && !ForceChecked)) + { + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Converting image will lose media tag {mediaTag}, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); + + return; + } + + bool useLong = _inputFormat.Info.ReadableSectorTags.Count != 0; + + foreach(SectorTagType sectorTag in _inputFormat.Info.ReadableSectorTags.Where(sectorTag => + !outputFormat.SupportedSectorTags.Contains(sectorTag))) + { + if(ForceChecked) { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Converting image will lose media tag {mediaTag}, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); + if(sectorTag != SectorTagType.CdTrackFlags && + sectorTag != SectorTagType.CdTrackIsrc && + sectorTag != SectorTagType.CdSectorSubchannel) + useLong = false; - return; + continue; } - bool useLong = _inputFormat.Info.ReadableSectorTags.Count != 0; + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Converting image will lose sector tag {sectorTag}, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); - foreach(SectorTagType sectorTag in _inputFormat.Info.ReadableSectorTags.Where(sectorTag => - !outputFormat.SupportedSectorTags.Contains(sectorTag))) + return; + } + + Dictionary parsedOptions = new(); + + /* TODO: + if(grpOptions.Content is StackLayout stkImageOptions) + foreach(Control option in stkImageOptions.Children) { - if(ForceChecked) + if(cancel) + break; + + string value; + + switch(option) { - if(sectorTag != SectorTagType.CdTrackFlags && - sectorTag != SectorTagType.CdTrackIsrc && - sectorTag != SectorTagType.CdSectorSubchannel) - useLong = false; + case CheckBox optBoolean: + value = optBooleanChecked?.ToString(); - continue; - } - - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Converting image will lose sector tag {sectorTag}, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - return; - } - - Dictionary parsedOptions = new(); - - /* TODO: - if(grpOptions.Content is StackLayout stkImageOptions) - foreach(Control option in stkImageOptions.Children) - { - if(cancel) break; + case NumericStepper optNumber: + value = optNumber.Value.ToString(CultureInfo.CurrentCulture); - string value; + break; + case TextBox optString: + value = optString.Text; - switch(option) - { - case CheckBox optBoolean: - value = optBooleanChecked?.ToString(); - - break; - case NumericStepper optNumber: - value = optNumber.Value.ToString(CultureInfo.CurrentCulture); - - break; - case TextBox optString: - value = optString.Text; - - break; - default: continue; - } - - string key = option.ID.Substring(3); - - parsedOptions.Add(key, value); + break; + default: continue; } - */ - await Dispatcher.UIThread.InvokeAsync(() => + string key = option.ID.Substring(3); + + parsedOptions.Add(key, value); + } + */ + + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = "Creating output image"; + Progress2Text = ""; + Progress2Indeterminate = true; + }); + + if(!outputFormat.Create(DestinationText, _inputFormat.Info.MediaType, parsedOptions, + _inputFormat.Info.Sectors, _inputFormat.Info.SectorSize)) + { + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {outputFormat.ErrorMessage} creating output image.", + icon: Icon.Error). + ShowDialog(_view)); + + AaruConsole.ErrorWriteLine("Error {0} creating output image.", outputFormat.ErrorMessage); + + return; + } + + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = "Setting image metadata"; + ProgressValue++; + Progress2Text = ""; + Progress2Indeterminate = true; + }); + + var metadata = new ImageInfo + { + Application = "Aaru", + ApplicationVersion = Version.GetVersion(), + Comments = CommentsText, + Creator = CreatorText, + DriveFirmwareRevision = DriveFirmwareRevisionText, + DriveManufacturer = DriveManufacturerText, + DriveModel = DriveModelText, + DriveSerialNumber = DriveSerialNumberText, + LastMediaSequence = (int)LastMediaSequenceValue, + MediaBarcode = MediaBarcodeText, + MediaManufacturer = MediaManufacturerText, + MediaModel = MediaModelText, + MediaPartNumber = MediaPartNumberText, + MediaSequence = (int)MediaSequenceValue, + MediaSerialNumber = MediaSerialNumberText, + MediaTitle = MediaTitleText + }; + + if(!_cancel) + if(!outputFormat.SetMetadata(metadata)) { - ProgressText = "Creating output image"; - Progress2Text = ""; - Progress2Indeterminate = true; - }); + AaruConsole.ErrorWrite("Error {0} setting metadata, ", outputFormat.ErrorMessage); - if(!outputFormat.Create(DestinationText, _inputFormat.Info.MediaType, parsedOptions, - _inputFormat.Info.Sectors, _inputFormat.Info.SectorSize)) - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} creating output image.", - icon: Icon.Error). - ShowDialog(_view)); + if(ForceChecked != true) + { + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {outputFormat.ErrorMessage} setting metadata, not continuing...", + icon: Icon.Error).ShowDialog(_view)); - AaruConsole.ErrorWriteLine("Error {0} creating output image.", outputFormat.ErrorMessage); + AaruConsole.ErrorWriteLine("not continuing..."); - return; + return; + } + + warning = true; + AaruConsole.ErrorWriteLine("continuing..."); } + if(tracks != null && + !_cancel && + outputOptical != null) + { await Dispatcher.UIThread.InvokeAsync(() => { - ProgressText = "Setting image metadata"; + ProgressText = "Setting tracks list"; ProgressValue++; Progress2Text = ""; Progress2Indeterminate = true; }); - var metadata = new ImageInfo + if(!outputOptical.SetTracks(tracks)) { - Application = "Aaru", - ApplicationVersion = Version.GetVersion(), - Comments = CommentsText, - Creator = CreatorText, - DriveFirmwareRevision = DriveFirmwareRevisionText, - DriveManufacturer = DriveManufacturerText, - DriveModel = DriveModelText, - DriveSerialNumber = DriveSerialNumberText, - LastMediaSequence = (int)LastMediaSequenceValue, - MediaBarcode = MediaBarcodeText, - MediaManufacturer = MediaManufacturerText, - MediaModel = MediaModelText, - MediaPartNumber = MediaPartNumberText, - MediaSequence = (int)MediaSequenceValue, - MediaSerialNumber = MediaSerialNumberText, - MediaTitle = MediaTitleText - }; + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {outputFormat.ErrorMessage} sending tracks list to output image.", + icon: Icon.Error). + ShowDialog(_view)); - if(!_cancel) - if(!outputFormat.SetMetadata(metadata)) + AaruConsole.ErrorWriteLine("Error {0} sending tracks list to output image.", + outputFormat.ErrorMessage); + + return; + } + } + + ErrorNumber errno; + + foreach(MediaTagType mediaTag in _inputFormat.Info.ReadableMediaTags.TakeWhile(mediaTag => !_cancel)) + { + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = $"Converting media tag {mediaTag}"; + ProgressValue++; + Progress2Text = ""; + Progress2Indeterminate = true; + }); + + if(ForceChecked && !outputFormat.SupportedMediaTags.Contains(mediaTag)) + continue; + + errno = _inputFormat.ReadMediaTag(mediaTag, out byte[] tag); + + if(errno == ErrorNumber.NoError && + outputFormat.WriteMediaTag(tag, mediaTag)) + continue; + + if(ForceChecked) + { + warning = true; + + if(errno == ErrorNumber.NoError) + AaruConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", + outputFormat.ErrorMessage); + else + AaruConsole.ErrorWriteLine("Error {0} reading media tag, continuing...", errno); + } + else + { + if(errno == ErrorNumber.NoError) { - AaruConsole.ErrorWrite("Error {0} setting metadata, ", outputFormat.ErrorMessage); + await Dispatcher.UIThread.InvokeAsync(action: async () => + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {outputFormat.ErrorMessage} writing media tag, not continuing...", + icon: Icon.Error).ShowDialog(_view)); - if(ForceChecked != true) + AaruConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...", + outputFormat.ErrorMessage); + } + else + { + await Dispatcher.UIThread.InvokeAsync(action: async () => + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {errno} reading media tag, not continuing...", + icon: Icon.Error).ShowDialog(_view)); + + AaruConsole.ErrorWriteLine("Error {0} reading media tag, not continuing...", errno); + } + + return; + } + } + + ulong doneSectors = 0; + + if(tracks == null && + !_cancel) + { + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = + $"Setting geometry to {_inputFormat.Info.Cylinders} cylinders, {_inputFormat.Info.Heads} heads and {_inputFormat.Info.SectorsPerTrack} sectors per track"; + + ProgressValue++; + Progress2Text = ""; + Progress2Indeterminate = true; + }); + + if(!outputFormat.SetGeometry(_inputFormat.Info.Cylinders, _inputFormat.Info.Heads, + _inputFormat.Info.SectorsPerTrack)) + { + warning = true; + + AaruConsole.ErrorWriteLine("Error {0} setting geometry, image may be incorrect, continuing...", + outputFormat.ErrorMessage); + } + + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = "Converting sectors"; + ProgressValue++; + Progress2Text = ""; + Progress2Indeterminate = false; + Progress2MaxValue = (int)(_inputFormat.Info.Sectors / SectorsValue); + }); + + while(doneSectors < _inputFormat.Info.Sectors) + { + if(_cancel) + break; + + byte[] sector; + + uint sectorsToDo; + + if(_inputFormat.Info.Sectors - doneSectors >= (ulong)SectorsValue) + sectorsToDo = (uint)SectorsValue; + else + sectorsToDo = (uint)(_inputFormat.Info.Sectors - doneSectors); + + ulong sectors = doneSectors; + + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Text = + $"Converting sectors {sectors} to {sectors + sectorsToDo} ({sectors / (double)_inputFormat.Info.Sectors:P2} done)"; + + Progress2Value = (int)(sectors / SectorsValue); + }); + + bool result; + + if(useLong) + { + errno = sectorsToDo == 1 ? _inputFormat.ReadSectorLong(doneSectors, out sector) + : _inputFormat.ReadSectorsLong(doneSectors, sectorsToDo, out sector); + + if(errno == ErrorNumber.NoError) + result = sectorsToDo == 1 ? outputFormat.WriteSectorLong(sector, doneSectors) + : outputFormat.WriteSectorsLong(sector, doneSectors, sectorsToDo); + else + { + result = true; + + if(ForceChecked) + { + warning = true; + + AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, continuing...", errno, + doneSectors); + } + else + { + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {errno} reading sector {doneSectors}, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); + + AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, not continuing...", errno, + doneSectors); + + return; + } + } + } + else + { + errno = sectorsToDo == 1 ? _inputFormat.ReadSector(doneSectors, out sector) + : _inputFormat.ReadSectors(doneSectors, sectorsToDo, out sector); + + if(errno == ErrorNumber.NoError) + result = sectorsToDo == 1 ? outputFormat.WriteSector(sector, doneSectors) + : outputFormat.WriteSectors(sector, doneSectors, sectorsToDo); + else + { + result = true; + + if(ForceChecked) + { + warning = true; + + AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, continuing...", errno, + doneSectors); + } + else + { + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {errno} reading sector {doneSectors}, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); + + AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, not continuing...", errno, + doneSectors); + + return; + } + } + } + + if(!result) + if(ForceChecked) + { + warning = true; + + AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", + outputFormat.ErrorMessage, doneSectors); + } + else { await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} setting metadata, not continuing...", + $"Error {outputFormat.ErrorMessage} writing sector {doneSectors}, not continuing...", icon: Icon.Error).ShowDialog(_view)); - AaruConsole.ErrorWriteLine("not continuing..."); + AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...", + outputFormat.ErrorMessage, doneSectors); return; } - warning = true; - AaruConsole.ErrorWriteLine("continuing..."); - } + doneSectors += sectorsToDo; + } - if(tracks != null && - !_cancel && - outputOptical != null) + await Dispatcher.UIThread.InvokeAsync(() => { - await Dispatcher.UIThread.InvokeAsync(() => + Progress2Text = + $"Converting sectors {_inputFormat.Info.Sectors} to {_inputFormat.Info.Sectors} ({1.0:P2} done)"; + + Progress2Value = Progress2MaxValue; + }); + + Dictionary isrcs = new(); + Dictionary trackFlags = new(); + string mcn = null; + HashSet subchannelExtents = new(); + Dictionary smallestPregapLbaPerTrack = new(); + + foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags. + Where(t => t == SectorTagType.CdTrackIsrc).OrderBy(t => t)) + { + foreach(Track track in inputOptical.Tracks) { - ProgressText = "Setting tracks list"; - ProgressValue++; - Progress2Text = ""; - Progress2Indeterminate = true; - }); + errno = _inputFormat.ReadSectorTag(track.Sequence, tag, out byte[] isrc); - if(!outputOptical.SetTracks(tracks)) - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} sending tracks list to output image.", - icon: Icon.Error). - ShowDialog(_view)); + if(errno != ErrorNumber.NoError) + continue; - AaruConsole.ErrorWriteLine("Error {0} sending tracks list to output image.", - outputFormat.ErrorMessage); - - return; + isrcs[(byte)track.Sequence] = Encoding.UTF8.GetString(isrc); } } - ErrorNumber errno; - - foreach(MediaTagType mediaTag in _inputFormat.Info.ReadableMediaTags.TakeWhile(mediaTag => !_cancel)) + foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags. + Where(t => t == SectorTagType.CdTrackFlags).OrderBy(t => t)) { - await Dispatcher.UIThread.InvokeAsync(() => + foreach(Track track in inputOptical.Tracks) { - ProgressText = $"Converting media tag {mediaTag}"; - ProgressValue++; - Progress2Text = ""; - Progress2Indeterminate = true; - }); + errno = _inputFormat.ReadSectorTag(track.Sequence, tag, out byte[] flags); - if(ForceChecked && !outputFormat.SupportedMediaTags.Contains(mediaTag)) - continue; + if(errno != ErrorNumber.NoError) + continue; - errno = _inputFormat.ReadMediaTag(mediaTag, out byte[] tag); - - if(errno == ErrorNumber.NoError && - outputFormat.WriteMediaTag(tag, mediaTag)) - continue; - - if(ForceChecked) - { - warning = true; - - if(errno == ErrorNumber.NoError) - AaruConsole.ErrorWriteLine("Error {0} writing media tag, continuing...", - outputFormat.ErrorMessage); - else - AaruConsole.ErrorWriteLine("Error {0} reading media tag, continuing...", errno); - } - else - { - if(errno == ErrorNumber.NoError) - { - await Dispatcher.UIThread.InvokeAsync(action: async () => - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} writing media tag, not continuing...", - icon: Icon.Error).ShowDialog(_view)); - - AaruConsole.ErrorWriteLine("Error {0} writing media tag, not continuing...", - outputFormat.ErrorMessage); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {errno} reading media tag, not continuing...", - icon: Icon.Error).ShowDialog(_view)); - - AaruConsole.ErrorWriteLine("Error {0} reading media tag, not continuing...", errno); - } - - return; + trackFlags[(byte)track.Sequence] = flags[0]; } } - ulong doneSectors = 0; - - if(tracks == null && - !_cancel) + for(ulong s = 0; s < _inputFormat.Info.Sectors; s++) { - await Dispatcher.UIThread.InvokeAsync(() => + if(s > int.MaxValue) + break; + + subchannelExtents.Add((int)s); + } + + foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags.TakeWhile(tag => useLong && !_cancel)) + { + switch(tag) { - ProgressText = - $"Setting geometry to {_inputFormat.Info.Cylinders} cylinders, {_inputFormat.Info.Heads} heads and {_inputFormat.Info.SectorsPerTrack} sectors per track"; - - ProgressValue++; - Progress2Text = ""; - Progress2Indeterminate = true; - }); - - if(!outputFormat.SetGeometry(_inputFormat.Info.Cylinders, _inputFormat.Info.Heads, - _inputFormat.Info.SectorsPerTrack)) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} setting geometry, image may be incorrect, continuing...", - outputFormat.ErrorMessage); + case SectorTagType.AppleSectorTag: + case SectorTagType.CdSectorSync: + case SectorTagType.CdSectorHeader: + case SectorTagType.CdSectorSubHeader: + case SectorTagType.CdSectorEdc: + case SectorTagType.CdSectorEccP: + case SectorTagType.CdSectorEccQ: + case SectorTagType.CdSectorEcc: + // This tags are inline in long sector + continue; } + if(ForceChecked && !outputFormat.SupportedSectorTags.Contains(tag)) + continue; + await Dispatcher.UIThread.InvokeAsync(() => { - ProgressText = "Converting sectors"; + ProgressText = $"Converting tag {tag}"; ProgressValue++; Progress2Text = ""; Progress2Indeterminate = false; Progress2MaxValue = (int)(_inputFormat.Info.Sectors / SectorsValue); }); + doneSectors = 0; + while(doneSectors < _inputFormat.Info.Sectors) { if(_cancel) @@ -953,21 +1152,37 @@ namespace Aaru.Gui.ViewModels.Windows await Dispatcher.UIThread.InvokeAsync(() => { Progress2Text = - $"Converting sectors {sectors} to {sectors + sectorsToDo} ({sectors / (double)_inputFormat.Info.Sectors:P2} done)"; + $"Converting tag {sectors / (double)_inputFormat.Info.Sectors} for sectors {sectors} to {sectors + sectorsToDo} ({sectors / (double)_inputFormat.Info.Sectors:P2} done)"; Progress2Value = (int)(sectors / SectorsValue); }); bool result; - if(useLong) + if(sectorsToDo == 1) { - errno = sectorsToDo == 1 ? _inputFormat.ReadSectorLong(doneSectors, out sector) - : _inputFormat.ReadSectorsLong(doneSectors, sectorsToDo, out sector); + errno = _inputFormat.ReadSectorTag(doneSectors, tag, out sector); if(errno == ErrorNumber.NoError) - result = sectorsToDo == 1 ? outputFormat.WriteSectorLong(sector, doneSectors) - : outputFormat.WriteSectorsLong(sector, doneSectors, sectorsToDo); + { + Track track = tracks.LastOrDefault(t => t.StartSector >= doneSectors); + + if(tag == SectorTagType.CdSectorSubchannel && + track != null) + { + bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw, + MmcSubchannel.Raw, sector, doneSectors, 1, null, isrcs, + (byte)track.Sequence, ref mcn, tracks.ToArray(), subchannelExtents, false, + outputFormat as IWritableOpticalImage, false, false, null, null, smallestPregapLbaPerTrack, false); + + if(indexesChanged) + outputOptical.SetTracks(tracks.ToList()); + + result = true; + } + else + result = outputFormat.WriteSectorTag(sector, doneSectors, tag); + } else { result = true; @@ -996,12 +1211,29 @@ namespace Aaru.Gui.ViewModels.Windows } else { - errno = sectorsToDo == 1 ? _inputFormat.ReadSector(doneSectors, out sector) - : _inputFormat.ReadSectors(doneSectors, sectorsToDo, out sector); + errno = _inputFormat.ReadSectorsTag(doneSectors, sectorsToDo, tag, out sector); if(errno == ErrorNumber.NoError) - result = sectorsToDo == 1 ? outputFormat.WriteSector(sector, doneSectors) - : outputFormat.WriteSectors(sector, doneSectors, sectorsToDo); + { + Track track = tracks.LastOrDefault(t => t.StartSector >= doneSectors); + + if(tag == SectorTagType.CdSectorSubchannel && + track != null) + + { + bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw, + MmcSubchannel.Raw, sector, doneSectors, sectorsToDo, null, isrcs, + (byte)track.Sequence, ref mcn, tracks.ToArray(), subchannelExtents, false, + outputFormat as IWritableOpticalImage, false, false, null, null, smallestPregapLbaPerTrack, false); + + if(indexesChanged) + outputOptical.SetTracks(tracks.ToList()); + + result = true; + } + else + result = outputFormat.WriteSectorsTag(sector, doneSectors, sectorsToDo, tag); + } else { result = true; @@ -1042,7 +1274,8 @@ namespace Aaru.Gui.ViewModels.Windows await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. GetMessageBoxStandardWindow("Error", $"Error {outputFormat.ErrorMessage} writing sector {doneSectors}, not continuing...", - icon: Icon.Error).ShowDialog(_view)); + icon: Icon.Error). + ShowDialog(_view)); AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...", outputFormat.ErrorMessage, doneSectors); @@ -1056,283 +1289,267 @@ namespace Aaru.Gui.ViewModels.Windows await Dispatcher.UIThread.InvokeAsync(() => { Progress2Text = - $"Converting sectors {_inputFormat.Info.Sectors} to {_inputFormat.Info.Sectors} ({1.0:P2} done)"; + $"Converting tag {tag} for sectors {_inputFormat.Info.Sectors} to {_inputFormat.Info.Sectors} ({1.0:P2} done)"; Progress2Value = Progress2MaxValue; }); - Dictionary isrcs = new(); - Dictionary trackFlags = new(); - string mcn = null; - HashSet subchannelExtents = new(); - Dictionary smallestPregapLbaPerTrack = new(); + if(isrcs.Count > 0) + foreach(KeyValuePair isrc in isrcs) + outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key, + SectorTagType.CdTrackIsrc); - foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags. - Where(t => t == SectorTagType.CdTrackIsrc).OrderBy(t => t)) + if(trackFlags.Count > 0) + foreach(KeyValuePair flags in trackFlags) + outputOptical.WriteSectorTag(new[] + { + flags.Value + }, flags.Key, SectorTagType.CdTrackFlags); + + if(mcn != null) + outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN); + } + } + else + { + foreach(Track track in tracks.TakeWhile(track => !_cancel)) + { + doneSectors = 0; + ulong trackSectors = track.EndSector - track.StartSector + 1; + + await Dispatcher.UIThread.InvokeAsync(() => { - foreach(Track track in inputOptical.Tracks) - { - errno = _inputFormat.ReadSectorTag(track.Sequence, tag, out byte[] isrc); + ProgressText = $"Converting sectors in track {track.Sequence}"; + ProgressValue++; + Progress2Text = ""; + Progress2Indeterminate = false; + Progress2MaxValue = (int)(trackSectors / SectorsValue); + }); - if(errno != ErrorNumber.NoError) - continue; - - isrcs[(byte)track.Sequence] = Encoding.UTF8.GetString(isrc); - } - } - - foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags. - Where(t => t == SectorTagType.CdTrackFlags).OrderBy(t => t)) + while(doneSectors < trackSectors) { - foreach(Track track in inputOptical.Tracks) - { - errno = _inputFormat.ReadSectorTag(track.Sequence, tag, out byte[] flags); - - if(errno != ErrorNumber.NoError) - continue; - - trackFlags[(byte)track.Sequence] = flags[0]; - } - } - - for(ulong s = 0; s < _inputFormat.Info.Sectors; s++) - { - if(s > int.MaxValue) + if(_cancel) break; - subchannelExtents.Add((int)s); - } + byte[] sector; - foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags.TakeWhile(tag => useLong && !_cancel)) - { - switch(tag) - { - case SectorTagType.AppleSectorTag: - case SectorTagType.CdSectorSync: - case SectorTagType.CdSectorHeader: - case SectorTagType.CdSectorSubHeader: - case SectorTagType.CdSectorEdc: - case SectorTagType.CdSectorEccP: - case SectorTagType.CdSectorEccQ: - case SectorTagType.CdSectorEcc: - // This tags are inline in long sector - continue; - } + uint sectorsToDo; - if(ForceChecked && !outputFormat.SupportedSectorTags.Contains(tag)) - continue; + if(trackSectors - doneSectors >= (ulong)SectorsValue) + sectorsToDo = (uint)SectorsValue; + else + sectorsToDo = (uint)(trackSectors - doneSectors); + + ulong sectors = doneSectors; await Dispatcher.UIThread.InvokeAsync(() => { - ProgressText = $"Converting tag {tag}"; - ProgressValue++; - Progress2Text = ""; - Progress2Indeterminate = false; - Progress2MaxValue = (int)(_inputFormat.Info.Sectors / SectorsValue); + Progress2Text = + $"Converting sectors {sectors + track.StartSector} to {sectors + sectorsToDo + track.StartSector} in track {track.Sequence} ({(sectors + track.StartSector) / (double)_inputFormat.Info.Sectors:P2} done)"; + + Progress2Value = (int)(sectors / SectorsValue); }); - doneSectors = 0; + bool result; - while(doneSectors < _inputFormat.Info.Sectors) + if(useLong) { - if(_cancel) - break; + errno = sectorsToDo == 1 + ? _inputFormat.ReadSectorLong(doneSectors + track.StartSector, out sector) + : _inputFormat.ReadSectorsLong(doneSectors + track.StartSector, sectorsToDo, + out sector); - byte[] sector; - - uint sectorsToDo; - - if(_inputFormat.Info.Sectors - doneSectors >= (ulong)SectorsValue) - sectorsToDo = (uint)SectorsValue; - else - sectorsToDo = (uint)(_inputFormat.Info.Sectors - doneSectors); - - ulong sectors = doneSectors; - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Text = - $"Converting tag {sectors / (double)_inputFormat.Info.Sectors} for sectors {sectors} to {sectors + sectorsToDo} ({sectors / (double)_inputFormat.Info.Sectors:P2} done)"; - - Progress2Value = (int)(sectors / SectorsValue); - }); - - bool result; - - if(sectorsToDo == 1) - { - errno = _inputFormat.ReadSectorTag(doneSectors, tag, out sector); - - if(errno == ErrorNumber.NoError) - { - Track track = tracks.LastOrDefault(t => t.StartSector >= doneSectors); - - if(tag == SectorTagType.CdSectorSubchannel && - track != null) - { - bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw, - MmcSubchannel.Raw, sector, doneSectors, 1, null, isrcs, - (byte)track.Sequence, ref mcn, tracks.ToArray(), subchannelExtents, false, - outputFormat as IWritableOpticalImage, false, false, null, null, smallestPregapLbaPerTrack, false); - - if(indexesChanged) - outputOptical.SetTracks(tracks.ToList()); - - result = true; - } - else - result = outputFormat.WriteSectorTag(sector, doneSectors, tag); - } - else - { - result = true; - - if(ForceChecked) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, continuing...", errno, - doneSectors); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {errno} reading sector {doneSectors}, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, not continuing...", errno, - doneSectors); - - return; - } - } - } + if(errno == ErrorNumber.NoError) + result = sectorsToDo == 1 + ? outputFormat.WriteSectorLong(sector, doneSectors + track.StartSector) + : outputFormat.WriteSectorsLong(sector, doneSectors + track.StartSector, + sectorsToDo); else { - errno = _inputFormat.ReadSectorsTag(doneSectors, sectorsToDo, tag, out sector); + result = true; - if(errno == ErrorNumber.NoError) - { - Track track = tracks.LastOrDefault(t => t.StartSector >= doneSectors); - - if(tag == SectorTagType.CdSectorSubchannel && - track != null) - - { - bool indexesChanged = CompactDisc.WriteSubchannelToImage(MmcSubchannel.Raw, - MmcSubchannel.Raw, sector, doneSectors, sectorsToDo, null, isrcs, - (byte)track.Sequence, ref mcn, tracks.ToArray(), subchannelExtents, false, - outputFormat as IWritableOpticalImage, false, false, null, null, smallestPregapLbaPerTrack, false); - - if(indexesChanged) - outputOptical.SetTracks(tracks.ToList()); - - result = true; - } - else - result = outputFormat.WriteSectorsTag(sector, doneSectors, sectorsToDo, tag); - } - else - { - result = true; - - if(ForceChecked) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, continuing...", errno, - doneSectors); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {errno} reading sector {doneSectors}, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, not continuing...", errno, - doneSectors); - - return; - } - } - } - - if(!result) if(ForceChecked) { warning = true; - AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", - outputFormat.ErrorMessage, doneSectors); + AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, continuing...", errno, + doneSectors); } else { await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} writing sector {doneSectors}, not continuing...", + $"Error {errno} reading sector {doneSectors}, not continuing...", icon: Icon.Error). ShowDialog(_view)); - AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, not continuing...", - outputFormat.ErrorMessage, doneSectors); + return; + } + } + } + else + { + errno = sectorsToDo == 1 + ? _inputFormat.ReadSector(doneSectors + track.StartSector, out sector) + : _inputFormat.ReadSectors(doneSectors + track.StartSector, sectorsToDo, + out sector); + + if(errno == ErrorNumber.NoError) + result = sectorsToDo == 1 + ? outputFormat.WriteSector(sector, doneSectors + track.StartSector) + : outputFormat.WriteSectors(sector, doneSectors + track.StartSector, + sectorsToDo); + else + { + result = true; + + if(ForceChecked) + { + warning = true; + + AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, continuing...", errno, + doneSectors); + } + else + { + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {errno} reading sector {doneSectors}, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); return; } - - doneSectors += sectorsToDo; + } } - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Text = - $"Converting tag {tag} for sectors {_inputFormat.Info.Sectors} to {_inputFormat.Info.Sectors} ({1.0:P2} done)"; + if(!result) + if(ForceChecked) + { + warning = true; - Progress2Value = Progress2MaxValue; - }); + AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", + outputFormat.ErrorMessage, doneSectors); + } + else + { + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {outputFormat.ErrorMessage} writing sector {doneSectors}, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); - if(isrcs.Count > 0) - foreach(KeyValuePair isrc in isrcs) - outputOptical.WriteSectorTag(Encoding.UTF8.GetBytes(isrc.Value), isrc.Key, - SectorTagType.CdTrackIsrc); + return; + } - if(trackFlags.Count > 0) - foreach(KeyValuePair flags in trackFlags) - outputOptical.WriteSectorTag(new[] - { - flags.Value - }, flags.Key, SectorTagType.CdTrackFlags); - - if(mcn != null) - outputOptical.WriteMediaTag(Encoding.UTF8.GetBytes(mcn), MediaTagType.CD_MCN); + doneSectors += sectorsToDo; } } - else + + await Dispatcher.UIThread.InvokeAsync(() => { + Progress2Text = + $"Converting sectors {_inputFormat.Info.Sectors} to {_inputFormat.Info.Sectors} in track {tracks.Count} ({1.0:P2} done)"; + + Progress2Value = Progress2MaxValue; + }); + + foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags.OrderBy(t => t). + TakeWhile(tag => useLong && !_cancel)) + { + switch(tag) + { + case SectorTagType.AppleSectorTag: + case SectorTagType.CdSectorSync: + case SectorTagType.CdSectorHeader: + case SectorTagType.CdSectorSubHeader: + case SectorTagType.CdSectorEdc: + case SectorTagType.CdSectorEccP: + case SectorTagType.CdSectorEccQ: + case SectorTagType.CdSectorEcc: + // This tags are inline in long sector + continue; + } + + if(ForceChecked && !outputFormat.SupportedSectorTags.Contains(tag)) + continue; + foreach(Track track in tracks.TakeWhile(track => !_cancel)) { doneSectors = 0; - ulong trackSectors = track.EndSector - track.StartSector + 1; + ulong trackSectors = track.EndSector - track.StartSector + 1; + byte[] sector; + bool result; await Dispatcher.UIThread.InvokeAsync(() => { - ProgressText = $"Converting sectors in track {track.Sequence}"; + ProgressText = $"Converting tag {tag} in track {track.Sequence}."; ProgressValue++; Progress2Text = ""; Progress2Indeterminate = false; Progress2MaxValue = (int)(trackSectors / SectorsValue); }); + switch(tag) + { + case SectorTagType.CdTrackFlags: + case SectorTagType.CdTrackIsrc: + + errno = _inputFormat.ReadSectorTag(track.Sequence, tag, out sector); + + if(errno == ErrorNumber.NoError) + result = outputFormat.WriteSectorTag(sector, track.Sequence, tag); + else + { + if(ForceChecked) + { + warning = true; + + AaruConsole.ErrorWriteLine("Error {0} reading tag, continuing...", errno); + } + else + { + await Dispatcher.UIThread.InvokeAsync(action: async () => + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {errno} reading tag, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); + + return; + } + + continue; + } + + if(!result) + if(ForceChecked) + { + warning = true; + + AaruConsole.ErrorWriteLine("Error {0} writing tag, continuing...", + outputFormat.ErrorMessage); + } + else + { + await Dispatcher.UIThread.InvokeAsync(action: async () => + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {outputFormat.ErrorMessage} writing tag, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); + + return; + } + + continue; + } + while(doneSectors < trackSectors) { if(_cancel) break; - byte[] sector; - uint sectorsToDo; if(trackSectors - doneSectors >= (ulong)SectorsValue) @@ -1345,81 +1562,43 @@ namespace Aaru.Gui.ViewModels.Windows await Dispatcher.UIThread.InvokeAsync(() => { Progress2Text = - $"Converting sectors {sectors + track.StartSector} to {sectors + sectorsToDo + track.StartSector} in track {track.Sequence} ({(sectors + track.StartSector) / (double)_inputFormat.Info.Sectors:P2} done)"; + $"Converting tag {tag} for sectors {sectors + track.StartSector} to {sectors + sectorsToDo + track.StartSector} in track {track.Sequence} ({(sectors + track.StartSector) / (double)_inputFormat.Info.Sectors:P2} done)"; Progress2Value = (int)(sectors / SectorsValue); }); - bool result; + errno = sectorsToDo == 1 + ? _inputFormat.ReadSectorTag(doneSectors + track.StartSector, tag, out sector) + : _inputFormat.ReadSectorsTag(doneSectors + track.StartSector, sectorsToDo, tag, + out sector); - if(useLong) + if(errno == ErrorNumber.NoError) { - errno = sectorsToDo == 1 - ? _inputFormat.ReadSectorLong(doneSectors + track.StartSector, out sector) - : _inputFormat.ReadSectorsLong(doneSectors + track.StartSector, sectorsToDo, - out sector); - - if(errno == ErrorNumber.NoError) - result = sectorsToDo == 1 - ? outputFormat.WriteSectorLong(sector, doneSectors + track.StartSector) - : outputFormat.WriteSectorsLong(sector, doneSectors + track.StartSector, - sectorsToDo); - else - { - result = true; - - if(ForceChecked) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, continuing...", errno, - doneSectors); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {errno} reading sector {doneSectors}, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - return; - } - } + result = sectorsToDo == 1 + ? outputFormat.WriteSectorTag(sector, doneSectors + track.StartSector, tag) + : outputFormat.WriteSectorsTag(sector, doneSectors + track.StartSector, + sectorsToDo, tag); } else { - errno = sectorsToDo == 1 - ? _inputFormat.ReadSector(doneSectors + track.StartSector, out sector) - : _inputFormat.ReadSectors(doneSectors + track.StartSector, sectorsToDo, - out sector); + result = true; - if(errno == ErrorNumber.NoError) - result = sectorsToDo == 1 - ? outputFormat.WriteSector(sector, doneSectors + track.StartSector) - : outputFormat.WriteSectors(sector, doneSectors + track.StartSector, - sectorsToDo); + if(ForceChecked) + { + warning = true; + + AaruConsole.ErrorWriteLine("Error {0} reading tag for sector {1}, continuing...", + errno, doneSectors); + } else { - result = true; + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {errno} reading tag for sector {doneSectors}, not continuing...", + icon: Icon.Error). + ShowDialog(_view)); - if(ForceChecked) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} reading sector {1}, continuing...", errno, - doneSectors); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {errno} reading sector {doneSectors}, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - return; - } + return; } } @@ -1428,14 +1607,14 @@ namespace Aaru.Gui.ViewModels.Windows { warning = true; - AaruConsole.ErrorWriteLine("Error {0} writing sector {1}, continuing...", + AaruConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, continuing...", outputFormat.ErrorMessage, doneSectors); } else { await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} writing sector {doneSectors}, not continuing...", + $"Error {outputFormat.ErrorMessage} writing tag for sector {doneSectors}, not continuing...", icon: Icon.Error). ShowDialog(_view)); @@ -1445,587 +1624,407 @@ namespace Aaru.Gui.ViewModels.Windows doneSectors += sectorsToDo; } } - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Text = - $"Converting sectors {_inputFormat.Info.Sectors} to {_inputFormat.Info.Sectors} in track {tracks.Count} ({1.0:P2} done)"; - - Progress2Value = Progress2MaxValue; - }); - - foreach(SectorTagType tag in _inputFormat.Info.ReadableSectorTags.OrderBy(t => t). - TakeWhile(tag => useLong && !_cancel)) - { - switch(tag) - { - case SectorTagType.AppleSectorTag: - case SectorTagType.CdSectorSync: - case SectorTagType.CdSectorHeader: - case SectorTagType.CdSectorSubHeader: - case SectorTagType.CdSectorEdc: - case SectorTagType.CdSectorEccP: - case SectorTagType.CdSectorEccQ: - case SectorTagType.CdSectorEcc: - // This tags are inline in long sector - continue; - } - - if(ForceChecked && !outputFormat.SupportedSectorTags.Contains(tag)) - continue; - - foreach(Track track in tracks.TakeWhile(track => !_cancel)) - { - doneSectors = 0; - ulong trackSectors = track.EndSector - track.StartSector + 1; - byte[] sector; - bool result; - - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = $"Converting tag {tag} in track {track.Sequence}."; - ProgressValue++; - Progress2Text = ""; - Progress2Indeterminate = false; - Progress2MaxValue = (int)(trackSectors / SectorsValue); - }); - - switch(tag) - { - case SectorTagType.CdTrackFlags: - case SectorTagType.CdTrackIsrc: - - errno = _inputFormat.ReadSectorTag(track.Sequence, tag, out sector); - - if(errno == ErrorNumber.NoError) - result = outputFormat.WriteSectorTag(sector, track.Sequence, tag); - else - { - if(ForceChecked) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} reading tag, continuing...", errno); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {errno} reading tag, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - return; - } - - continue; - } - - if(!result) - if(ForceChecked) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} writing tag, continuing...", - outputFormat.ErrorMessage); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} writing tag, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - return; - } - - continue; - } - - while(doneSectors < trackSectors) - { - if(_cancel) - break; - - uint sectorsToDo; - - if(trackSectors - doneSectors >= (ulong)SectorsValue) - sectorsToDo = (uint)SectorsValue; - else - sectorsToDo = (uint)(trackSectors - doneSectors); - - ulong sectors = doneSectors; - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Text = - $"Converting tag {tag} for sectors {sectors + track.StartSector} to {sectors + sectorsToDo + track.StartSector} in track {track.Sequence} ({(sectors + track.StartSector) / (double)_inputFormat.Info.Sectors:P2} done)"; - - Progress2Value = (int)(sectors / SectorsValue); - }); - - errno = sectorsToDo == 1 - ? _inputFormat.ReadSectorTag(doneSectors + track.StartSector, tag, out sector) - : _inputFormat.ReadSectorsTag(doneSectors + track.StartSector, sectorsToDo, tag, - out sector); - - if(errno == ErrorNumber.NoError) - { - result = sectorsToDo == 1 - ? outputFormat.WriteSectorTag(sector, doneSectors + track.StartSector, tag) - : outputFormat.WriteSectorsTag(sector, doneSectors + track.StartSector, - sectorsToDo, tag); - } - else - { - result = true; - - if(ForceChecked) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} reading tag for sector {1}, continuing...", - errno, doneSectors); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {errno} reading tag for sector {doneSectors}, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - return; - } - } - - if(!result) - if(ForceChecked) - { - warning = true; - - AaruConsole.ErrorWriteLine("Error {0} writing tag for sector {1}, continuing...", - outputFormat.ErrorMessage, doneSectors); - } - else - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} writing tag for sector {doneSectors}, not continuing...", - icon: Icon.Error). - ShowDialog(_view)); - - return; - } - - doneSectors += sectorsToDo; - } - } - } } + } + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Visible = false; + Progress2Visible = false; + }); + + bool ret; + + if(_dumpHardware != null && + !_cancel) + { await Dispatcher.UIThread.InvokeAsync(() => { - Progress2Visible = false; - Progress2Visible = false; + ProgressText = "Writing dump hardware list to output image."; + ProgressValue++; }); - bool ret; + ret = outputFormat.SetDumpHardware(_dumpHardware); - if(_dumpHardware != null && - !_cancel) - { - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = "Writing dump hardware list to output image."; - ProgressValue++; - }); + if(!ret) + AaruConsole.WriteLine("Error {0} writing dump hardware list to output image.", + outputFormat.ErrorMessage); + } - ret = outputFormat.SetDumpHardware(_dumpHardware); - - if(!ret) - AaruConsole.WriteLine("Error {0} writing dump hardware list to output image.", - outputFormat.ErrorMessage); - } - - ret = false; - - if(_cicmMetadata != null && - !_cancel) - { - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = "Writing CICM XML metadata to output image."; - ProgressValue++; - }); - - outputFormat.SetCicmMetadata(_cicmMetadata); - - if(!ret) - AaruConsole.WriteLine("Error {0} writing CICM XML metadata to output image.", - outputFormat.ErrorMessage); - } + ret = false; + if(_cicmMetadata != null && + !_cancel) + { await Dispatcher.UIThread.InvokeAsync(() => { - ProgressText = "Closing output image."; - ProgressIndeterminate = true; + ProgressText = "Writing CICM XML metadata to output image."; + ProgressValue++; }); - if(_cancel) - { - await Dispatcher.UIThread.InvokeAsync(action: async () => - { - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", "Operation canceled, the output file is not correct.", - icon: Icon.Error).ShowDialog(_view); + outputFormat.SetCicmMetadata(_cicmMetadata); - CloseVisible = true; - StopVisible = false; - ProgressVisible = false; - }); + if(!ret) + AaruConsole.WriteLine("Error {0} writing CICM XML metadata to output image.", + outputFormat.ErrorMessage); + } - return; - } - - if(!outputFormat.Close()) - { - await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - $"Error {outputFormat.ErrorMessage} closing output image... Contents are not correct.", - icon: Icon.Error). - ShowDialog(_view)); - - return; - } + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = "Closing output image."; + ProgressIndeterminate = true; + }); + if(_cancel) + { await Dispatcher.UIThread.InvokeAsync(action: async () => { - await MessageBoxManager.GetMessageBoxStandardWindow(warning ? "Warning" : "Conversion success", - warning - ? "Some warnings happened. Check console for more information. Image should be correct." - : "Image converted successfully.", - icon: warning ? Icon.Warning : Icon.Info). - ShowDialog(_view); + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", "Operation canceled, the output file is not correct.", + icon: Icon.Error).ShowDialog(_view); CloseVisible = true; StopVisible = false; ProgressVisible = false; }); - Statistics.AddCommand("convert-image"); + return; } - void ExecuteCloseCommand() => _view.Close(); - - internal void ExecuteStopCommand() + if(!outputFormat.Close()) { - _cancel = true; - StopEnabled = false; + await Dispatcher.UIThread.InvokeAsync(action: async () => await MessageBoxManager. + GetMessageBoxStandardWindow("Error", + $"Error {outputFormat.ErrorMessage} closing output image... Contents are not correct.", + icon: Icon.Error). + ShowDialog(_view)); + + return; } - /* TODO - void OnCmbFormatSelectedIndexChanged() + await Dispatcher.UIThread.InvokeAsync(action: async () => + { + await MessageBoxManager.GetMessageBoxStandardWindow(warning ? "Warning" : "Conversion success", + warning + ? "Some warnings happened. Check console for more information. Image should be correct." + : "Image converted successfully.", + icon: warning ? Icon.Warning : Icon.Info). + ShowDialog(_view); + + CloseVisible = true; + StopVisible = false; + ProgressVisible = false; + }); + + Statistics.AddCommand("convert-image"); + } + + void ExecuteCloseCommand() => _view.Close(); + + internal void ExecuteStopCommand() + { + _cancel = true; + StopEnabled = false; + } + + /* TODO + void OnCmbFormatSelectedIndexChanged() + { + txtDestination.Text = ""; + + if(!(cmbFormat.SelectedValue is IWritableImage plugin)) { - txtDestination.Text = ""; + grpOptions.Visible = false; + btnDestination.Enabled = false; - if(!(cmbFormat.SelectedValue is IWritableImage plugin)) - { - grpOptions.Visible = false; - btnDestination.Enabled = false; - - return; - } - - btnDestination.Enabled = true; - - if(!plugin.SupportedOptions.Any()) - { - grpOptions.Content = null; - grpOptions.Visible = false; - - return; - } - - chkForce.Visible = false; - - foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags) - { - if(plugin.SupportedMediaTags.Contains(mediaTag)) - continue; - - chkForce.Visible = true; - ForceChecked = true; - - break; - } - - foreach(SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags) - { - if(plugin.SupportedSectorTags.Contains(sectorTag)) - continue; - - chkForce.Visible = true; - ForceChecked = true; - - break; - } - - grpOptions.Visible = true; - - var stkImageOptions = new StackLayout - { - Orientation = Orientation.Vertical - }; - - foreach((string name, Type type, string description, object @default) option in plugin.SupportedOptions) - switch(option.type.ToString()) - { - case "System.Boolean": - var optBoolean = new CheckBox(); - optBoolean.ID = "opt" + option.name; - optBoolean.Text = option.description; - optBooleanChecked = (bool)option.@default; - stkImageOptions.Items.Add(optBoolean); - - break; - case "System.SByte": - case "System.Int16": - case "System.Int32": - case "System.Int64": - var stkNumber = new StackLayout(); - stkNumber.Orientation = Orientation.Horizontal; - var optNumber = new NumericStepper(); - optNumber.ID = "opt" + option.name; - optNumber.Value = Convert.ToDouble(option.@default); - stkNumber.Items.Add(optNumber); - var lblNumber = new Label(); - lblNumber.Text = option.description; - stkNumber.Items.Add(lblNumber); - stkImageOptions.Items.Add(stkNumber); - - break; - case "System.Byte": - case "System.UInt16": - case "System.UInt32": - case "System.UInt64": - var stkUnsigned = new StackLayout(); - stkUnsigned.Orientation = Orientation.Horizontal; - var optUnsigned = new NumericStepper(); - optUnsigned.ID = "opt" + option.name; - optUnsigned.MinValue = 0; - optUnsigned.Value = Convert.ToDouble(option.@default); - stkUnsigned.Items.Add(optUnsigned); - var lblUnsigned = new Label(); - lblUnsigned.Text = option.description; - stkUnsigned.Items.Add(lblUnsigned); - stkImageOptions.Items.Add(stkUnsigned); - - break; - case "System.Single": - case "System.Double": - var stkFloat = new StackLayout(); - stkFloat.Orientation = Orientation.Horizontal; - var optFloat = new NumericStepper(); - optFloat.ID = "opt" + option.name; - optFloat.DecimalPlaces = 2; - optFloat.Value = Convert.ToDouble(option.@default); - stkFloat.Items.Add(optFloat); - var lblFloat = new Label(); - lblFloat.Text = option.description; - stkFloat.Items.Add(lblFloat); - stkImageOptions.Items.Add(stkFloat); - - break; - case "System.Guid": - // TODO - break; - case "System.String": - var stkString = new StackLayout(); - stkString.Orientation = Orientation.Horizontal; - var lblString = new Label(); - lblString.Text = option.description; - stkString.Items.Add(lblString); - var optString = new TextBox(); - optString.ID = "opt" + option.name; - optString.Text = (string)option.@default; - stkString.Items.Add(optString); - stkImageOptions.Items.Add(stkString); - - break; - } - - grpOptions.Content = stkImageOptions; + return; } - */ - async void ExecuteDestinationCommand() - { - if(SelectedPlugin is null) - return; - var dlgDestination = new SaveFileDialog - { - Title = "Choose destination file" - }; + btnDestination.Enabled = true; - dlgDestination.Filters.Add(new FileDialogFilter - { - Name = SelectedPlugin.Plugin.Name, - Extensions = SelectedPlugin.Plugin.KnownExtensions.ToList() - }); - - string result = await dlgDestination.ShowAsync(_view); - - if(result is null || - result.Length != 1) - { - DestinationText = ""; - - return; - } - - if(string.IsNullOrEmpty(Path.GetExtension(result))) - result += SelectedPlugin.Plugin.KnownExtensions.First(); - - DestinationText = result; - } - - void ExecuteCreatorCommand() => CreatorText = _inputFormat.Info.Creator; - - void ExecuteMediaTitleCommand() => MediaTitleText = _inputFormat.Info.MediaTitle; - - void ExecuteCommentsCommand() => CommentsText = _inputFormat.Info.Comments; - - void ExecuteMediaManufacturerCommand() => MediaManufacturerText = _inputFormat.Info.MediaManufacturer; - - void ExecuteMediaModelCommand() => MediaModelText = _inputFormat.Info.MediaModel; - - void ExecuteMediaSerialNumberCommand() => MediaSerialNumberText = _inputFormat.Info.MediaSerialNumber; - - void ExecuteMediaBarcodeCommand() => MediaBarcodeText = _inputFormat.Info.MediaBarcode; - - void ExecuteMediaPartNumberCommand() => MediaPartNumberText = _inputFormat.Info.MediaPartNumber; - - void ExecuteMediaSequenceCommand() => MediaSequenceValue = _inputFormat.Info.MediaSequence; - - void ExecuteLastMediaSequenceCommand() => LastMediaSequenceValue = _inputFormat.Info.LastMediaSequence; - - void ExecuteDriveManufacturerCommand() => DriveManufacturerText = _inputFormat.Info.DriveManufacturer; - - void ExecuteDriveModelCommand() => DriveModelText = _inputFormat.Info.DriveModel; - - void ExecuteDriveSerialNumberCommand() => DriveSerialNumberText = _inputFormat.Info.DriveSerialNumber; - - void ExecuteDriveFirmwareRevisionCommand() => - DriveFirmwareRevisionText = _inputFormat.Info.DriveFirmwareRevision; - - void ExecuteCicmXmlFromImageCommand() - { - CicmXmlText = ""; - _cicmMetadata = _inputFormat.CicmMetadata; - } - - async void ExecuteCicmXmlCommand() - { - _cicmMetadata = null; - CicmXmlText = ""; - - var dlgMetadata = new OpenFileDialog - { - Title = "Choose existing metadata sidecar" - }; - - dlgMetadata.Filters.Add(new FileDialogFilter - { - Name = "CICM XML metadata", - Extensions = new List(new[] + if(!plugin.SupportedOptions.Any()) { - ".xml" - }) - }); + grpOptions.Content = null; + grpOptions.Visible = false; - string[] result = await dlgMetadata.ShowAsync(_view); - - if(result is null || - result.Length != 1) - return; - - var sidecarXs = new XmlSerializer(typeof(CICMMetadataType)); - - try - { - var sr = new StreamReader(result[0]); - _cicmMetadata = (CICMMetadataType)sidecarXs.Deserialize(sr); - sr.Close(); - CicmXmlText = result[0]; - } - catch - { - await MessageBoxManager.GetMessageBoxStandardWindow("Error", "Incorrect metadata sidecar file...", - icon: Icon.Error).ShowDialog(_view); - } - } - - void ExecuteResumeFileFromImageCommand() - { - ResumeFileText = ""; - _dumpHardware = _inputFormat.DumpHardware; - } - - async void ExecuteResumeFileCommand() - { - _dumpHardware = null; - ResumeFileText = ""; - - var dlgMetadata = new OpenFileDialog - { - Title = "Choose existing resume file" - }; - - dlgMetadata.Filters.Add(new FileDialogFilter - { - Name = "CICM XML metadata", - Extensions = new List(new[] - { - ".xml" - }) - }); - - string[] result = await dlgMetadata.ShowAsync(_view); - - if(result is null || - result.Length != 1) - return; - - var sidecarXs = new XmlSerializer(typeof(Resume)); - - try - { - var sr = new StreamReader(result[0]); - var resume = (Resume)sidecarXs.Deserialize(sr); - - if(resume.Tries?.Any() == false) - { - _dumpHardware = resume.Tries; - ResumeFileText = result[0]; + return; } - else - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", - "Resume file does not contain dump hardware information...", - icon: Icon.Error).ShowDialog(_view); - sr.Close(); + chkForce.Visible = false; + + foreach(MediaTagType mediaTag in inputFormat.Info.ReadableMediaTags) + { + if(plugin.SupportedMediaTags.Contains(mediaTag)) + continue; + + chkForce.Visible = true; + ForceChecked = true; + + break; + } + + foreach(SectorTagType sectorTag in inputFormat.Info.ReadableSectorTags) + { + if(plugin.SupportedSectorTags.Contains(sectorTag)) + continue; + + chkForce.Visible = true; + ForceChecked = true; + + break; + } + + grpOptions.Visible = true; + + var stkImageOptions = new StackLayout + { + Orientation = Orientation.Vertical + }; + + foreach((string name, Type type, string description, object @default) option in plugin.SupportedOptions) + switch(option.type.ToString()) + { + case "System.Boolean": + var optBoolean = new CheckBox(); + optBoolean.ID = "opt" + option.name; + optBoolean.Text = option.description; + optBooleanChecked = (bool)option.@default; + stkImageOptions.Items.Add(optBoolean); + + break; + case "System.SByte": + case "System.Int16": + case "System.Int32": + case "System.Int64": + var stkNumber = new StackLayout(); + stkNumber.Orientation = Orientation.Horizontal; + var optNumber = new NumericStepper(); + optNumber.ID = "opt" + option.name; + optNumber.Value = Convert.ToDouble(option.@default); + stkNumber.Items.Add(optNumber); + var lblNumber = new Label(); + lblNumber.Text = option.description; + stkNumber.Items.Add(lblNumber); + stkImageOptions.Items.Add(stkNumber); + + break; + case "System.Byte": + case "System.UInt16": + case "System.UInt32": + case "System.UInt64": + var stkUnsigned = new StackLayout(); + stkUnsigned.Orientation = Orientation.Horizontal; + var optUnsigned = new NumericStepper(); + optUnsigned.ID = "opt" + option.name; + optUnsigned.MinValue = 0; + optUnsigned.Value = Convert.ToDouble(option.@default); + stkUnsigned.Items.Add(optUnsigned); + var lblUnsigned = new Label(); + lblUnsigned.Text = option.description; + stkUnsigned.Items.Add(lblUnsigned); + stkImageOptions.Items.Add(stkUnsigned); + + break; + case "System.Single": + case "System.Double": + var stkFloat = new StackLayout(); + stkFloat.Orientation = Orientation.Horizontal; + var optFloat = new NumericStepper(); + optFloat.ID = "opt" + option.name; + optFloat.DecimalPlaces = 2; + optFloat.Value = Convert.ToDouble(option.@default); + stkFloat.Items.Add(optFloat); + var lblFloat = new Label(); + lblFloat.Text = option.description; + stkFloat.Items.Add(lblFloat); + stkImageOptions.Items.Add(stkFloat); + + break; + case "System.Guid": + // TODO + break; + case "System.String": + var stkString = new StackLayout(); + stkString.Orientation = Orientation.Horizontal; + var lblString = new Label(); + lblString.Text = option.description; + stkString.Items.Add(lblString); + var optString = new TextBox(); + optString.ID = "opt" + option.name; + optString.Text = (string)option.@default; + stkString.Items.Add(optString); + stkImageOptions.Items.Add(stkString); + + break; + } + + grpOptions.Content = stkImageOptions; } - catch + */ + async void ExecuteDestinationCommand() + { + if(SelectedPlugin is null) + return; + + var dlgDestination = new SaveFileDialog + { + Title = "Choose destination file" + }; + + dlgDestination.Filters.Add(new FileDialogFilter + { + Name = SelectedPlugin.Plugin.Name, + Extensions = SelectedPlugin.Plugin.KnownExtensions.ToList() + }); + + string result = await dlgDestination.ShowAsync(_view); + + if(result is null || + result.Length != 1) + { + DestinationText = ""; + + return; + } + + if(string.IsNullOrEmpty(Path.GetExtension(result))) + result += SelectedPlugin.Plugin.KnownExtensions.First(); + + DestinationText = result; + } + + void ExecuteCreatorCommand() => CreatorText = _inputFormat.Info.Creator; + + void ExecuteMediaTitleCommand() => MediaTitleText = _inputFormat.Info.MediaTitle; + + void ExecuteCommentsCommand() => CommentsText = _inputFormat.Info.Comments; + + void ExecuteMediaManufacturerCommand() => MediaManufacturerText = _inputFormat.Info.MediaManufacturer; + + void ExecuteMediaModelCommand() => MediaModelText = _inputFormat.Info.MediaModel; + + void ExecuteMediaSerialNumberCommand() => MediaSerialNumberText = _inputFormat.Info.MediaSerialNumber; + + void ExecuteMediaBarcodeCommand() => MediaBarcodeText = _inputFormat.Info.MediaBarcode; + + void ExecuteMediaPartNumberCommand() => MediaPartNumberText = _inputFormat.Info.MediaPartNumber; + + void ExecuteMediaSequenceCommand() => MediaSequenceValue = _inputFormat.Info.MediaSequence; + + void ExecuteLastMediaSequenceCommand() => LastMediaSequenceValue = _inputFormat.Info.LastMediaSequence; + + void ExecuteDriveManufacturerCommand() => DriveManufacturerText = _inputFormat.Info.DriveManufacturer; + + void ExecuteDriveModelCommand() => DriveModelText = _inputFormat.Info.DriveModel; + + void ExecuteDriveSerialNumberCommand() => DriveSerialNumberText = _inputFormat.Info.DriveSerialNumber; + + void ExecuteDriveFirmwareRevisionCommand() => + DriveFirmwareRevisionText = _inputFormat.Info.DriveFirmwareRevision; + + void ExecuteCicmXmlFromImageCommand() + { + CicmXmlText = ""; + _cicmMetadata = _inputFormat.CicmMetadata; + } + + async void ExecuteCicmXmlCommand() + { + _cicmMetadata = null; + CicmXmlText = ""; + + var dlgMetadata = new OpenFileDialog + { + Title = "Choose existing metadata sidecar" + }; + + dlgMetadata.Filters.Add(new FileDialogFilter + { + Name = "CICM XML metadata", + Extensions = new List(new[] { + ".xml" + }) + }); + + string[] result = await dlgMetadata.ShowAsync(_view); + + if(result is null || + result.Length != 1) + return; + + var sidecarXs = new XmlSerializer(typeof(CICMMetadataType)); + + try + { + var sr = new StreamReader(result[0]); + _cicmMetadata = (CICMMetadataType)sidecarXs.Deserialize(sr); + sr.Close(); + CicmXmlText = result[0]; + } + catch + { + await MessageBoxManager.GetMessageBoxStandardWindow("Error", "Incorrect metadata sidecar file...", + icon: Icon.Error).ShowDialog(_view); + } + } + + void ExecuteResumeFileFromImageCommand() + { + ResumeFileText = ""; + _dumpHardware = _inputFormat.DumpHardware; + } + + async void ExecuteResumeFileCommand() + { + _dumpHardware = null; + ResumeFileText = ""; + + var dlgMetadata = new OpenFileDialog + { + Title = "Choose existing resume file" + }; + + dlgMetadata.Filters.Add(new FileDialogFilter + { + Name = "CICM XML metadata", + Extensions = new List(new[] + { + ".xml" + }) + }); + + string[] result = await dlgMetadata.ShowAsync(_view); + + if(result is null || + result.Length != 1) + return; + + var sidecarXs = new XmlSerializer(typeof(Resume)); + + try + { + var sr = new StreamReader(result[0]); + var resume = (Resume)sidecarXs.Deserialize(sr); + + if(resume.Tries?.Any() == false) + { + _dumpHardware = resume.Tries; + ResumeFileText = result[0]; + } + else await MessageBoxManager. - GetMessageBoxStandardWindow("Error", "Incorrect resume file...", icon: Icon.Error). - ShowDialog(_view); - } + GetMessageBoxStandardWindow("Error", + "Resume file does not contain dump hardware information...", + icon: Icon.Error).ShowDialog(_view); + + sr.Close(); + } + catch + { + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", "Incorrect resume file...", icon: Icon.Error). + ShowDialog(_view); } } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/ImageEntropyViewModel.cs b/Aaru.Gui/ViewModels/Windows/ImageEntropyViewModel.cs index 8f03362dd..2001f0e0a 100644 --- a/Aaru.Gui/ViewModels/Windows/ImageEntropyViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/ImageEntropyViewModel.cs @@ -44,384 +44,383 @@ using Avalonia.Threading; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class ImageEntropyViewModel : ViewModelBase { - public sealed class ImageEntropyViewModel : ViewModelBase + readonly IMediaImage _inputFormat; + readonly Window _view; + bool _closeVisible; + bool _duplicatedSectorsChecked; + bool _duplicatedSectorsEnabled; + EntropyResults _entropy; + string _mediaEntropyText; + bool _mediaEntropyVisible; + string _mediaUniqueSectorsText; + bool _mediaUniqueSectorsVisible; + bool _optionsVisible; + bool _progress1Visible; + bool _progress2Indeterminate; + double _progress2Max; + string _progress2Text; + double _progress2Value; + bool _progress2Visible; + bool _progressIndeterminate; + double _progressMax; + string _progressText; + double _progressValue; + bool _progressVisible; + bool _resultsVisible; + bool _separatedTracksChecked; + bool _separatedTracksEnabled; + bool _separatedTracksVisible; + bool _startVisible; + bool _stopVisible; + EntropyResults[] _tracksEntropy; + bool _wholeDiscChecked; + bool _wholeDiscEnabled; + bool _wholeDiscVisible; + + public ImageEntropyViewModel(IMediaImage inputFormat, Window view) { - readonly IMediaImage _inputFormat; - readonly Window _view; - bool _closeVisible; - bool _duplicatedSectorsChecked; - bool _duplicatedSectorsEnabled; - EntropyResults _entropy; - string _mediaEntropyText; - bool _mediaEntropyVisible; - string _mediaUniqueSectorsText; - bool _mediaUniqueSectorsVisible; - bool _optionsVisible; - bool _progress1Visible; - bool _progress2Indeterminate; - double _progress2Max; - string _progress2Text; - double _progress2Value; - bool _progress2Visible; - bool _progressIndeterminate; - double _progressMax; - string _progressText; - double _progressValue; - bool _progressVisible; - bool _resultsVisible; - bool _separatedTracksChecked; - bool _separatedTracksEnabled; - bool _separatedTracksVisible; - bool _startVisible; - bool _stopVisible; - EntropyResults[] _tracksEntropy; - bool _wholeDiscChecked; - bool _wholeDiscEnabled; - bool _wholeDiscVisible; + _inputFormat = inputFormat; + _view = view; + TrackEntropy = new ObservableCollection(); + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StopCommand = ReactiveCommand.Create(ExecuteStopCommand); + OptionsVisible = true; + DuplicatedSectorsChecked = true; + SeparatedTracksChecked = true; + WholeDiscChecked = true; + StartVisible = true; - public ImageEntropyViewModel(IMediaImage inputFormat, Window view) + var inputOptical = inputFormat as IOpticalMediaImage; + + if(inputOptical?.Tracks.Count > 0) { - _inputFormat = inputFormat; - _view = view; - TrackEntropy = new ObservableCollection(); - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StopCommand = ReactiveCommand.Create(ExecuteStopCommand); - OptionsVisible = true; - DuplicatedSectorsChecked = true; - SeparatedTracksChecked = true; - WholeDiscChecked = true; - StartVisible = true; + SeparatedTracksVisible = true; + WholeDiscVisible = true; + } + else + { + SeparatedTracksChecked = false; + WholeDiscChecked = true; + } + } - var inputOptical = inputFormat as IOpticalMediaImage; + public bool SeparatedTracksVisible + { + get => _separatedTracksVisible; + set => this.RaiseAndSetIfChanged(ref _separatedTracksVisible, value); + } - if(inputOptical?.Tracks.Count > 0) - { - SeparatedTracksVisible = true; - WholeDiscVisible = true; - } - else - { - SeparatedTracksChecked = false; - WholeDiscChecked = true; - } + public bool WholeDiscVisible + { + get => _wholeDiscVisible; + set => this.RaiseAndSetIfChanged(ref _wholeDiscVisible, value); + } + + public bool SeparatedTracksChecked + { + get => _separatedTracksChecked; + set => this.RaiseAndSetIfChanged(ref _separatedTracksChecked, value); + } + + public bool WholeDiscChecked + { + get => _wholeDiscChecked; + set => this.RaiseAndSetIfChanged(ref _wholeDiscChecked, value); + } + + public bool DuplicatedSectorsEnabled + { + get => _duplicatedSectorsEnabled; + set => this.RaiseAndSetIfChanged(ref _duplicatedSectorsEnabled, value); + } + + public bool SeparatedTracksEnabled + { + get => _separatedTracksEnabled; + set => this.RaiseAndSetIfChanged(ref _separatedTracksEnabled, value); + } + + public bool WholeDiscEnabled + { + get => _wholeDiscEnabled; + set => this.RaiseAndSetIfChanged(ref _wholeDiscEnabled, value); + } + + public bool CloseVisible + { + get => _closeVisible; + set => this.RaiseAndSetIfChanged(ref _closeVisible, value); + } + + public bool StartVisible + { + get => _startVisible; + set => this.RaiseAndSetIfChanged(ref _startVisible, value); + } + + public bool StopVisible + { + get => _stopVisible; + set => this.RaiseAndSetIfChanged(ref _stopVisible, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public bool DuplicatedSectorsChecked + { + get => _duplicatedSectorsChecked; + set => this.RaiseAndSetIfChanged(ref _duplicatedSectorsChecked, value); + } + + public bool OptionsVisible + { + get => _optionsVisible; + set => this.RaiseAndSetIfChanged(ref _optionsVisible, value); + } + + public bool ResultsVisible + { + get => _resultsVisible; + set => this.RaiseAndSetIfChanged(ref _resultsVisible, value); + } + + public string MediaEntropyText + { + get => _mediaEntropyText; + set => this.RaiseAndSetIfChanged(ref _mediaEntropyText, value); + } + + public bool MediaEntropyVisible + { + get => _mediaEntropyVisible; + set => this.RaiseAndSetIfChanged(ref _mediaEntropyVisible, value); + } + + public string MediaUniqueSectorsText + { + get => _mediaUniqueSectorsText; + set => this.RaiseAndSetIfChanged(ref _mediaUniqueSectorsText, value); + } + + public bool MediaUniqueSectorsVisible + { + get => _mediaUniqueSectorsVisible; + set => this.RaiseAndSetIfChanged(ref _mediaUniqueSectorsVisible, value); + } + + public bool Progress1Visible + { + get => _progress1Visible; + set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); + } + + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } + + public string ProgressText + { + get => _progressText; + set => this.RaiseAndSetIfChanged(ref _progressText, value); + } + + public bool ProgressIndeterminate + { + get => _progressIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); + } + + public double ProgressMax + { + get => _progressMax; + set => this.RaiseAndSetIfChanged(ref _progressMax, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public string Progress2Text + { + get => _progress2Text; + set => this.RaiseAndSetIfChanged(ref _progress2Text, value); + } + + public bool Progress2Indeterminate + { + get => _progress2Indeterminate; + set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); + } + + public double Progress2Max + { + get => _progress2Max; + set => this.RaiseAndSetIfChanged(ref _progress2Max, value); + } + + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } + + [NotNull] + public string Title => "Calculating entropy"; + public ObservableCollection TrackEntropy { get; } + public ReactiveCommand StartCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StopCommand { get; } + + void ExecuteStartCommand() + { + var entropyCalculator = new Entropy(false, _inputFormat); + entropyCalculator.InitProgressEvent += InitProgress; + entropyCalculator.InitProgress2Event += InitProgress2; + entropyCalculator.UpdateProgressEvent += UpdateProgress; + entropyCalculator.UpdateProgress2Event += UpdateProgress2; + entropyCalculator.EndProgressEvent += EndProgress; + entropyCalculator.EndProgress2Event += EndProgress2; + DuplicatedSectorsEnabled = false; + SeparatedTracksEnabled = false; + WholeDiscEnabled = false; + CloseVisible = false; + StartVisible = false; + StopVisible = false; + ProgressVisible = true; + + if(WholeDiscChecked && + _inputFormat is IOpticalMediaImage opticalFormat && + opticalFormat.Sessions?.Count > 1) + { + AaruConsole.ErrorWriteLine("Calculating disc entropy of multisession images is not yet implemented."); + WholeDiscChecked = false; } - public bool SeparatedTracksVisible + var thread = new Thread(async () => { - get => _separatedTracksVisible; - set => this.RaiseAndSetIfChanged(ref _separatedTracksVisible, value); - } - - public bool WholeDiscVisible - { - get => _wholeDiscVisible; - set => this.RaiseAndSetIfChanged(ref _wholeDiscVisible, value); - } - - public bool SeparatedTracksChecked - { - get => _separatedTracksChecked; - set => this.RaiseAndSetIfChanged(ref _separatedTracksChecked, value); - } - - public bool WholeDiscChecked - { - get => _wholeDiscChecked; - set => this.RaiseAndSetIfChanged(ref _wholeDiscChecked, value); - } - - public bool DuplicatedSectorsEnabled - { - get => _duplicatedSectorsEnabled; - set => this.RaiseAndSetIfChanged(ref _duplicatedSectorsEnabled, value); - } - - public bool SeparatedTracksEnabled - { - get => _separatedTracksEnabled; - set => this.RaiseAndSetIfChanged(ref _separatedTracksEnabled, value); - } - - public bool WholeDiscEnabled - { - get => _wholeDiscEnabled; - set => this.RaiseAndSetIfChanged(ref _wholeDiscEnabled, value); - } - - public bool CloseVisible - { - get => _closeVisible; - set => this.RaiseAndSetIfChanged(ref _closeVisible, value); - } - - public bool StartVisible - { - get => _startVisible; - set => this.RaiseAndSetIfChanged(ref _startVisible, value); - } - - public bool StopVisible - { - get => _stopVisible; - set => this.RaiseAndSetIfChanged(ref _stopVisible, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public bool DuplicatedSectorsChecked - { - get => _duplicatedSectorsChecked; - set => this.RaiseAndSetIfChanged(ref _duplicatedSectorsChecked, value); - } - - public bool OptionsVisible - { - get => _optionsVisible; - set => this.RaiseAndSetIfChanged(ref _optionsVisible, value); - } - - public bool ResultsVisible - { - get => _resultsVisible; - set => this.RaiseAndSetIfChanged(ref _resultsVisible, value); - } - - public string MediaEntropyText - { - get => _mediaEntropyText; - set => this.RaiseAndSetIfChanged(ref _mediaEntropyText, value); - } - - public bool MediaEntropyVisible - { - get => _mediaEntropyVisible; - set => this.RaiseAndSetIfChanged(ref _mediaEntropyVisible, value); - } - - public string MediaUniqueSectorsText - { - get => _mediaUniqueSectorsText; - set => this.RaiseAndSetIfChanged(ref _mediaUniqueSectorsText, value); - } - - public bool MediaUniqueSectorsVisible - { - get => _mediaUniqueSectorsVisible; - set => this.RaiseAndSetIfChanged(ref _mediaUniqueSectorsVisible, value); - } - - public bool Progress1Visible - { - get => _progress1Visible; - set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); - } - - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } - - public string ProgressText - { - get => _progressText; - set => this.RaiseAndSetIfChanged(ref _progressText, value); - } - - public bool ProgressIndeterminate - { - get => _progressIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); - } - - public double ProgressMax - { - get => _progressMax; - set => this.RaiseAndSetIfChanged(ref _progressMax, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public string Progress2Text - { - get => _progress2Text; - set => this.RaiseAndSetIfChanged(ref _progress2Text, value); - } - - public bool Progress2Indeterminate - { - get => _progress2Indeterminate; - set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); - } - - public double Progress2Max - { - get => _progress2Max; - set => this.RaiseAndSetIfChanged(ref _progress2Max, value); - } - - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } - - [NotNull] - public string Title => "Calculating entropy"; - public ObservableCollection TrackEntropy { get; } - public ReactiveCommand StartCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StopCommand { get; } - - void ExecuteStartCommand() - { - var entropyCalculator = new Entropy(false, _inputFormat); - entropyCalculator.InitProgressEvent += InitProgress; - entropyCalculator.InitProgress2Event += InitProgress2; - entropyCalculator.UpdateProgressEvent += UpdateProgress; - entropyCalculator.UpdateProgress2Event += UpdateProgress2; - entropyCalculator.EndProgressEvent += EndProgress; - entropyCalculator.EndProgress2Event += EndProgress2; - DuplicatedSectorsEnabled = false; - SeparatedTracksEnabled = false; - WholeDiscEnabled = false; - CloseVisible = false; - StartVisible = false; - StopVisible = false; - ProgressVisible = true; - - if(WholeDiscChecked && - _inputFormat is IOpticalMediaImage opticalFormat && - opticalFormat.Sessions?.Count > 1) - { - AaruConsole.ErrorWriteLine("Calculating disc entropy of multisession images is not yet implemented."); - WholeDiscChecked = false; - } - - var thread = new Thread(async () => - { - if(SeparatedTracksChecked) - { - _tracksEntropy = entropyCalculator.CalculateTracksEntropy(DuplicatedSectorsChecked); - - foreach(EntropyResults trackEntropy in _tracksEntropy) - { - AaruConsole.WriteLine("Entropy for track {0} is {1:F4}.", trackEntropy.Track, - trackEntropy.Entropy); - - if(trackEntropy.UniqueSectors != null) - AaruConsole.WriteLine("Track {0} has {1} unique sectors ({2:P3})", trackEntropy.Track, - trackEntropy.UniqueSectors, - (double)trackEntropy.UniqueSectors / trackEntropy.Sectors); - } - } - - if(WholeDiscChecked != true) - return; - - _entropy = entropyCalculator.CalculateMediaEntropy(DuplicatedSectorsChecked); - - await Dispatcher.UIThread.InvokeAsync(Finish); - }); - - Statistics.AddCommand("entropy"); - - thread.Start(); - } - - void Finish() - { - OptionsVisible = false; - CloseVisible = true; - ProgressVisible = false; - ResultsVisible = true; - if(SeparatedTracksChecked) { + _tracksEntropy = entropyCalculator.CalculateTracksEntropy(DuplicatedSectorsChecked); + foreach(EntropyResults trackEntropy in _tracksEntropy) - TrackEntropy.Add(new TrackEntropyModel - { - Track = trackEntropy.Track.ToString(), - Entropy = trackEntropy.Entropy.ToString(CultureInfo.CurrentUICulture), - UniqueSectors = - $"{trackEntropy.UniqueSectors} ({(trackEntropy.UniqueSectors ?? 0) / (double)trackEntropy.Sectors:P3})" - }); + { + AaruConsole.WriteLine("Entropy for track {0} is {1:F4}.", trackEntropy.Track, + trackEntropy.Entropy); + + if(trackEntropy.UniqueSectors != null) + AaruConsole.WriteLine("Track {0} has {1} unique sectors ({2:P3})", trackEntropy.Track, + trackEntropy.UniqueSectors, + (double)trackEntropy.UniqueSectors / trackEntropy.Sectors); + } } if(WholeDiscChecked != true) return; - MediaEntropyText = $"Entropy for disk is {_entropy.Entropy:F4}."; - MediaEntropyVisible = true; + _entropy = entropyCalculator.CalculateMediaEntropy(DuplicatedSectorsChecked); - if(_entropy.UniqueSectors == null) - return; + await Dispatcher.UIThread.InvokeAsync(Finish); + }); - MediaUniqueSectorsText = - $"Disk has {_entropy.UniqueSectors} unique sectors ({(double)_entropy.UniqueSectors / _entropy.Sectors:P3})"; + Statistics.AddCommand("entropy"); - MediaUniqueSectorsVisible = true; + thread.Start(); + } + + void Finish() + { + OptionsVisible = false; + CloseVisible = true; + ProgressVisible = false; + ResultsVisible = true; + + if(SeparatedTracksChecked) + { + foreach(EntropyResults trackEntropy in _tracksEntropy) + TrackEntropy.Add(new TrackEntropyModel + { + Track = trackEntropy.Track.ToString(), + Entropy = trackEntropy.Entropy.ToString(CultureInfo.CurrentUICulture), + UniqueSectors = + $"{trackEntropy.UniqueSectors} ({(trackEntropy.UniqueSectors ?? 0) / (double)trackEntropy.Sectors:P3})" + }); } - void ExecuteCloseCommand() => _view.Close(); + if(WholeDiscChecked != true) + return; - internal void ExecuteStopCommand() => throw new NotImplementedException(); + MediaEntropyText = $"Entropy for disk is {_entropy.Entropy:F4}."; + MediaEntropyVisible = true; - void InitProgress() => Progress1Visible = true; + if(_entropy.UniqueSectors == null) + return; - void EndProgress() => Progress1Visible = false; + MediaUniqueSectorsText = + $"Disk has {_entropy.UniqueSectors} unique sectors ({(double)_entropy.UniqueSectors / _entropy.Sectors:P3})"; - void InitProgress2() => Progress2Visible = true; - - void EndProgress2() => Progress2Visible = false; - - async void UpdateProgress(string text, long current, long maximum) => - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = text; - - if(maximum == 0) - { - ProgressIndeterminate = true; - - return; - } - - if(ProgressIndeterminate) - ProgressIndeterminate = false; - - ProgressMax = maximum; - ProgressValue = current; - }); - - async void UpdateProgress2(string text, long current, long maximum) => - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Text = text; - - if(maximum == 0) - { - Progress2Indeterminate = true; - - return; - } - - if(Progress2Indeterminate) - Progress2Indeterminate = false; - - Progress2Max = maximum; - Progress2Value = current; - }); + MediaUniqueSectorsVisible = true; } + + void ExecuteCloseCommand() => _view.Close(); + + internal void ExecuteStopCommand() => throw new NotImplementedException(); + + void InitProgress() => Progress1Visible = true; + + void EndProgress() => Progress1Visible = false; + + void InitProgress2() => Progress2Visible = true; + + void EndProgress2() => Progress2Visible = false; + + async void UpdateProgress(string text, long current, long maximum) => + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = text; + + if(maximum == 0) + { + ProgressIndeterminate = true; + + return; + } + + if(ProgressIndeterminate) + ProgressIndeterminate = false; + + ProgressMax = maximum; + ProgressValue = current; + }); + + async void UpdateProgress2(string text, long current, long maximum) => + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Text = text; + + if(maximum == 0) + { + Progress2Indeterminate = true; + + return; + } + + if(Progress2Indeterminate) + Progress2Indeterminate = false; + + Progress2Max = maximum; + Progress2Value = current; + }); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/ImageSidecarViewModel.cs b/Aaru.Gui/ViewModels/Windows/ImageSidecarViewModel.cs index ee700c413..4fc9aface 100644 --- a/Aaru.Gui/ViewModels/Windows/ImageSidecarViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/ImageSidecarViewModel.cs @@ -45,305 +45,304 @@ using Avalonia.Threading; using ReactiveUI; using Schemas; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class ImageSidecarViewModel : ViewModelBase { - public sealed class ImageSidecarViewModel : ViewModelBase + readonly Encoding _encoding; + readonly Guid _filterId; + readonly string _imageSource; + readonly IMediaImage _inputFormat; + readonly Window _view; + bool _closeVisible; + bool _destinationEnabled; + string _destinationText; + bool _progress1Visible; + bool _progress2Indeterminate; + double _progress2MaxValue; + string _progress2Text; + double _progress2Value; + bool _progress2Visible; + bool _progressIndeterminate; + double _progressMaxValue; + string _progressText; + double _progressValue; + bool _progressVisible; + Sidecar _sidecarClass; + bool _startVisible; + string _statusText; + bool _statusVisible; + bool _stopEnabled; + bool _stopVisible; + + public ImageSidecarViewModel(IMediaImage inputFormat, string imageSource, Guid filterId, Encoding encoding, + Window view) { - readonly Encoding _encoding; - readonly Guid _filterId; - readonly string _imageSource; - readonly IMediaImage _inputFormat; - readonly Window _view; - bool _closeVisible; - bool _destinationEnabled; - string _destinationText; - bool _progress1Visible; - bool _progress2Indeterminate; - double _progress2MaxValue; - string _progress2Text; - double _progress2Value; - bool _progress2Visible; - bool _progressIndeterminate; - double _progressMaxValue; - string _progressText; - double _progressValue; - bool _progressVisible; - Sidecar _sidecarClass; - bool _startVisible; - string _statusText; - bool _statusVisible; - bool _stopEnabled; - bool _stopVisible; + _view = view; + _inputFormat = inputFormat; + _imageSource = imageSource; + _filterId = filterId; + _encoding = encoding; - public ImageSidecarViewModel(IMediaImage inputFormat, string imageSource, Guid filterId, Encoding encoding, - Window view) + DestinationText = Path.Combine(Path.GetDirectoryName(imageSource) ?? "", + Path.GetFileNameWithoutExtension(imageSource) + ".cicm.xml"); + + DestinationEnabled = true; + StartVisible = true; + CloseVisible = true; + DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand); + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StopCommand = ReactiveCommand.Create(ExecuteStopCommand); + } + + public string Title { get; } + public ReactiveCommand DestinationCommand { get; } + public ReactiveCommand StartCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StopCommand { get; } + + public bool ProgressIndeterminate + { + get => _progressIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public bool Progress1Visible + { + get => _progress1Visible; + set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); + } + + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } + + public bool Progress2Indeterminate + { + get => _progress2Indeterminate; + set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); + } + + public double ProgressMaxValue + { + get => _progressMaxValue; + set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); + } + + public double Progress2MaxValue + { + get => _progress2MaxValue; + set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value); + } + + public string ProgressText + { + get => _progressText; + set => this.RaiseAndSetIfChanged(ref _progressText, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } + + public string Progress2Text + { + get => _progress2Text; + set => this.RaiseAndSetIfChanged(ref _progress2Text, value); + } + + public string DestinationText + { + get => _destinationText; + set => this.RaiseAndSetIfChanged(ref _destinationText, value); + } + + public bool DestinationEnabled + { + get => _destinationEnabled; + set => this.RaiseAndSetIfChanged(ref _destinationEnabled, value); + } + + public string StatusText + { + get => _statusText; + set => this.RaiseAndSetIfChanged(ref _statusText, value); + } + + public bool StatusVisible + { + get => _statusVisible; + set => this.RaiseAndSetIfChanged(ref _statusVisible, value); + } + + public bool StartVisible + { + get => _startVisible; + set => this.RaiseAndSetIfChanged(ref _startVisible, value); + } + + public bool CloseVisible + { + get => _closeVisible; + set => this.RaiseAndSetIfChanged(ref _closeVisible, value); + } + + public bool StopEnabled + { + get => _stopEnabled; + set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); + } + + public bool StopVisible + { + get => _stopVisible; + set => this.RaiseAndSetIfChanged(ref _stopVisible, value); + } + + void ExecuteStartCommand() => new Thread(DoWork).Start(); + + async void DoWork() + { + // Prepare UI + await Dispatcher.UIThread.InvokeAsync(() => { - _view = view; - _inputFormat = inputFormat; - _imageSource = imageSource; - _filterId = filterId; - _encoding = encoding; - - DestinationText = Path.Combine(Path.GetDirectoryName(imageSource) ?? "", - Path.GetFileNameWithoutExtension(imageSource) + ".cicm.xml"); - - DestinationEnabled = true; - StartVisible = true; - CloseVisible = true; - DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand); - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StopCommand = ReactiveCommand.Create(ExecuteStopCommand); - } - - public string Title { get; } - public ReactiveCommand DestinationCommand { get; } - public ReactiveCommand StartCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StopCommand { get; } - - public bool ProgressIndeterminate - { - get => _progressIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public bool Progress1Visible - { - get => _progress1Visible; - set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); - } - - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } - - public bool Progress2Indeterminate - { - get => _progress2Indeterminate; - set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); - } - - public double ProgressMaxValue - { - get => _progressMaxValue; - set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); - } - - public double Progress2MaxValue - { - get => _progress2MaxValue; - set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value); - } - - public string ProgressText - { - get => _progressText; - set => this.RaiseAndSetIfChanged(ref _progressText, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } - - public string Progress2Text - { - get => _progress2Text; - set => this.RaiseAndSetIfChanged(ref _progress2Text, value); - } - - public string DestinationText - { - get => _destinationText; - set => this.RaiseAndSetIfChanged(ref _destinationText, value); - } - - public bool DestinationEnabled - { - get => _destinationEnabled; - set => this.RaiseAndSetIfChanged(ref _destinationEnabled, value); - } - - public string StatusText - { - get => _statusText; - set => this.RaiseAndSetIfChanged(ref _statusText, value); - } - - public bool StatusVisible - { - get => _statusVisible; - set => this.RaiseAndSetIfChanged(ref _statusVisible, value); - } - - public bool StartVisible - { - get => _startVisible; - set => this.RaiseAndSetIfChanged(ref _startVisible, value); - } - - public bool CloseVisible - { - get => _closeVisible; - set => this.RaiseAndSetIfChanged(ref _closeVisible, value); - } - - public bool StopEnabled - { - get => _stopEnabled; - set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); - } - - public bool StopVisible - { - get => _stopVisible; - set => this.RaiseAndSetIfChanged(ref _stopVisible, value); - } - - void ExecuteStartCommand() => new Thread(DoWork).Start(); - - async void DoWork() - { - // Prepare UI - await Dispatcher.UIThread.InvokeAsync(() => - { - CloseVisible = false; - StartVisible = false; - StopVisible = true; - StopEnabled = true; - ProgressVisible = true; - DestinationEnabled = false; - StatusVisible = true; - }); - - _sidecarClass = new Sidecar(_inputFormat, _imageSource, _filterId, _encoding); - _sidecarClass.UpdateStatusEvent += UpdateStatus; - _sidecarClass.InitProgressEvent += InitProgress; - _sidecarClass.UpdateProgressEvent += UpdateProgress; - _sidecarClass.EndProgressEvent += EndProgress; - _sidecarClass.InitProgressEvent2 += InitProgress2; - _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; - _sidecarClass.EndProgressEvent2 += EndProgress2; - CICMMetadataType sidecar = _sidecarClass.Create(); - - AaruConsole.WriteLine("Writing metadata sidecar"); - - var xmlFs = new FileStream(DestinationText, FileMode.Create); - - var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); - xmlSer.Serialize(xmlFs, sidecar); - xmlFs.Close(); - - await Dispatcher.UIThread.InvokeAsync(() => - { - CloseVisible = true; - StopVisible = false; - ProgressVisible = false; - StatusVisible = false; - }); - - Statistics.AddCommand("create-sidecar"); - } - - async void EndProgress2() => await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Visible = false; + CloseVisible = false; + StartVisible = false; + StopVisible = true; + StopEnabled = true; + ProgressVisible = true; + DestinationEnabled = false; + StatusVisible = true; }); - async void UpdateProgress2(string text, long current, long maximum) => - await Dispatcher.UIThread.InvokeAsync(() => + _sidecarClass = new Sidecar(_inputFormat, _imageSource, _filterId, _encoding); + _sidecarClass.UpdateStatusEvent += UpdateStatus; + _sidecarClass.InitProgressEvent += InitProgress; + _sidecarClass.UpdateProgressEvent += UpdateProgress; + _sidecarClass.EndProgressEvent += EndProgress; + _sidecarClass.InitProgressEvent2 += InitProgress2; + _sidecarClass.UpdateProgressEvent2 += UpdateProgress2; + _sidecarClass.EndProgressEvent2 += EndProgress2; + CICMMetadataType sidecar = _sidecarClass.Create(); + + AaruConsole.WriteLine("Writing metadata sidecar"); + + var xmlFs = new FileStream(DestinationText, FileMode.Create); + + var xmlSer = new XmlSerializer(typeof(CICMMetadataType)); + xmlSer.Serialize(xmlFs, sidecar); + xmlFs.Close(); + + await Dispatcher.UIThread.InvokeAsync(() => + { + CloseVisible = true; + StopVisible = false; + ProgressVisible = false; + StatusVisible = false; + }); + + Statistics.AddCommand("create-sidecar"); + } + + async void EndProgress2() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Visible = false; + }); + + async void UpdateProgress2(string text, long current, long maximum) => + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Text = text; + Progress2Indeterminate = false; + + Progress2MaxValue = maximum; + Progress2Value = current; + }); + + async void InitProgress2() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Visible = true; + }); + + async void EndProgress() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress1Visible = false; + }); + + async void UpdateProgress(string text, long current, long maximum) => + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = text; + ProgressIndeterminate = false; + + ProgressMaxValue = maximum; + ProgressValue = current; + }); + + async void InitProgress() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress1Visible = true; + }); + + async void UpdateStatus(string text) => await Dispatcher.UIThread.InvokeAsync(() => + { + StatusText = text; + }); + + void ExecuteCloseCommand() => _view.Close(); + + void ExecuteStopCommand() + { + ProgressText = "Aborting..."; + StopEnabled = false; + _sidecarClass.Abort(); + } + + async void ExecuteDestinationCommand() + { + var dlgDestination = new SaveFileDialog + { + Title = "Choose destination file" + }; + + dlgDestination.Filters.Add(new FileDialogFilter + { + Name = "CICM XML metadata", + Extensions = new List(new[] { - Progress2Text = text; - Progress2Indeterminate = false; - - Progress2MaxValue = maximum; - Progress2Value = current; - }); - - async void InitProgress2() => await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Visible = true; + "*.xml" + }) }); - async void EndProgress() => await Dispatcher.UIThread.InvokeAsync(() => + string result = await dlgDestination.ShowAsync(_view); + + if(result is null) { - Progress1Visible = false; - }); + DestinationText = ""; - async void UpdateProgress(string text, long current, long maximum) => - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = text; - ProgressIndeterminate = false; - - ProgressMaxValue = maximum; - ProgressValue = current; - }); - - async void InitProgress() => await Dispatcher.UIThread.InvokeAsync(() => - { - Progress1Visible = true; - }); - - async void UpdateStatus(string text) => await Dispatcher.UIThread.InvokeAsync(() => - { - StatusText = text; - }); - - void ExecuteCloseCommand() => _view.Close(); - - void ExecuteStopCommand() - { - ProgressText = "Aborting..."; - StopEnabled = false; - _sidecarClass.Abort(); + return; } - async void ExecuteDestinationCommand() - { - var dlgDestination = new SaveFileDialog - { - Title = "Choose destination file" - }; + if(string.IsNullOrEmpty(Path.GetExtension(result))) + result += ".xml"; - dlgDestination.Filters.Add(new FileDialogFilter - { - Name = "CICM XML metadata", - Extensions = new List(new[] - { - "*.xml" - }) - }); - - string result = await dlgDestination.ShowAsync(_view); - - if(result is null) - { - DestinationText = ""; - - return; - } - - if(string.IsNullOrEmpty(Path.GetExtension(result))) - result += ".xml"; - - DestinationText = result; - } + DestinationText = result; } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/ImageVerifyViewModel.cs b/Aaru.Gui/ViewModels/Windows/ImageVerifyViewModel.cs index b84494f4c..5197bd81a 100644 --- a/Aaru.Gui/ViewModels/Windows/ImageVerifyViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/ImageVerifyViewModel.cs @@ -44,498 +44,436 @@ using Avalonia.Controls; using Avalonia.Threading; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class ImageVerifyViewModel : ViewModelBase { - public sealed class ImageVerifyViewModel : ViewModelBase + readonly IMediaImage _inputFormat; + readonly Window _view; + bool _cancel; + bool _closeVisible; + string _imageResultText; + bool _imageResultVisible; + bool _optionsVisible; + bool _progress2Indeterminate; + double _progress2MaxValue; + string _progress2Text; + double _progress2Value; + bool _progress2Visible; + bool _progressIndeterminate; + double _progressMaxValue; + string _progressText; + double _progressValue; + bool _progressVisible; + bool _resultsVisible; + string _sectorErrorsText; + bool _sectorErrorsVisible; + string _sectorsErrorsAllText; + bool _sectorsErrorsAllVisible; + bool _sectorSummaryVisible; + string _sectorsUnknownAllText; + bool _sectorsUnknownAllVisible; + string _sectorsUnknownsText; + bool _sectorsUnknownsVisible; + bool _startVisible; + bool _stopEnabled; + bool _stopVisible; + string _totalSectorErrorsText; + string _totalSectorErrorsUnknownsText; + string _totalSectorsText; + string _totalSectorUnknownsText; + bool _verifyImageChecked; + bool _verifyImageEnabled; + bool _verifySectorsChecked; + bool _verifySectorsEnabled; + bool _verifySectorsVisible; + + public ImageVerifyViewModel(IMediaImage inputFormat, Window view) { - readonly IMediaImage _inputFormat; - readonly Window _view; - bool _cancel; - bool _closeVisible; - string _imageResultText; - bool _imageResultVisible; - bool _optionsVisible; - bool _progress2Indeterminate; - double _progress2MaxValue; - string _progress2Text; - double _progress2Value; - bool _progress2Visible; - bool _progressIndeterminate; - double _progressMaxValue; - string _progressText; - double _progressValue; - bool _progressVisible; - bool _resultsVisible; - string _sectorErrorsText; - bool _sectorErrorsVisible; - string _sectorsErrorsAllText; - bool _sectorsErrorsAllVisible; - bool _sectorSummaryVisible; - string _sectorsUnknownAllText; - bool _sectorsUnknownAllVisible; - string _sectorsUnknownsText; - bool _sectorsUnknownsVisible; - bool _startVisible; - bool _stopEnabled; - bool _stopVisible; - string _totalSectorErrorsText; - string _totalSectorErrorsUnknownsText; - string _totalSectorsText; - string _totalSectorUnknownsText; - bool _verifyImageChecked; - bool _verifyImageEnabled; - bool _verifySectorsChecked; - bool _verifySectorsEnabled; - bool _verifySectorsVisible; + _view = view; + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StopCommand = ReactiveCommand.Create(ExecuteStopCommand); + _inputFormat = inputFormat; + _cancel = false; + ErrorList = new ObservableCollection(); + UnknownList = new ObservableCollection(); + VerifyImageEnabled = true; + VerifySectorsEnabled = true; + CloseVisible = true; + StartVisible = true; + OptionsVisible = true; + } - public ImageVerifyViewModel(IMediaImage inputFormat, Window view) + public ObservableCollection ErrorList { get; } + public ObservableCollection UnknownList { get; } + public ReactiveCommand StartCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StopCommand { get; } + + public bool VerifyImageEnabled + { + get => _verifyImageEnabled; + set => this.RaiseAndSetIfChanged(ref _verifyImageEnabled, value); + } + + public bool VerifySectorsEnabled + { + get => _verifySectorsEnabled; + set => this.RaiseAndSetIfChanged(ref _verifySectorsEnabled, value); + } + + public bool VerifySectorsVisible + { + get => _verifySectorsVisible; + set => this.RaiseAndSetIfChanged(ref _verifySectorsVisible, value); + } + + public double ProgressMaxValue + { + get => _progressMaxValue; + set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); + } + + public bool VerifyImageChecked + { + get => _verifyImageChecked; + set => this.RaiseAndSetIfChanged(ref _verifyImageChecked, value); + } + + public bool ProgressIndeterminate + { + get => _progressIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); + } + + public bool ImageResultVisible + { + get => _imageResultVisible; + set => this.RaiseAndSetIfChanged(ref _imageResultVisible, value); + } + + public string ImageResultText + { + get => _imageResultText; + set => this.RaiseAndSetIfChanged(ref _imageResultText, value); + } + + public bool VerifySectorsChecked + { + get => _verifySectorsChecked; + set => this.RaiseAndSetIfChanged(ref _verifySectorsChecked, value); + } + + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } + + public bool Progress2Indeterminate + { + get => _progress2Indeterminate; + set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); + } + + public double Progress2MaxValue + { + get => _progress2MaxValue; + set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value); + } + + public string ProgressText + { + get => _progressText; + set => this.RaiseAndSetIfChanged(ref _progressText, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } + + public string Progress2Text + { + get => _progress2Text; + set => this.RaiseAndSetIfChanged(ref _progress2Text, value); + } + + public bool SectorsErrorsAllVisible + { + get => _sectorsErrorsAllVisible; + set => this.RaiseAndSetIfChanged(ref _sectorsErrorsAllVisible, value); + } + + public string SectorsErrorsAllText + { + get => _sectorsErrorsAllText; + set => this.RaiseAndSetIfChanged(ref _sectorsErrorsAllText, value); + } + + public bool SectorsUnknownAllVisible + { + get => _sectorsUnknownAllVisible; + set => this.RaiseAndSetIfChanged(ref _sectorsUnknownAllVisible, value); + } + + public string SectorsUnknownAllText + { + get => _sectorsUnknownAllText; + set => this.RaiseAndSetIfChanged(ref _sectorsUnknownAllText, value); + } + + public string SectorErrorsText + { + get => _sectorErrorsText; + set => this.RaiseAndSetIfChanged(ref _sectorErrorsText, value); + } + + public bool SectorErrorsVisible + { + get => _sectorErrorsVisible; + set => this.RaiseAndSetIfChanged(ref _sectorErrorsVisible, value); + } + + public bool SectorsUnknownsVisible + { + get => _sectorsUnknownsVisible; + set => this.RaiseAndSetIfChanged(ref _sectorsUnknownsVisible, value); + } + + public string SectorsUnknownsText + { + get => _sectorsUnknownsText; + set => this.RaiseAndSetIfChanged(ref _sectorsUnknownsText, value); + } + + public bool SectorSummaryVisible + { + get => _sectorSummaryVisible; + set => this.RaiseAndSetIfChanged(ref _sectorSummaryVisible, value); + } + + public string TotalSectorsText + { + get => _totalSectorsText; + set => this.RaiseAndSetIfChanged(ref _totalSectorsText, value); + } + + public string TotalSectorErrorsText + { + get => _totalSectorErrorsText; + set => this.RaiseAndSetIfChanged(ref _totalSectorErrorsText, value); + } + + public string TotalSectorUnknownsText + { + get => _totalSectorUnknownsText; + set => this.RaiseAndSetIfChanged(ref _totalSectorUnknownsText, value); + } + + public string TotalSectorErrorsUnknownsText + { + get => _totalSectorErrorsUnknownsText; + set => this.RaiseAndSetIfChanged(ref _totalSectorErrorsUnknownsText, value); + } + + public bool OptionsVisible + { + get => _optionsVisible; + set => this.RaiseAndSetIfChanged(ref _optionsVisible, value); + } + + public bool ResultsVisible + { + get => _resultsVisible; + set => this.RaiseAndSetIfChanged(ref _resultsVisible, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public bool StartVisible + { + get => _startVisible; + set => this.RaiseAndSetIfChanged(ref _startVisible, value); + } + + public bool StopVisible + { + get => _stopVisible; + set => this.RaiseAndSetIfChanged(ref _stopVisible, value); + } + + public bool CloseVisible + { + get => _closeVisible; + set => this.RaiseAndSetIfChanged(ref _closeVisible, value); + } + + public bool StopEnabled + { + get => _stopEnabled; + set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); + } + + void ExecuteStartCommand() + { + VerifyImageEnabled = false; + VerifySectorsEnabled = false; + CloseVisible = false; + StartVisible = false; + StopVisible = true; + ProgressVisible = true; + Progress2Visible = false; + + VerifySectorsVisible = _inputFormat is IOpticalMediaImage || _inputFormat is IVerifiableSectorsImage; + + // TODO: Do not offer the option to use this form if the image does not support any kind of verification + new Thread(DoWork).Start(); + } + + async void DoWork() + { + bool formatHasTracks; + var inputOptical = _inputFormat as IOpticalMediaImage; + var verifiableSectorsImage = _inputFormat as IVerifiableSectorsImage; + + try { - _view = view; - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StopCommand = ReactiveCommand.Create(ExecuteStopCommand); - _inputFormat = inputFormat; - _cancel = false; - ErrorList = new ObservableCollection(); - UnknownList = new ObservableCollection(); - VerifyImageEnabled = true; - VerifySectorsEnabled = true; - CloseVisible = true; - StartVisible = true; - OptionsVisible = true; + formatHasTracks = inputOptical?.Tracks?.Count > 0; + } + catch + { + formatHasTracks = false; } - public ObservableCollection ErrorList { get; } - public ObservableCollection UnknownList { get; } - public ReactiveCommand StartCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StopCommand { get; } - - public bool VerifyImageEnabled + // Setup progress bars + await Dispatcher.UIThread.InvokeAsync(() => { - get => _verifyImageEnabled; - set => this.RaiseAndSetIfChanged(ref _verifyImageEnabled, value); - } + ProgressVisible = true; + ProgressMaxValue = 0; - public bool VerifySectorsEnabled - { - get => _verifySectorsEnabled; - set => this.RaiseAndSetIfChanged(ref _verifySectorsEnabled, value); - } + if(VerifyImageChecked || VerifySectorsChecked) + ProgressMaxValue = 1; - public bool VerifySectorsVisible - { - get => _verifySectorsVisible; - set => this.RaiseAndSetIfChanged(ref _verifySectorsVisible, value); - } - - public double ProgressMaxValue - { - get => _progressMaxValue; - set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); - } - - public bool VerifyImageChecked - { - get => _verifyImageChecked; - set => this.RaiseAndSetIfChanged(ref _verifyImageChecked, value); - } - - public bool ProgressIndeterminate - { - get => _progressIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); - } - - public bool ImageResultVisible - { - get => _imageResultVisible; - set => this.RaiseAndSetIfChanged(ref _imageResultVisible, value); - } - - public string ImageResultText - { - get => _imageResultText; - set => this.RaiseAndSetIfChanged(ref _imageResultText, value); - } - - public bool VerifySectorsChecked - { - get => _verifySectorsChecked; - set => this.RaiseAndSetIfChanged(ref _verifySectorsChecked, value); - } - - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } - - public bool Progress2Indeterminate - { - get => _progress2Indeterminate; - set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); - } - - public double Progress2MaxValue - { - get => _progress2MaxValue; - set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value); - } - - public string ProgressText - { - get => _progressText; - set => this.RaiseAndSetIfChanged(ref _progressText, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } - - public string Progress2Text - { - get => _progress2Text; - set => this.RaiseAndSetIfChanged(ref _progress2Text, value); - } - - public bool SectorsErrorsAllVisible - { - get => _sectorsErrorsAllVisible; - set => this.RaiseAndSetIfChanged(ref _sectorsErrorsAllVisible, value); - } - - public string SectorsErrorsAllText - { - get => _sectorsErrorsAllText; - set => this.RaiseAndSetIfChanged(ref _sectorsErrorsAllText, value); - } - - public bool SectorsUnknownAllVisible - { - get => _sectorsUnknownAllVisible; - set => this.RaiseAndSetIfChanged(ref _sectorsUnknownAllVisible, value); - } - - public string SectorsUnknownAllText - { - get => _sectorsUnknownAllText; - set => this.RaiseAndSetIfChanged(ref _sectorsUnknownAllText, value); - } - - public string SectorErrorsText - { - get => _sectorErrorsText; - set => this.RaiseAndSetIfChanged(ref _sectorErrorsText, value); - } - - public bool SectorErrorsVisible - { - get => _sectorErrorsVisible; - set => this.RaiseAndSetIfChanged(ref _sectorErrorsVisible, value); - } - - public bool SectorsUnknownsVisible - { - get => _sectorsUnknownsVisible; - set => this.RaiseAndSetIfChanged(ref _sectorsUnknownsVisible, value); - } - - public string SectorsUnknownsText - { - get => _sectorsUnknownsText; - set => this.RaiseAndSetIfChanged(ref _sectorsUnknownsText, value); - } - - public bool SectorSummaryVisible - { - get => _sectorSummaryVisible; - set => this.RaiseAndSetIfChanged(ref _sectorSummaryVisible, value); - } - - public string TotalSectorsText - { - get => _totalSectorsText; - set => this.RaiseAndSetIfChanged(ref _totalSectorsText, value); - } - - public string TotalSectorErrorsText - { - get => _totalSectorErrorsText; - set => this.RaiseAndSetIfChanged(ref _totalSectorErrorsText, value); - } - - public string TotalSectorUnknownsText - { - get => _totalSectorUnknownsText; - set => this.RaiseAndSetIfChanged(ref _totalSectorUnknownsText, value); - } - - public string TotalSectorErrorsUnknownsText - { - get => _totalSectorErrorsUnknownsText; - set => this.RaiseAndSetIfChanged(ref _totalSectorErrorsUnknownsText, value); - } - - public bool OptionsVisible - { - get => _optionsVisible; - set => this.RaiseAndSetIfChanged(ref _optionsVisible, value); - } - - public bool ResultsVisible - { - get => _resultsVisible; - set => this.RaiseAndSetIfChanged(ref _resultsVisible, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public bool StartVisible - { - get => _startVisible; - set => this.RaiseAndSetIfChanged(ref _startVisible, value); - } - - public bool StopVisible - { - get => _stopVisible; - set => this.RaiseAndSetIfChanged(ref _stopVisible, value); - } - - public bool CloseVisible - { - get => _closeVisible; - set => this.RaiseAndSetIfChanged(ref _closeVisible, value); - } - - public bool StopEnabled - { - get => _stopEnabled; - set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); - } - - void ExecuteStartCommand() - { - VerifyImageEnabled = false; - VerifySectorsEnabled = false; - CloseVisible = false; - StartVisible = false; - StopVisible = true; - ProgressVisible = true; - Progress2Visible = false; - - VerifySectorsVisible = _inputFormat is IOpticalMediaImage || _inputFormat is IVerifiableSectorsImage; - - // TODO: Do not offer the option to use this form if the image does not support any kind of verification - new Thread(DoWork).Start(); - } - - async void DoWork() - { - bool formatHasTracks; - var inputOptical = _inputFormat as IOpticalMediaImage; - var verifiableSectorsImage = _inputFormat as IVerifiableSectorsImage; - - try + if(formatHasTracks && inputOptical != null) + ProgressMaxValue += inputOptical.Tracks.Count; + else { - formatHasTracks = inputOptical?.Tracks?.Count > 0; - } - catch - { - formatHasTracks = false; - } - - // Setup progress bars - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressVisible = true; - ProgressMaxValue = 0; - - if(VerifyImageChecked || VerifySectorsChecked) - ProgressMaxValue = 1; - - if(formatHasTracks && inputOptical != null) - ProgressMaxValue += inputOptical.Tracks.Count; + if(VerifySectorsChecked) + { + ProgressMaxValue = 2; + Progress2Visible = false; + Progress2Visible = false; + } else { + Progress2Visible = true; + Progress2Visible = true; + } + } + + ProgressMaxValue++; + }); + + if(VerifyImageChecked) + { + if(!(_inputFormat is IVerifiableImage verifiableImage)) + await Dispatcher.UIThread.InvokeAsync(() => + { + ImageResultVisible = true; + ImageResultText = "Disc image does not support verification."; + }); + else + { + await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = "Checking media image..."; + if(VerifySectorsChecked) - { - ProgressMaxValue = 2; - Progress2Visible = false; - Progress2Visible = false; - } + ProgressValue = 1; else - { - Progress2Visible = true; - Progress2Visible = true; - } - } + ProgressIndeterminate = true; - ProgressMaxValue++; - }); + Progress2Indeterminate = true; + }); - if(VerifyImageChecked) - { - if(!(_inputFormat is IVerifiableImage verifiableImage)) - await Dispatcher.UIThread.InvokeAsync(() => - { - ImageResultVisible = true; - ImageResultText = "Disc image does not support verification."; - }); - else - { - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = "Checking media image..."; + DateTime startCheck = DateTime.UtcNow; + bool? discCheckStatus = verifiableImage.VerifyMediaImage(); + DateTime endCheck = DateTime.UtcNow; - if(VerifySectorsChecked) - ProgressValue = 1; - else - ProgressIndeterminate = true; - - Progress2Indeterminate = true; - }); - - DateTime startCheck = DateTime.UtcNow; - bool? discCheckStatus = verifiableImage.VerifyMediaImage(); - DateTime endCheck = DateTime.UtcNow; - - TimeSpan checkTime = endCheck - startCheck; - - await Dispatcher.UIThread.InvokeAsync(() => - { - ImageResultVisible = true; - - switch(discCheckStatus) - { - case true: - ImageResultText = "Disc image checksums are correct"; - - break; - case false: - ImageResultText = "Disc image checksums are incorrect"; - - break; - case null: - ImageResultText = "Disc image does not contain checksums"; - - break; - } - }); - - AaruConsole.VerboseWriteLine("Checking disc image checksums took {0} seconds", - checkTime.TotalSeconds); - } - } - - if(VerifySectorsChecked) - { - DateTime startCheck = DateTime.Now; - DateTime endCheck = startCheck; - List failingLbas = new(); - List unknownLbas = new(); + TimeSpan checkTime = endCheck - startCheck; await Dispatcher.UIThread.InvokeAsync(() => { - Progress2Visible = true; - Progress2Indeterminate = false; - Progress2MaxValue = _inputFormat.Info.Sectors / 512d; - StopEnabled = true; + ImageResultVisible = true; + + switch(discCheckStatus) + { + case true: + ImageResultText = "Disc image checksums are correct"; + + break; + case false: + ImageResultText = "Disc image checksums are incorrect"; + + break; + case null: + ImageResultText = "Disc image does not contain checksums"; + + break; + } }); - if(formatHasTracks) + AaruConsole.VerboseWriteLine("Checking disc image checksums took {0} seconds", + checkTime.TotalSeconds); + } + } + + if(VerifySectorsChecked) + { + DateTime startCheck = DateTime.Now; + DateTime endCheck = startCheck; + List failingLbas = new(); + List unknownLbas = new(); + + await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Visible = true; + Progress2Indeterminate = false; + Progress2MaxValue = _inputFormat.Info.Sectors / 512d; + StopEnabled = true; + }); + + if(formatHasTracks) + { + ulong currentSectorAll = 0; + + startCheck = DateTime.UtcNow; + + foreach(Track currentTrack in inputOptical.Tracks) { - ulong currentSectorAll = 0; - - startCheck = DateTime.UtcNow; - - foreach(Track currentTrack in inputOptical.Tracks) + await Dispatcher.UIThread.InvokeAsync(() => { - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = $"Verifying track {currentTrack.Sequence} of {inputOptical.Tracks.Count}"; + ProgressText = $"Verifying track {currentTrack.Sequence} of {inputOptical.Tracks.Count}"; - ProgressValue++; - }); + ProgressValue++; + }); - ulong remainingSectors = currentTrack.EndSector - currentTrack.StartSector; - ulong currentSector = 0; - - while(remainingSectors > 0) - { - if(_cancel) - { - await Dispatcher.UIThread.InvokeAsync(() => - { - CloseVisible = true; - StartVisible = false; - StopVisible = false; - }); - - return; - } - - ulong all = currentSectorAll; - - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Value = all / 512d; - - Progress2Text = - $"Checking sector {all} of {_inputFormat.Info.Sectors}, on track {currentTrack.Sequence}"; - }); - - List tempFailingLbas; - List tempUnknownLbas; - - if(remainingSectors < 512) - inputOptical.VerifySectors(currentSector, (uint)remainingSectors, currentTrack.Sequence, - out tempFailingLbas, out tempUnknownLbas); - else - inputOptical.VerifySectors(currentSector, 512, currentTrack.Sequence, - out tempFailingLbas, out tempUnknownLbas); - - failingLbas.AddRange(tempFailingLbas); - - unknownLbas.AddRange(tempUnknownLbas); - - if(remainingSectors < 512) - { - currentSector += remainingSectors; - currentSectorAll += remainingSectors; - remainingSectors = 0; - } - else - { - currentSector += 512; - currentSectorAll += 512; - remainingSectors -= 512; - } - } - } - - endCheck = DateTime.UtcNow; - } - else if(!(verifiableSectorsImage is null)) - { - ulong remainingSectors = _inputFormat.Info.Sectors; + ulong remainingSectors = currentTrack.EndSector - currentTrack.StartSector; ulong currentSector = 0; - startCheck = DateTime.UtcNow; - while(remainingSectors > 0) { if(_cancel) @@ -550,23 +488,25 @@ namespace Aaru.Gui.ViewModels.Windows return; } - ulong sector = currentSector; + ulong all = currentSectorAll; await Dispatcher.UIThread.InvokeAsync(() => { - Progress2Value = (int)(sector / 512); - Progress2Text = $"Checking sector {sector} of {_inputFormat.Info.Sectors}"; + Progress2Value = all / 512d; + + Progress2Text = + $"Checking sector {all} of {_inputFormat.Info.Sectors}, on track {currentTrack.Sequence}"; }); List tempFailingLbas; List tempUnknownLbas; if(remainingSectors < 512) - verifiableSectorsImage.VerifySectors(currentSector, (uint)remainingSectors, - out tempFailingLbas, out tempUnknownLbas); + inputOptical.VerifySectors(currentSector, (uint)remainingSectors, currentTrack.Sequence, + out tempFailingLbas, out tempUnknownLbas); else - verifiableSectorsImage.VerifySectors(currentSector, 512, out tempFailingLbas, - out tempUnknownLbas); + inputOptical.VerifySectors(currentSector, 512, currentTrack.Sequence, + out tempFailingLbas, out tempUnknownLbas); failingLbas.AddRange(tempFailingLbas); @@ -575,91 +515,150 @@ namespace Aaru.Gui.ViewModels.Windows if(remainingSectors < 512) { currentSector += remainingSectors; + currentSectorAll += remainingSectors; remainingSectors = 0; } else { currentSector += 512; + currentSectorAll += 512; remainingSectors -= 512; } } - - endCheck = DateTime.UtcNow; } - TimeSpan checkTime = endCheck - startCheck; - AaruConsole.VerboseWriteLine("Checking sector checksums took {0} seconds", checkTime.TotalSeconds); + endCheck = DateTime.UtcNow; + } + else if(!(verifiableSectorsImage is null)) + { + ulong remainingSectors = _inputFormat.Info.Sectors; + ulong currentSector = 0; - await Dispatcher.UIThread.InvokeAsync(() => + startCheck = DateTime.UtcNow; + + while(remainingSectors > 0) { - if(failingLbas.Count > 0) + if(_cancel) { - if(failingLbas.Count == (int)_inputFormat.Info.Sectors) + await Dispatcher.UIThread.InvokeAsync(() => { - SectorsErrorsAllVisible = true; - SectorsErrorsAllText = "All sectors contain errors"; - } - else - { - SectorErrorsText = "LBAs with error:"; - SectorErrorsVisible = true; + CloseVisible = true; + StartVisible = false; + StopVisible = false; + }); - foreach(ulong t in failingLbas) - ErrorList.Add(new LbaModel - { - Lba = t.ToString() - }); - } + return; } - if(unknownLbas.Count > 0) + ulong sector = currentSector; + + await Dispatcher.UIThread.InvokeAsync(() => { - if(unknownLbas.Count == (int)_inputFormat.Info.Sectors) - { - SectorsUnknownAllVisible = true; - SectorsUnknownAllText = "All sectors are unknown"; - } - else - { - SectorsUnknownsText = "Unknown LBAs:"; - SectorsUnknownsVisible = true; + Progress2Value = (int)(sector / 512); + Progress2Text = $"Checking sector {sector} of {_inputFormat.Info.Sectors}"; + }); - foreach(ulong t in unknownLbas) - UnknownList.Add(new LbaModel - { - Lba = t.ToString() - }); - } + List tempFailingLbas; + List tempUnknownLbas; + + if(remainingSectors < 512) + verifiableSectorsImage.VerifySectors(currentSector, (uint)remainingSectors, + out tempFailingLbas, out tempUnknownLbas); + else + verifiableSectorsImage.VerifySectors(currentSector, 512, out tempFailingLbas, + out tempUnknownLbas); + + failingLbas.AddRange(tempFailingLbas); + + unknownLbas.AddRange(tempUnknownLbas); + + if(remainingSectors < 512) + { + currentSector += remainingSectors; + remainingSectors = 0; } + else + { + currentSector += 512; + remainingSectors -= 512; + } + } - SectorSummaryVisible = true; - TotalSectorsText = $"Total sectors........... {_inputFormat.Info.Sectors}"; - TotalSectorErrorsText = $"Total errors............ {failingLbas.Count}"; - TotalSectorUnknownsText = $"Total unknowns.......... {unknownLbas.Count}"; - - TotalSectorErrorsUnknownsText = $"Total errors+unknowns... {failingLbas.Count + unknownLbas.Count}"; - }); + endCheck = DateTime.UtcNow; } - Statistics.AddCommand("verify"); + TimeSpan checkTime = endCheck - startCheck; + AaruConsole.VerboseWriteLine("Checking sector checksums took {0} seconds", checkTime.TotalSeconds); await Dispatcher.UIThread.InvokeAsync(() => { - OptionsVisible = false; - ResultsVisible = true; - ProgressVisible = false; - StartVisible = false; - StopVisible = false; - CloseVisible = true; + if(failingLbas.Count > 0) + { + if(failingLbas.Count == (int)_inputFormat.Info.Sectors) + { + SectorsErrorsAllVisible = true; + SectorsErrorsAllText = "All sectors contain errors"; + } + else + { + SectorErrorsText = "LBAs with error:"; + SectorErrorsVisible = true; + + foreach(ulong t in failingLbas) + ErrorList.Add(new LbaModel + { + Lba = t.ToString() + }); + } + } + + if(unknownLbas.Count > 0) + { + if(unknownLbas.Count == (int)_inputFormat.Info.Sectors) + { + SectorsUnknownAllVisible = true; + SectorsUnknownAllText = "All sectors are unknown"; + } + else + { + SectorsUnknownsText = "Unknown LBAs:"; + SectorsUnknownsVisible = true; + + foreach(ulong t in unknownLbas) + UnknownList.Add(new LbaModel + { + Lba = t.ToString() + }); + } + } + + SectorSummaryVisible = true; + TotalSectorsText = $"Total sectors........... {_inputFormat.Info.Sectors}"; + TotalSectorErrorsText = $"Total errors............ {failingLbas.Count}"; + TotalSectorUnknownsText = $"Total unknowns.......... {unknownLbas.Count}"; + + TotalSectorErrorsUnknownsText = $"Total errors+unknowns... {failingLbas.Count + unknownLbas.Count}"; }); } - void ExecuteCloseCommand() => _view.Close(); + Statistics.AddCommand("verify"); - internal void ExecuteStopCommand() + await Dispatcher.UIThread.InvokeAsync(() => { - _cancel = true; - StopEnabled = false; - } + OptionsVisible = false; + ResultsVisible = true; + ProgressVisible = false; + StartVisible = false; + StopVisible = false; + CloseVisible = true; + }); + } + + void ExecuteCloseCommand() => _view.Close(); + + internal void ExecuteStopCommand() + { + _cancel = true; + StopEnabled = false; } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/MainWindowViewModel.cs b/Aaru.Gui/ViewModels/Windows/MainWindowViewModel.cs index b7aab9b11..5af157613 100644 --- a/Aaru.Gui/ViewModels/Windows/MainWindowViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/MainWindowViewModel.cs @@ -67,788 +67,787 @@ using ImageInfo = Aaru.Gui.Views.Panels.ImageInfo; using Partition = Aaru.Gui.Views.Panels.Partition; using PlatformID = Aaru.CommonTypes.Interop.PlatformID; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class MainWindowViewModel : ViewModelBase { - public sealed class MainWindowViewModel : ViewModelBase + readonly IAssetLoader _assets; + readonly DevicesRootModel _devicesRoot; + readonly Bitmap _ejectIcon; + readonly Bitmap _genericFolderIcon; + readonly Bitmap _genericHddIcon; + readonly Bitmap _genericOpticalIcon; + readonly Bitmap _genericTapeIcon; + readonly ImagesRootModel _imagesRoot; + readonly Bitmap _removableIcon; + readonly Bitmap _sdIcon; + readonly Bitmap _usbIcon; + readonly MainWindow _view; + Views.Dialogs.Console _console; + object _contentPanel; + bool _devicesSupported; + object _treeViewSelectedItem; + + public MainWindowViewModel(MainWindow view) { - readonly IAssetLoader _assets; - readonly DevicesRootModel _devicesRoot; - readonly Bitmap _ejectIcon; - readonly Bitmap _genericFolderIcon; - readonly Bitmap _genericHddIcon; - readonly Bitmap _genericOpticalIcon; - readonly Bitmap _genericTapeIcon; - readonly ImagesRootModel _imagesRoot; - readonly Bitmap _removableIcon; - readonly Bitmap _sdIcon; - readonly Bitmap _usbIcon; - readonly MainWindow _view; - Views.Dialogs.Console _console; - object _contentPanel; - bool _devicesSupported; - object _treeViewSelectedItem; + AboutCommand = ReactiveCommand.Create(ExecuteAboutCommand); + EncodingsCommand = ReactiveCommand.Create(ExecuteEncodingsCommand); + PluginsCommand = ReactiveCommand.Create(ExecutePluginsCommand); + StatisticsCommand = ReactiveCommand.Create(ExecuteStatisticsCommand); + ExitCommand = ReactiveCommand.Create(ExecuteExitCommand); + SettingsCommand = ReactiveCommand.Create(ExecuteSettingsCommand); + ConsoleCommand = ReactiveCommand.Create(ExecuteConsoleCommand); + OpenCommand = ReactiveCommand.Create(ExecuteOpenCommand); + CalculateEntropyCommand = ReactiveCommand.Create(ExecuteCalculateEntropyCommand); + VerifyImageCommand = ReactiveCommand.Create(ExecuteVerifyImageCommand); + ChecksumImageCommand = ReactiveCommand.Create(ExecuteChecksumImageCommand); + ConvertImageCommand = ReactiveCommand.Create(ExecuteConvertImageCommand); + CreateSidecarCommand = ReactiveCommand.Create(ExecuteCreateSidecarCommand); + ViewImageSectorsCommand = ReactiveCommand.Create(ExecuteViewImageSectorsCommand); + DecodeImageMediaTagsCommand = ReactiveCommand.Create(ExecuteDecodeImageMediaTagsCommand); + RefreshDevicesCommand = ReactiveCommand.Create(ExecuteRefreshDevicesCommand); + _view = view; + TreeRoot = new ObservableCollection(); + _assets = AvaloniaLocator.Current.GetService(); + ContentPanel = Greeting; - public MainWindowViewModel(MainWindow view) + _imagesRoot = new ImagesRootModel { - AboutCommand = ReactiveCommand.Create(ExecuteAboutCommand); - EncodingsCommand = ReactiveCommand.Create(ExecuteEncodingsCommand); - PluginsCommand = ReactiveCommand.Create(ExecutePluginsCommand); - StatisticsCommand = ReactiveCommand.Create(ExecuteStatisticsCommand); - ExitCommand = ReactiveCommand.Create(ExecuteExitCommand); - SettingsCommand = ReactiveCommand.Create(ExecuteSettingsCommand); - ConsoleCommand = ReactiveCommand.Create(ExecuteConsoleCommand); - OpenCommand = ReactiveCommand.Create(ExecuteOpenCommand); - CalculateEntropyCommand = ReactiveCommand.Create(ExecuteCalculateEntropyCommand); - VerifyImageCommand = ReactiveCommand.Create(ExecuteVerifyImageCommand); - ChecksumImageCommand = ReactiveCommand.Create(ExecuteChecksumImageCommand); - ConvertImageCommand = ReactiveCommand.Create(ExecuteConvertImageCommand); - CreateSidecarCommand = ReactiveCommand.Create(ExecuteCreateSidecarCommand); - ViewImageSectorsCommand = ReactiveCommand.Create(ExecuteViewImageSectorsCommand); - DecodeImageMediaTagsCommand = ReactiveCommand.Create(ExecuteDecodeImageMediaTagsCommand); - RefreshDevicesCommand = ReactiveCommand.Create(ExecuteRefreshDevicesCommand); - _view = view; - TreeRoot = new ObservableCollection(); - _assets = AvaloniaLocator.Current.GetService(); - ContentPanel = Greeting; + Name = "Images" + }; - _imagesRoot = new ImagesRootModel + TreeRoot.Add(_imagesRoot); + + switch(DetectOS.GetRealPlatformID()) + { + case PlatformID.Win32NT: + case PlatformID.Linux: + case PlatformID.FreeBSD: + _devicesRoot = new DevicesRootModel + { + Name = "Devices" + }; + + TreeRoot.Add(_devicesRoot); + DevicesSupported = true; + + break; + } + + _genericHddIcon = + new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png"))); + + _genericOpticalIcon = + new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-optical.png"))); + + _genericTapeIcon = + new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-tape.png"))); + + _genericFolderIcon = + new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png"))); + + _usbIcon = + new + Bitmap(_assets.Open(new + Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-removable-media-usb.png"))); + + _removableIcon = + new + Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-removable-media.png"))); + + _sdIcon = + new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-flash-sd-mmc.png"))); + + _ejectIcon = + new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-eject.png"))); + } + + public bool DevicesSupported + { + get => _devicesSupported; + set => this.RaiseAndSetIfChanged(ref _devicesSupported, value); + } + + public bool NativeMenuSupported => + NativeMenu.GetIsNativeMenuExported((Application.Current.ApplicationLifetime as + IClassicDesktopStyleApplicationLifetime)?.MainWindow); + + [NotNull] + public string Greeting => "Welcome to Aaru!"; + public ObservableCollection TreeRoot { get; } + public ReactiveCommand AboutCommand { get; } + public ReactiveCommand ConsoleCommand { get; } + public ReactiveCommand EncodingsCommand { get; } + public ReactiveCommand PluginsCommand { get; } + public ReactiveCommand StatisticsCommand { get; } + public ReactiveCommand ExitCommand { get; } + public ReactiveCommand SettingsCommand { get; } + public ReactiveCommand OpenCommand { get; } + public ReactiveCommand CalculateEntropyCommand { get; } + public ReactiveCommand VerifyImageCommand { get; } + public ReactiveCommand ChecksumImageCommand { get; } + public ReactiveCommand ConvertImageCommand { get; } + public ReactiveCommand CreateSidecarCommand { get; } + public ReactiveCommand ViewImageSectorsCommand { get; } + public ReactiveCommand DecodeImageMediaTagsCommand { get; } + public ReactiveCommand RefreshDevicesCommand { get; } + + public object ContentPanel + { + get => _contentPanel; + set => this.RaiseAndSetIfChanged(ref _contentPanel, value); + } + + public object TreeViewSelectedItem + { + get => _treeViewSelectedItem; + set + { + if(value == _treeViewSelectedItem) + return; + + this.RaiseAndSetIfChanged(ref _treeViewSelectedItem, value); + + ContentPanel = null; + + switch(value) { - Name = "Images" - }; - - TreeRoot.Add(_imagesRoot); - - switch(DetectOS.GetRealPlatformID()) - { - case PlatformID.Win32NT: - case PlatformID.Linux: - case PlatformID.FreeBSD: - _devicesRoot = new DevicesRootModel + case ImageModel imageModel: + ContentPanel = new ImageInfo { - Name = "Devices" + DataContext = imageModel.ViewModel }; - TreeRoot.Add(_devicesRoot); - DevicesSupported = true; + break; + case PartitionModel partitionModel: + ContentPanel = new Partition + { + DataContext = partitionModel.ViewModel + }; break; - } - - _genericHddIcon = - new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-harddisk.png"))); - - _genericOpticalIcon = - new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-optical.png"))); - - _genericTapeIcon = - new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-tape.png"))); - - _genericFolderIcon = - new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/inode-directory.png"))); - - _usbIcon = - new - Bitmap(_assets.Open(new - Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-removable-media-usb.png"))); - - _removableIcon = - new - Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/drive-removable-media.png"))); - - _sdIcon = - new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-flash-sd-mmc.png"))); - - _ejectIcon = - new Bitmap(_assets.Open(new Uri("avares://Aaru.Gui/Assets/Icons/oxygen/32x32/media-eject.png"))); - } - - public bool DevicesSupported - { - get => _devicesSupported; - set => this.RaiseAndSetIfChanged(ref _devicesSupported, value); - } - - public bool NativeMenuSupported => - NativeMenu.GetIsNativeMenuExported((Application.Current.ApplicationLifetime as - IClassicDesktopStyleApplicationLifetime)?.MainWindow); - - [NotNull] - public string Greeting => "Welcome to Aaru!"; - public ObservableCollection TreeRoot { get; } - public ReactiveCommand AboutCommand { get; } - public ReactiveCommand ConsoleCommand { get; } - public ReactiveCommand EncodingsCommand { get; } - public ReactiveCommand PluginsCommand { get; } - public ReactiveCommand StatisticsCommand { get; } - public ReactiveCommand ExitCommand { get; } - public ReactiveCommand SettingsCommand { get; } - public ReactiveCommand OpenCommand { get; } - public ReactiveCommand CalculateEntropyCommand { get; } - public ReactiveCommand VerifyImageCommand { get; } - public ReactiveCommand ChecksumImageCommand { get; } - public ReactiveCommand ConvertImageCommand { get; } - public ReactiveCommand CreateSidecarCommand { get; } - public ReactiveCommand ViewImageSectorsCommand { get; } - public ReactiveCommand DecodeImageMediaTagsCommand { get; } - public ReactiveCommand RefreshDevicesCommand { get; } - - public object ContentPanel - { - get => _contentPanel; - set => this.RaiseAndSetIfChanged(ref _contentPanel, value); - } - - public object TreeViewSelectedItem - { - get => _treeViewSelectedItem; - set - { - if(value == _treeViewSelectedItem) - return; - - this.RaiseAndSetIfChanged(ref _treeViewSelectedItem, value); - - ContentPanel = null; - - switch(value) - { - case ImageModel imageModel: - ContentPanel = new ImageInfo - { - DataContext = imageModel.ViewModel - }; - - break; - case PartitionModel partitionModel: - ContentPanel = new Partition - { - DataContext = partitionModel.ViewModel - }; - - break; - case FileSystemModel fileSystemModel: - ContentPanel = new FileSystem - { - DataContext = fileSystemModel.ViewModel - }; - - break; - case SubdirectoryModel subdirectoryModel: - ContentPanel = new Subdirectory - { - DataContext = new SubdirectoryViewModel(subdirectoryModel, _view) - }; - - break; - case DeviceModel deviceModel: + case FileSystemModel fileSystemModel: + ContentPanel = new FileSystem { - if(deviceModel.ViewModel is null) + DataContext = fileSystemModel.ViewModel + }; + + break; + case SubdirectoryModel subdirectoryModel: + ContentPanel = new Subdirectory + { + DataContext = new SubdirectoryViewModel(subdirectoryModel, _view) + }; + + break; + case DeviceModel deviceModel: + { + if(deviceModel.ViewModel is null) + { + try { - try + var dev = new Device(deviceModel.Path); + + if(dev.IsRemote) + Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, + dev.RemoteOperatingSystem, dev.RemoteOperatingSystemVersion, + dev.RemoteArchitecture); + + if(dev.Error) { - var dev = new Device(deviceModel.Path); - - if(dev.IsRemote) - Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, - dev.RemoteOperatingSystem, dev.RemoteOperatingSystemVersion, - dev.RemoteArchitecture); - - if(dev.Error) - { - ContentPanel = $"Error {dev.LastError} opening device"; - - return; - } - - var devInfo = new DeviceInfo(dev); - - deviceModel.ViewModel = new DeviceInfoViewModel(devInfo, _view); - - if(!dev.IsRemovable) - deviceModel.Media.Add(new MediaModel - { - NonRemovable = true, - Name = "Non-removable device commands not yet implemented" - }); - else - { - // TODO: Removable non-SCSI? - var scsiInfo = new ScsiInfo(dev); - - if(!scsiInfo.MediaInserted) - deviceModel.Media.Add(new MediaModel - { - NoMediaInserted = true, - Icon = _ejectIcon, - Name = "No media inserted" - }); - else - { - var mediaResource = - new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{scsiInfo.MediaType}.png"); - - deviceModel.Media.Add(new MediaModel - { - DevicePath = deviceModel.Path, - Icon = _assets.Exists(mediaResource) - ? new Bitmap(_assets.Open(mediaResource)) : null, - Name = $"{scsiInfo.MediaType}", - ViewModel = new MediaInfoViewModel(scsiInfo, deviceModel.Path, _view) - }); - } - } - - dev.Close(); - } - catch(SystemException ex) - { - if(Debugger.IsAttached) - throw; - - ContentPanel = ex.Message; - AaruConsole.ErrorWriteLine(ex.Message); + ContentPanel = $"Error {dev.LastError} opening device"; return; } - } - ContentPanel = new Views.Panels.DeviceInfo + var devInfo = new DeviceInfo(dev); + + deviceModel.ViewModel = new DeviceInfoViewModel(devInfo, _view); + + if(!dev.IsRemovable) + deviceModel.Media.Add(new MediaModel + { + NonRemovable = true, + Name = "Non-removable device commands not yet implemented" + }); + else + { + // TODO: Removable non-SCSI? + var scsiInfo = new ScsiInfo(dev); + + if(!scsiInfo.MediaInserted) + deviceModel.Media.Add(new MediaModel + { + NoMediaInserted = true, + Icon = _ejectIcon, + Name = "No media inserted" + }); + else + { + var mediaResource = + new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{scsiInfo.MediaType}.png"); + + deviceModel.Media.Add(new MediaModel + { + DevicePath = deviceModel.Path, + Icon = _assets.Exists(mediaResource) + ? new Bitmap(_assets.Open(mediaResource)) : null, + Name = $"{scsiInfo.MediaType}", + ViewModel = new MediaInfoViewModel(scsiInfo, deviceModel.Path, _view) + }); + } + } + + dev.Close(); + } + catch(SystemException ex) { - DataContext = deviceModel.ViewModel + if(Debugger.IsAttached) + throw; + + ContentPanel = ex.Message; + AaruConsole.ErrorWriteLine(ex.Message); + + return; + } + } + + ContentPanel = new Views.Panels.DeviceInfo + { + DataContext = deviceModel.ViewModel + }; + + break; + } + case MediaModel { NonRemovable: true }: + ContentPanel = "Non-removable device commands not yet implemented"; + + break; + case MediaModel { NoMediaInserted: true }: + ContentPanel = "No media inserted"; + + break; + case MediaModel mediaModel: + { + if(mediaModel.ViewModel != null) + ContentPanel = new MediaInfo + { + DataContext = mediaModel.ViewModel }; - break; - } - case MediaModel { NonRemovable: true }: - ContentPanel = "Non-removable device commands not yet implemented"; - - break; - case MediaModel { NoMediaInserted: true }: - ContentPanel = "No media inserted"; - - break; - case MediaModel mediaModel: - { - if(mediaModel.ViewModel != null) - ContentPanel = new MediaInfo - { - DataContext = mediaModel.ViewModel - }; - - break; - } + break; } } } + } - void ExecuteCalculateEntropyCommand() + void ExecuteCalculateEntropyCommand() + { + if(!(TreeViewSelectedItem is ImageModel imageModel)) + return; + + var imageEntropyWindow = new ImageEntropy(); + imageEntropyWindow.DataContext = new ImageEntropyViewModel(imageModel.Image, imageEntropyWindow); + + imageEntropyWindow.Closed += (sender, args) => imageEntropyWindow = null; + + imageEntropyWindow.Show(); + } + + void ExecuteVerifyImageCommand() + { + if(!(TreeViewSelectedItem is ImageModel imageModel)) + return; + + var imageVerifyWindow = new ImageVerify(); + imageVerifyWindow.DataContext = new ImageVerifyViewModel(imageModel.Image, imageVerifyWindow); + + imageVerifyWindow.Closed += (sender, args) => imageVerifyWindow = null; + + imageVerifyWindow.Show(); + } + + void ExecuteChecksumImageCommand() + { + if(!(TreeViewSelectedItem is ImageModel imageModel)) + return; + + var imageChecksumWindow = new ImageChecksum(); + imageChecksumWindow.DataContext = new ImageChecksumViewModel(imageModel.Image, imageChecksumWindow); + + imageChecksumWindow.Closed += (sender, args) => imageChecksumWindow = null; + + imageChecksumWindow.Show(); + } + + void ExecuteConvertImageCommand() + { + if(!(TreeViewSelectedItem is ImageModel imageModel)) + return; + + var imageConvertWindow = new ImageConvert(); + + imageConvertWindow.DataContext = + new ImageConvertViewModel(imageModel.Image, imageModel.Path, imageConvertWindow); + + imageConvertWindow.Closed += (sender, args) => imageConvertWindow = null; + + imageConvertWindow.Show(); + } + + void ExecuteCreateSidecarCommand() + { + if(!(TreeViewSelectedItem is ImageModel imageModel)) + return; + + var imageSidecarWindow = new ImageSidecar(); + + // TODO: Pass thru chosen default encoding + imageSidecarWindow.DataContext = + new ImageSidecarViewModel(imageModel.Image, imageModel.Path, imageModel.Filter.Id, null, + imageSidecarWindow); + + imageSidecarWindow.Show(); + } + + void ExecuteViewImageSectorsCommand() + { + if(!(TreeViewSelectedItem is ImageModel imageModel)) + return; + + new ViewSector { - if(!(TreeViewSelectedItem is ImageModel imageModel)) - return; + DataContext = new ViewSectorViewModel(imageModel.Image) + }.Show(); + } - var imageEntropyWindow = new ImageEntropy(); - imageEntropyWindow.DataContext = new ImageEntropyViewModel(imageModel.Image, imageEntropyWindow); + void ExecuteDecodeImageMediaTagsCommand() + { + if(!(TreeViewSelectedItem is ImageModel imageModel)) + return; - imageEntropyWindow.Closed += (sender, args) => imageEntropyWindow = null; + new DecodeMediaTags + { + DataContext = new DecodeMediaTagsViewModel(imageModel.Image) + }.Show(); + } - imageEntropyWindow.Show(); + internal void ExecuteAboutCommand() + { + var dialog = new About(); + dialog.DataContext = new AboutViewModel(dialog); + dialog.ShowDialog(_view); + } + + void ExecuteEncodingsCommand() + { + var dialog = new Encodings(); + dialog.DataContext = new EncodingsViewModel(dialog); + dialog.ShowDialog(_view); + } + + void ExecutePluginsCommand() + { + var dialog = new PluginsDialog(); + dialog.DataContext = new PluginsViewModel(dialog); + dialog.ShowDialog(_view); + } + + void ExecuteStatisticsCommand() + { + using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + + if(!ctx.Commands.Any() && + !ctx.Filesystems.Any() && + !ctx.Filters.Any() && + !ctx.MediaFormats.Any() && + !ctx.Medias.Any() && + !ctx.Partitions.Any() && + !ctx.SeenDevices.Any()) + { + MessageBoxManager.GetMessageBoxStandardWindow("Warning", "There are no statistics.").ShowDialog(_view); + + return; } - void ExecuteVerifyImageCommand() + var dialog = new StatisticsDialog(); + dialog.DataContext = new StatisticsViewModel(dialog); + dialog.ShowDialog(_view); + } + + internal async void ExecuteSettingsCommand() + { + var dialog = new SettingsDialog(); + dialog.DataContext = new SettingsViewModel(dialog, false); + await dialog.ShowDialog(_view); + } + + internal void ExecuteExitCommand() => + (Application.Current.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown(); + + void ExecuteConsoleCommand() + { + if(_console is null) { - if(!(TreeViewSelectedItem is ImageModel imageModel)) - return; - - var imageVerifyWindow = new ImageVerify(); - imageVerifyWindow.DataContext = new ImageVerifyViewModel(imageModel.Image, imageVerifyWindow); - - imageVerifyWindow.Closed += (sender, args) => imageVerifyWindow = null; - - imageVerifyWindow.Show(); + _console = new Views.Dialogs.Console(); + _console.DataContext = new ConsoleViewModel(_console); } - void ExecuteChecksumImageCommand() + _console.Show(); + } + + async void ExecuteOpenCommand() + { + // TODO: Extensions + var dlgOpenImage = new OpenFileDialog { - if(!(TreeViewSelectedItem is ImageModel imageModel)) - return; + Title = "Choose image to open", + AllowMultiple = false + }; - var imageChecksumWindow = new ImageChecksum(); - imageChecksumWindow.DataContext = new ImageChecksumViewModel(imageModel.Image, imageChecksumWindow); + string[] result = await dlgOpenImage.ShowAsync(_view); - imageChecksumWindow.Closed += (sender, args) => imageChecksumWindow = null; + if(result?.Length != 1) + return; - imageChecksumWindow.Show(); + var filtersList = new FiltersList(); + IFilter inputFilter = filtersList.GetFilter(result[0]); + + if(inputFilter == null) + { + MessageBoxManager.GetMessageBoxStandardWindow("Error", "Cannot open specified file.", ButtonEnum.Ok, + Icon.Error); + + return; } - void ExecuteConvertImageCommand() + try { - if(!(TreeViewSelectedItem is ImageModel imageModel)) - return; + IMediaImage imageFormat = ImageFormat.Detect(inputFilter) as IMediaImage; - var imageConvertWindow = new ImageConvert(); - - imageConvertWindow.DataContext = - new ImageConvertViewModel(imageModel.Image, imageModel.Path, imageConvertWindow); - - imageConvertWindow.Closed += (sender, args) => imageConvertWindow = null; - - imageConvertWindow.Show(); - } - - void ExecuteCreateSidecarCommand() - { - if(!(TreeViewSelectedItem is ImageModel imageModel)) - return; - - var imageSidecarWindow = new ImageSidecar(); - - // TODO: Pass thru chosen default encoding - imageSidecarWindow.DataContext = - new ImageSidecarViewModel(imageModel.Image, imageModel.Path, imageModel.Filter.Id, null, - imageSidecarWindow); - - imageSidecarWindow.Show(); - } - - void ExecuteViewImageSectorsCommand() - { - if(!(TreeViewSelectedItem is ImageModel imageModel)) - return; - - new ViewSector + if(imageFormat == null) { - DataContext = new ViewSectorViewModel(imageModel.Image) - }.Show(); - } - - void ExecuteDecodeImageMediaTagsCommand() - { - if(!(TreeViewSelectedItem is ImageModel imageModel)) - return; - - new DecodeMediaTags - { - DataContext = new DecodeMediaTagsViewModel(imageModel.Image) - }.Show(); - } - - internal void ExecuteAboutCommand() - { - var dialog = new About(); - dialog.DataContext = new AboutViewModel(dialog); - dialog.ShowDialog(_view); - } - - void ExecuteEncodingsCommand() - { - var dialog = new Encodings(); - dialog.DataContext = new EncodingsViewModel(dialog); - dialog.ShowDialog(_view); - } - - void ExecutePluginsCommand() - { - var dialog = new PluginsDialog(); - dialog.DataContext = new PluginsViewModel(dialog); - dialog.ShowDialog(_view); - } - - void ExecuteStatisticsCommand() - { - using var ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - - if(!ctx.Commands.Any() && - !ctx.Filesystems.Any() && - !ctx.Filters.Any() && - !ctx.MediaFormats.Any() && - !ctx.Medias.Any() && - !ctx.Partitions.Any() && - !ctx.SeenDevices.Any()) - { - MessageBoxManager.GetMessageBoxStandardWindow("Warning", "There are no statistics.").ShowDialog(_view); + MessageBoxManager.GetMessageBoxStandardWindow("Error", "Image format not identified.", + ButtonEnum.Ok, Icon.Error); return; } - var dialog = new StatisticsDialog(); - dialog.DataContext = new StatisticsViewModel(dialog); - dialog.ShowDialog(_view); - } - - internal async void ExecuteSettingsCommand() - { - var dialog = new SettingsDialog(); - dialog.DataContext = new SettingsViewModel(dialog, false); - await dialog.ShowDialog(_view); - } - - internal void ExecuteExitCommand() => - (Application.Current.ApplicationLifetime as ClassicDesktopStyleApplicationLifetime)?.Shutdown(); - - void ExecuteConsoleCommand() - { - if(_console is null) - { - _console = new Views.Dialogs.Console(); - _console.DataContext = new ConsoleViewModel(_console); - } - - _console.Show(); - } - - async void ExecuteOpenCommand() - { - // TODO: Extensions - var dlgOpenImage = new OpenFileDialog - { - Title = "Choose image to open", - AllowMultiple = false - }; - - string[] result = await dlgOpenImage.ShowAsync(_view); - - if(result?.Length != 1) - return; - - var filtersList = new FiltersList(); - IFilter inputFilter = filtersList.GetFilter(result[0]); - - if(inputFilter == null) - { - MessageBoxManager.GetMessageBoxStandardWindow("Error", "Cannot open specified file.", ButtonEnum.Ok, - Icon.Error); - - return; - } + AaruConsole.WriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id); try { - IMediaImage imageFormat = ImageFormat.Detect(inputFilter) as IMediaImage; + ErrorNumber opened = imageFormat.Open(inputFilter); - if(imageFormat == null) + if(opened != ErrorNumber.NoError) { - MessageBoxManager.GetMessageBoxStandardWindow("Error", "Image format not identified.", + MessageBoxManager.GetMessageBoxStandardWindow("Error", $"Error {opened} opening image format.", ButtonEnum.Ok, Icon.Error); + AaruConsole.ErrorWriteLine("Unable to open image format"); + AaruConsole.ErrorWriteLine("No error given"); + return; } - AaruConsole.WriteLine("Image format identified by {0} ({1}).", imageFormat.Name, imageFormat.Id); + var mediaResource = + new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{imageFormat.Info.MediaType}.png"); - try + var imageModel = new ImageModel { - ErrorNumber opened = imageFormat.Open(inputFilter); + Path = result[0], + Icon = _assets.Exists(mediaResource) + ? new Bitmap(_assets.Open(mediaResource)) + : imageFormat.Info.XmlMediaType == XmlMediaType.BlockMedia + ? _genericHddIcon + : imageFormat.Info.XmlMediaType == XmlMediaType.OpticalDisc + ? _genericOpticalIcon + : _genericFolderIcon, + FileName = Path.GetFileName(result[0]), + Image = imageFormat, + ViewModel = new ImageInfoViewModel(result[0], inputFilter, imageFormat, _view), + Filter = inputFilter + }; - if(opened != ErrorNumber.NoError) + List partitions = Core.Partitions.GetAll(imageFormat); + Core.Partitions.AddSchemesToStats(partitions); + + bool checkRaw = false; + List idPlugins; + IFilesystem plugin; + PluginBase plugins = GetPluginBase.Instance; + + if(partitions.Count == 0) + { + AaruConsole.DebugWriteLine("Fs-info command", "No partitions found"); + + checkRaw = true; + } + else + { + AaruConsole.WriteLine("{0} partitions found.", partitions.Count); + + foreach(string scheme in partitions.Select(p => p.Scheme).Distinct().OrderBy(s => s)) { - MessageBoxManager.GetMessageBoxStandardWindow("Error", $"Error {opened} opening image format.", - ButtonEnum.Ok, Icon.Error); - - AaruConsole.ErrorWriteLine("Unable to open image format"); - AaruConsole.ErrorWriteLine("No error given"); - - return; - } - - var mediaResource = - new Uri($"avares://Aaru.Gui/Assets/Logos/Media/{imageFormat.Info.MediaType}.png"); - - var imageModel = new ImageModel - { - Path = result[0], - Icon = _assets.Exists(mediaResource) - ? new Bitmap(_assets.Open(mediaResource)) - : imageFormat.Info.XmlMediaType == XmlMediaType.BlockMedia - ? _genericHddIcon - : imageFormat.Info.XmlMediaType == XmlMediaType.OpticalDisc - ? _genericOpticalIcon - : _genericFolderIcon, - FileName = Path.GetFileName(result[0]), - Image = imageFormat, - ViewModel = new ImageInfoViewModel(result[0], inputFilter, imageFormat, _view), - Filter = inputFilter - }; - - List partitions = Core.Partitions.GetAll(imageFormat); - Core.Partitions.AddSchemesToStats(partitions); - - bool checkRaw = false; - List idPlugins; - IFilesystem plugin; - PluginBase plugins = GetPluginBase.Instance; - - if(partitions.Count == 0) - { - AaruConsole.DebugWriteLine("Fs-info command", "No partitions found"); - - checkRaw = true; - } - else - { - AaruConsole.WriteLine("{0} partitions found.", partitions.Count); - - foreach(string scheme in partitions.Select(p => p.Scheme).Distinct().OrderBy(s => s)) + // TODO: Add icons to partition schemes + var schemeModel = new PartitionSchemeModel { - // TODO: Add icons to partition schemes - var schemeModel = new PartitionSchemeModel - { - Name = scheme - }; - - foreach(CommonTypes.Partition partition in partitions.Where(p => p.Scheme == scheme). - OrderBy(p => p.Start)) - { - var partitionModel = new PartitionModel - { - // TODO: Add icons to partition types - Name = $"{partition.Name} ({partition.Type})", - Partition = partition, - ViewModel = new PartitionViewModel(partition) - }; - - AaruConsole.WriteLine("Identifying filesystem on partition"); - - Core.Filesystems.Identify(imageFormat, out idPlugins, partition); - - if(idPlugins.Count == 0) - AaruConsole.WriteLine("Filesystem not identified"); - else - { - AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); - - foreach(string pluginName in idPlugins) - if(plugins.PluginsList.TryGetValue(pluginName, out plugin)) - { - plugin.GetInformation(imageFormat, partition, out string information, null); - - var fsPlugin = plugin as IReadOnlyFilesystem; - - if(fsPlugin != null) - { - ErrorNumber error = - fsPlugin.Mount(imageFormat, partition, null, - new Dictionary(), null); - - if(error != ErrorNumber.NoError) - fsPlugin = null; - } - - var filesystemModel = new FileSystemModel - { - VolumeName = - plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}" - : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})", - Filesystem = plugin, - ReadOnlyFilesystem = fsPlugin, - ViewModel = new FileSystemViewModel(plugin.XmlFsType, information) - }; - - // TODO: Trap expanding item - if(fsPlugin != null) - { - filesystemModel.Roots.Add(new SubdirectoryModel - { - Name = "/", - Path = "", - Plugin = fsPlugin - }); - - Statistics.AddCommand("ls"); - } - - Statistics.AddFilesystem(plugin.XmlFsType.Type); - partitionModel.FileSystems.Add(filesystemModel); - } - } - - schemeModel.Partitions.Add(partitionModel); - } - - imageModel.PartitionSchemesOrFileSystems.Add(schemeModel); - } - } - - if(checkRaw) - { - var wholePart = new CommonTypes.Partition - { - Name = "Whole device", - Length = imageFormat.Info.Sectors, - Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize + Name = scheme }; - Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart); - - if(idPlugins.Count == 0) - AaruConsole.WriteLine("Filesystem not identified"); - else + foreach(CommonTypes.Partition partition in partitions.Where(p => p.Scheme == scheme). + OrderBy(p => p.Start)) { - AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); + var partitionModel = new PartitionModel + { + // TODO: Add icons to partition types + Name = $"{partition.Name} ({partition.Type})", + Partition = partition, + ViewModel = new PartitionViewModel(partition) + }; - foreach(string pluginName in idPlugins) - if(plugins.PluginsList.TryGetValue(pluginName, out plugin)) - { - plugin.GetInformation(imageFormat, wholePart, out string information, null); + AaruConsole.WriteLine("Identifying filesystem on partition"); - var fsPlugin = plugin as IReadOnlyFilesystem; + Core.Filesystems.Identify(imageFormat, out idPlugins, partition); - if(fsPlugin != null) + if(idPlugins.Count == 0) + AaruConsole.WriteLine("Filesystem not identified"); + else + { + AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); + + foreach(string pluginName in idPlugins) + if(plugins.PluginsList.TryGetValue(pluginName, out plugin)) { - ErrorNumber error = fsPlugin.Mount(imageFormat, wholePart, null, - new Dictionary(), null); + plugin.GetInformation(imageFormat, partition, out string information, null); - if(error != ErrorNumber.NoError) - fsPlugin = null; - } + var fsPlugin = plugin as IReadOnlyFilesystem; - var filesystemModel = new FileSystemModel - { - VolumeName = plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}" - : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})", - Filesystem = plugin, - ReadOnlyFilesystem = fsPlugin, - ViewModel = new FileSystemViewModel(plugin.XmlFsType, information) - }; - - // TODO: Trap expanding item - if(fsPlugin != null) - { - filesystemModel.Roots.Add(new SubdirectoryModel + if(fsPlugin != null) { - Name = "/", - Path = "", - Plugin = fsPlugin - }); + ErrorNumber error = + fsPlugin.Mount(imageFormat, partition, null, + new Dictionary(), null); - Statistics.AddCommand("ls"); + if(error != ErrorNumber.NoError) + fsPlugin = null; + } + + var filesystemModel = new FileSystemModel + { + VolumeName = + plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}" + : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})", + Filesystem = plugin, + ReadOnlyFilesystem = fsPlugin, + ViewModel = new FileSystemViewModel(plugin.XmlFsType, information) + }; + + // TODO: Trap expanding item + if(fsPlugin != null) + { + filesystemModel.Roots.Add(new SubdirectoryModel + { + Name = "/", + Path = "", + Plugin = fsPlugin + }); + + Statistics.AddCommand("ls"); + } + + Statistics.AddFilesystem(plugin.XmlFsType.Type); + partitionModel.FileSystems.Add(filesystemModel); } + } - Statistics.AddFilesystem(plugin.XmlFsType.Type); - imageModel.PartitionSchemesOrFileSystems.Add(filesystemModel); - } + schemeModel.Partitions.Add(partitionModel); } + + imageModel.PartitionSchemesOrFileSystems.Add(schemeModel); } - - Statistics.AddMediaFormat(imageFormat.Format); - Statistics.AddMedia(imageFormat.Info.MediaType, false); - Statistics.AddFilter(inputFilter.Name); - - _imagesRoot.Images.Add(imageModel); } - catch(Exception ex) + + if(checkRaw) { - MessageBoxManager.GetMessageBoxStandardWindow("Error", "Unable to open image format.", - ButtonEnum.Ok, Icon.Error); + var wholePart = new CommonTypes.Partition + { + Name = "Whole device", + Length = imageFormat.Info.Sectors, + Size = imageFormat.Info.Sectors * imageFormat.Info.SectorSize + }; - AaruConsole.ErrorWriteLine("Unable to open image format"); - AaruConsole.ErrorWriteLine("Error: {0}", ex.Message); - AaruConsole.DebugWriteLine("Image-info command", "Stack trace: {0}", ex.StackTrace); + Core.Filesystems.Identify(imageFormat, out idPlugins, wholePart); + + if(idPlugins.Count == 0) + AaruConsole.WriteLine("Filesystem not identified"); + else + { + AaruConsole.WriteLine($"Identified by {idPlugins.Count} plugins"); + + foreach(string pluginName in idPlugins) + if(plugins.PluginsList.TryGetValue(pluginName, out plugin)) + { + plugin.GetInformation(imageFormat, wholePart, out string information, null); + + var fsPlugin = plugin as IReadOnlyFilesystem; + + if(fsPlugin != null) + { + ErrorNumber error = fsPlugin.Mount(imageFormat, wholePart, null, + new Dictionary(), null); + + if(error != ErrorNumber.NoError) + fsPlugin = null; + } + + var filesystemModel = new FileSystemModel + { + VolumeName = plugin.XmlFsType.VolumeName is null ? $"{plugin.XmlFsType.Type}" + : $"{plugin.XmlFsType.VolumeName} ({plugin.XmlFsType.Type})", + Filesystem = plugin, + ReadOnlyFilesystem = fsPlugin, + ViewModel = new FileSystemViewModel(plugin.XmlFsType, information) + }; + + // TODO: Trap expanding item + if(fsPlugin != null) + { + filesystemModel.Roots.Add(new SubdirectoryModel + { + Name = "/", + Path = "", + Plugin = fsPlugin + }); + + Statistics.AddCommand("ls"); + } + + Statistics.AddFilesystem(plugin.XmlFsType.Type); + imageModel.PartitionSchemesOrFileSystems.Add(filesystemModel); + } + } } + + Statistics.AddMediaFormat(imageFormat.Format); + Statistics.AddMedia(imageFormat.Info.MediaType, false); + Statistics.AddFilter(inputFilter.Name); + + _imagesRoot.Images.Add(imageModel); } catch(Exception ex) { - MessageBoxManager.GetMessageBoxStandardWindow("Error", "Exception reading file.", ButtonEnum.Ok, - Icon.Error); + MessageBoxManager.GetMessageBoxStandardWindow("Error", "Unable to open image format.", + ButtonEnum.Ok, Icon.Error); - AaruConsole.ErrorWriteLine($"Error reading file: {ex.Message}"); - AaruConsole.DebugWriteLine("Image-info command", ex.StackTrace); + AaruConsole.ErrorWriteLine("Unable to open image format"); + AaruConsole.ErrorWriteLine("Error: {0}", ex.Message); + AaruConsole.DebugWriteLine("Image-info command", "Stack trace: {0}", ex.StackTrace); } + } + catch(Exception ex) + { + MessageBoxManager.GetMessageBoxStandardWindow("Error", "Exception reading file.", ButtonEnum.Ok, + Icon.Error); - Statistics.AddCommand("image-info"); + AaruConsole.ErrorWriteLine($"Error reading file: {ex.Message}"); + AaruConsole.DebugWriteLine("Image-info command", ex.StackTrace); } - internal void LoadComplete() => RefreshDevices(); + Statistics.AddCommand("image-info"); + } - void ExecuteRefreshDevicesCommand() => RefreshDevices(); + internal void LoadComplete() => RefreshDevices(); - void RefreshDevices() + void ExecuteRefreshDevicesCommand() => RefreshDevices(); + + void RefreshDevices() + { + if(!DevicesSupported) + return; + + try { - if(!DevicesSupported) - return; + AaruConsole.WriteLine("Refreshing devices"); + _devicesRoot.Devices.Clear(); - try + foreach(Devices.DeviceInfo device in Device.ListDevices().Where(d => d.Supported). + OrderBy(d => d.Vendor).ThenBy(d => d.Model)) { - AaruConsole.WriteLine("Refreshing devices"); - _devicesRoot.Devices.Clear(); + AaruConsole.DebugWriteLine("Main window", + "Found supported device model {0} by manufacturer {1} on bus {2} and path {3}", + device.Model, device.Vendor, device.Bus, device.Path); - foreach(Devices.DeviceInfo device in Device.ListDevices().Where(d => d.Supported). - OrderBy(d => d.Vendor).ThenBy(d => d.Model)) + var deviceModel = new DeviceModel { - AaruConsole.DebugWriteLine("Main window", - "Found supported device model {0} by manufacturer {1} on bus {2} and path {3}", - device.Model, device.Vendor, device.Bus, device.Path); + Icon = _genericHddIcon, + Name = $"{device.Vendor} {device.Model} ({device.Bus})", + Path = device.Path + }; - var deviceModel = new DeviceModel + try + { + var dev = new Device(device.Path); + + if(dev.IsRemote) + Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, + dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); + + switch(dev.Type) { - Icon = _genericHddIcon, - Name = $"{device.Vendor} {device.Model} ({device.Bus})", - Path = device.Path - }; + case DeviceType.ATAPI: + case DeviceType.SCSI: + switch(dev.ScsiType) + { + case PeripheralDeviceTypes.DirectAccess: + case PeripheralDeviceTypes.SCSIZonedBlockDevice: + case PeripheralDeviceTypes.SimplifiedDevice: + deviceModel.Icon = dev.IsRemovable ? dev.IsUsb + ? _usbIcon + : _removableIcon : _genericHddIcon; - try - { - var dev = new Device(device.Path); + break; + case PeripheralDeviceTypes.SequentialAccess: + deviceModel.Icon = _genericTapeIcon; - if(dev.IsRemote) - Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, - dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); + break; + case PeripheralDeviceTypes.OpticalDevice: + case PeripheralDeviceTypes.WriteOnceDevice: + case PeripheralDeviceTypes.OCRWDevice: + deviceModel.Icon = _removableIcon; - switch(dev.Type) - { - case DeviceType.ATAPI: - case DeviceType.SCSI: - switch(dev.ScsiType) - { - case PeripheralDeviceTypes.DirectAccess: - case PeripheralDeviceTypes.SCSIZonedBlockDevice: - case PeripheralDeviceTypes.SimplifiedDevice: - deviceModel.Icon = dev.IsRemovable ? dev.IsUsb - ? _usbIcon - : _removableIcon : _genericHddIcon; + break; + case PeripheralDeviceTypes.MultiMediaDevice: + deviceModel.Icon = _genericOpticalIcon; - break; - case PeripheralDeviceTypes.SequentialAccess: - deviceModel.Icon = _genericTapeIcon; + break; + } - break; - case PeripheralDeviceTypes.OpticalDevice: - case PeripheralDeviceTypes.WriteOnceDevice: - case PeripheralDeviceTypes.OCRWDevice: - deviceModel.Icon = _removableIcon; + break; + case DeviceType.SecureDigital: + case DeviceType.MMC: + deviceModel.Icon = _sdIcon; - break; - case PeripheralDeviceTypes.MultiMediaDevice: - deviceModel.Icon = _genericOpticalIcon; + break; + case DeviceType.NVMe: + deviceModel.Icon = null; - break; - } - - break; - case DeviceType.SecureDigital: - case DeviceType.MMC: - deviceModel.Icon = _sdIcon; - - break; - case DeviceType.NVMe: - deviceModel.Icon = null; - - break; - } - - dev.Close(); - } - catch - { - // ignored + break; } - _devicesRoot.Devices.Add(deviceModel); + dev.Close(); } + catch + { + // ignored + } + + _devicesRoot.Devices.Add(deviceModel); } - catch(InvalidOperationException ex) - { - AaruConsole.ErrorWriteLine(ex.Message); - } + } + catch(InvalidOperationException ex) + { + AaruConsole.ErrorWriteLine(ex.Message); } } } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/MediaDumpViewModel.cs b/Aaru.Gui/ViewModels/Windows/MediaDumpViewModel.cs index cce20e6e9..f80a64309 100644 --- a/Aaru.Gui/ViewModels/Windows/MediaDumpViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/MediaDumpViewModel.cs @@ -60,853 +60,852 @@ using Schemas; using DeviceInfo = Aaru.Core.Devices.Info.DeviceInfo; using MediaType = Aaru.CommonTypes.MediaType; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class MediaDumpViewModel : ViewModelBase { - public sealed class MediaDumpViewModel : ViewModelBase + readonly string _devicePath; + readonly Window _view; + bool _closeVisible; + string _destination; + bool _destinationEnabled; + Device _dev; + Dump _dumper; + string _encodingEnabled; + bool _encodingVisible; + bool _existingMetadata; + bool _force; + string _formatReadOnly; + string _log; + bool _optionsVisible; + string _outputPrefix; + bool _persistent; + bool _progress1Visible; + bool _progress2Indeterminate; + double _progress2MaxValue; + string _progress2Text; + double _progress2Value; + bool _progress2Visible; + bool _progressIndeterminate; + double _progressMaxValue; + string _progressText; + double _progressValue; + bool _progressVisible; + Resume _resume; + double _retries; + EncodingModel _selectedEncoding; + ImagePluginModel _selectedPlugin; + CICMMetadataType _sidecar; + double _skipped; + bool _startVisible; + bool _stopEnabled; + bool _stopOnError; + bool _stopVisible; + bool _track1Pregap; + bool _track1PregapVisible; + bool _trim; + bool _useResume; + bool _useSidecar; + + public MediaDumpViewModel(string devicePath, DeviceInfo deviceInfo, Window view, + [CanBeNull] ScsiInfo scsiInfo = null) { - readonly string _devicePath; - readonly Window _view; - bool _closeVisible; - string _destination; - bool _destinationEnabled; - Device _dev; - Dump _dumper; - string _encodingEnabled; - bool _encodingVisible; - bool _existingMetadata; - bool _force; - string _formatReadOnly; - string _log; - bool _optionsVisible; - string _outputPrefix; - bool _persistent; - bool _progress1Visible; - bool _progress2Indeterminate; - double _progress2MaxValue; - string _progress2Text; - double _progress2Value; - bool _progress2Visible; - bool _progressIndeterminate; - double _progressMaxValue; - string _progressText; - double _progressValue; - bool _progressVisible; - Resume _resume; - double _retries; - EncodingModel _selectedEncoding; - ImagePluginModel _selectedPlugin; - CICMMetadataType _sidecar; - double _skipped; - bool _startVisible; - bool _stopEnabled; - bool _stopOnError; - bool _stopVisible; - bool _track1Pregap; - bool _track1PregapVisible; - bool _trim; - bool _useResume; - bool _useSidecar; + _view = view; + DestinationEnabled = true; + StartVisible = true; + CloseVisible = true; + OptionsVisible = true; + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StopCommand = ReactiveCommand.Create(ExecuteStopCommand); + DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand); + PluginsList = new ObservableCollection(); + Encodings = new ObservableCollection(); - public MediaDumpViewModel(string devicePath, DeviceInfo deviceInfo, Window view, - [CanBeNull] ScsiInfo scsiInfo = null) - { - _view = view; - DestinationEnabled = true; - StartVisible = true; - CloseVisible = true; - OptionsVisible = true; - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StopCommand = ReactiveCommand.Create(ExecuteStopCommand); - DestinationCommand = ReactiveCommand.Create(ExecuteDestinationCommand); - PluginsList = new ObservableCollection(); - Encodings = new ObservableCollection(); + // Defaults + StopOnError = false; + Force = false; + Persistent = true; + Resume = true; + Track1Pregap = false; + Sidecar = true; + Trim = true; + ExistingMetadata = false; + Retries = 5; + Skipped = 512; - // Defaults - StopOnError = false; - Force = false; - Persistent = true; - Resume = true; - Track1Pregap = false; - Sidecar = true; - Trim = true; - ExistingMetadata = false; - Retries = 5; - Skipped = 512; + MediaType mediaType; - MediaType mediaType; - - if(scsiInfo != null) - mediaType = scsiInfo.MediaType; - else - switch(deviceInfo.Type) - { - case DeviceType.SecureDigital: - mediaType = MediaType.SecureDigital; - - break; - case DeviceType.MMC: - mediaType = MediaType.MMC; - - break; - default: - if(deviceInfo.IsPcmcia) - mediaType = MediaType.PCCardTypeII; - else if(deviceInfo.IsCompactFlash) - mediaType = MediaType.CompactFlash; - else - mediaType = MediaType.GENERIC_HDD; - - break; - } - - PluginBase plugins = GetPluginBase.Instance; - - foreach(IWritableImage plugin in - plugins.WritableImages.Values.Where(p => p.SupportedMediaTypes.Contains(mediaType))) - PluginsList.Add(new ImagePluginModel - { - Plugin = plugin - }); - - Encodings.AddRange(Encoding.GetEncodings().Select(info => new EncodingModel + if(scsiInfo != null) + mediaType = scsiInfo.MediaType; + else + switch(deviceInfo.Type) { - Name = info.Name, - DisplayName = info.GetEncoding().EncodingName - })); + case DeviceType.SecureDigital: + mediaType = MediaType.SecureDigital; - Encodings.AddRange(Claunia.Encoding.Encoding.GetEncodings().Select(info => new EncodingModel - { - Name = info.Name, - DisplayName = info.DisplayName - })); - - switch(mediaType) - { - case MediaType.CD: - case MediaType.CDDA: - case MediaType.CDG: - case MediaType.CDEG: - case MediaType.CDI: - case MediaType.CDROM: - case MediaType.CDROMXA: - case MediaType.CDPLUS: - case MediaType.CDMO: - case MediaType.CDR: - case MediaType.CDRW: - case MediaType.CDMRW: - case MediaType.VCD: - case MediaType.SVCD: - case MediaType.PCD: - case MediaType.DDCD: - case MediaType.DDCDR: - case MediaType.DDCDRW: - case MediaType.DTSCD: - case MediaType.CDMIDI: - case MediaType.CDV: - case MediaType.CDIREADY: - case MediaType.FMTOWNS: - case MediaType.PS1CD: - case MediaType.PS2CD: - case MediaType.MEGACD: - case MediaType.SATURNCD: - case MediaType.GDROM: - case MediaType.GDR: - case MediaType.MilCD: - case MediaType.SuperCDROM2: - case MediaType.JaguarCD: - case MediaType.ThreeDO: - case MediaType.PCFX: - case MediaType.NeoGeoCD: - case MediaType.CDTV: - case MediaType.CD32: - case MediaType.Playdia: - case MediaType.Pippin: - case MediaType.VideoNow: - case MediaType.VideoNowColor: - case MediaType.VideoNowXp: - case MediaType.CVD: - Track1PregapVisible = true; + break; + case DeviceType.MMC: + mediaType = MediaType.MMC; break; default: - Track1PregapVisible = false; + if(deviceInfo.IsPcmcia) + mediaType = MediaType.PCCardTypeII; + else if(deviceInfo.IsCompactFlash) + mediaType = MediaType.CompactFlash; + else + mediaType = MediaType.GENERIC_HDD; break; } - _devicePath = devicePath; - } + PluginBase plugins = GetPluginBase.Instance; - public ReactiveCommand StartCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StopCommand { get; } - public ReactiveCommand DestinationCommand { get; } - - public ObservableCollection PluginsList { get; } - public ObservableCollection Encodings { get; } - - public string Title { get; } - - public bool OptionsVisible - { - get => _optionsVisible; - set => this.RaiseAndSetIfChanged(ref _optionsVisible, value); - } - - public ImagePluginModel SelectedPlugin - { - get => _selectedPlugin; - set + foreach(IWritableImage plugin in + plugins.WritableImages.Values.Where(p => p.SupportedMediaTypes.Contains(mediaType))) + PluginsList.Add(new ImagePluginModel { - this.RaiseAndSetIfChanged(ref _selectedPlugin, value); - - Destination = ""; - - if(value is null) - { - DestinationEnabled = false; - - return; - } - - DestinationEnabled = true; - - if(!value.Plugin.SupportedOptions.Any()) - { - // Hide options - } - - /* TODO: Plugin options - grpOptions.Visible = true; - - var stkOptions = new StackLayout - { - Orientation = Orientation.Vertical - }; - - foreach((string name, Type type, string description, object @default) option in plugin.SupportedOptions) - switch(option.type.ToString()) - { - case "System.Boolean": - var optBoolean = new CheckBox(); - optBoolean.ID = "opt" + option.name; - optBoolean.Text = option.description; - optBoolean.Checked = (bool)option.@default; - stkOptions.Items.Add(optBoolean); - - break; - case "System.SByte": - case "System.Int16": - case "System.Int32": - case "System.Int64": - var stkNumber = new StackLayout(); - stkNumber.Orientation = Orientation.Horizontal; - var optNumber = new NumericStepper(); - optNumber.ID = "opt" + option.name; - optNumber.Value = Convert.ToDouble(option.@default); - stkNumber.Items.Add(optNumber); - var lblNumber = new Label(); - lblNumber.Text = option.description; - stkNumber.Items.Add(lblNumber); - stkOptions.Items.Add(stkNumber); - - break; - case "System.Byte": - case "System.UInt16": - case "System.UInt32": - case "System.UInt64": - var stkUnsigned = new StackLayout(); - stkUnsigned.Orientation = Orientation.Horizontal; - var optUnsigned = new NumericStepper(); - optUnsigned.ID = "opt" + option.name; - optUnsigned.MinValue = 0; - optUnsigned.Value = Convert.ToDouble(option.@default); - stkUnsigned.Items.Add(optUnsigned); - var lblUnsigned = new Label(); - lblUnsigned.Text = option.description; - stkUnsigned.Items.Add(lblUnsigned); - stkOptions.Items.Add(stkUnsigned); - - break; - case "System.Single": - case "System.Double": - var stkFloat = new StackLayout(); - stkFloat.Orientation = Orientation.Horizontal; - var optFloat = new NumericStepper(); - optFloat.ID = "opt" + option.name; - optFloat.DecimalPlaces = 2; - optFloat.Value = Convert.ToDouble(option.@default); - stkFloat.Items.Add(optFloat); - var lblFloat = new Label(); - lblFloat.Text = option.description; - stkFloat.Items.Add(lblFloat); - stkOptions.Items.Add(stkFloat); - - break; - case "System.Guid": - // TODO - break; - case "System.String": - var stkString = new StackLayout(); - stkString.Orientation = Orientation.Horizontal; - var lblString = new Label(); - lblString.Text = option.description; - stkString.Items.Add(lblString); - var optString = new TextBox(); - optString.ID = "opt" + option.name; - optString.Text = (string)option.@default; - stkString.Items.Add(optString); - stkOptions.Items.Add(stkString); - - break; - } - - grpOptions.Content = stkOptions; - */ - } - } - - public string FormatReadOnly - { - get => _formatReadOnly; - set => this.RaiseAndSetIfChanged(ref _formatReadOnly, value); - } - - public string Destination - { - get => _destination; - set => this.RaiseAndSetIfChanged(ref _destination, value); - } - - public bool DestinationEnabled - { - get => _destinationEnabled; - set => this.RaiseAndSetIfChanged(ref _destinationEnabled, value); - } - - public bool StopOnError - { - get => _stopOnError; - set => this.RaiseAndSetIfChanged(ref _stopOnError, value); - } - - public bool Force - { - get => _force; - set => this.RaiseAndSetIfChanged(ref _force, value); - } - - public double Retries - { - get => _retries; - set => this.RaiseAndSetIfChanged(ref _retries, value); - } - - public bool Persistent - { - get => _persistent; - set => this.RaiseAndSetIfChanged(ref _persistent, value); - } - - public bool Resume - { - get => _useResume; - set - { - this.RaiseAndSetIfChanged(ref _useResume, value); - - if(value == false) - return; - - if(_outputPrefix != null) - CheckResumeFile(); - } - } - - public bool Track1Pregap - { - get => _track1Pregap; - set => this.RaiseAndSetIfChanged(ref _track1Pregap, value); - } - - public bool Track1PregapVisible - { - get => _track1PregapVisible; - set => this.RaiseAndSetIfChanged(ref _track1PregapVisible, value); - } - - public double Skipped - { - get => _skipped; - set => this.RaiseAndSetIfChanged(ref _skipped, value); - } - - public bool Sidecar - { - get => _useSidecar; - set - { - this.RaiseAndSetIfChanged(ref _useSidecar, value); - EncodingVisible = value; - } - } - - public bool EncodingVisible - { - get => _encodingVisible; - set => this.RaiseAndSetIfChanged(ref _encodingVisible, value); - } - - public bool Trim - { - get => _trim; - set => this.RaiseAndSetIfChanged(ref _trim, value); - } - - public bool ExistingMetadata - { - get => _existingMetadata; - set - { - this.RaiseAndSetIfChanged(ref _existingMetadata, value); - - if(value == false) - { - _sidecar = null; - - return; - } - - var dlgMetadata = new OpenFileDialog - { - Title = "Choose existing metadata sidecar" - }; - - dlgMetadata.Filters.Add(new FileDialogFilter - { - Name = "CICM XML metadata", - Extensions = new List(new[] - { - ".xml" - }) - }); - - string[] result = dlgMetadata.ShowAsync(_view).Result; - - if(result?.Length != 1) - { - ExistingMetadata = false; - - return; - } - - var sidecarXs = new XmlSerializer(typeof(CICMMetadataType)); - - try - { - var sr = new StreamReader(result[0]); - _sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr); - sr.Close(); - } - catch - { - // ReSharper disable AssignmentIsFullyDiscarded - _ = MessageBoxManager. - - // ReSharper restore AssignmentIsFullyDiscarded - GetMessageBoxStandardWindow("Error", "Incorrect metadata sidecar file...", ButtonEnum.Ok, - Icon.Error).ShowDialog(_view).Result; - - ExistingMetadata = false; - } - } - } - - public EncodingModel SelectedEncoding - { - get => _selectedEncoding; - set => this.RaiseAndSetIfChanged(ref _selectedEncoding, value); - } - - public string EncodingEnabled - { - get => _encodingEnabled; - set => this.RaiseAndSetIfChanged(ref _encodingEnabled, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public string Log - { - get => _log; - set => this.RaiseAndSetIfChanged(ref _log, value); - } - - public bool Progress1Visible - { - get => _progress1Visible; - set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); - } - - public string ProgressText - { - get => _progressText; - set => this.RaiseAndSetIfChanged(ref _progressText, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public double ProgressMaxValue - { - get => _progressMaxValue; - set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); - } - - public bool ProgressIndeterminate - { - get => _progressIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); - } - - public bool Progress2Visible - { - get => _progress2Visible; - set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); - } - - public string Progress2Text - { - get => _progress2Text; - set => this.RaiseAndSetIfChanged(ref _progress2Text, value); - } - - public double Progress2Value - { - get => _progress2Value; - set => this.RaiseAndSetIfChanged(ref _progress2Value, value); - } - - public double Progress2MaxValue - { - get => _progress2MaxValue; - set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value); - } - - public bool Progress2Indeterminate - { - get => _progress2Indeterminate; - set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); - } - - public bool StartVisible - { - get => _startVisible; - set => this.RaiseAndSetIfChanged(ref _startVisible, value); - } - - public bool CloseVisible - { - get => _closeVisible; - set => this.RaiseAndSetIfChanged(ref _closeVisible, value); - } - - public bool StopVisible - { - get => _stopVisible; - set => this.RaiseAndSetIfChanged(ref _stopVisible, value); - } - - public bool StopEnabled - { - get => _stopEnabled; - set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); - } - - async void ExecuteDestinationCommand() - { - if(SelectedPlugin is null) - return; - - var dlgDestination = new SaveFileDialog - { - Title = "Choose destination file" - }; - - dlgDestination.Filters.Add(new FileDialogFilter - { - Name = SelectedPlugin.Plugin.Name, - Extensions = SelectedPlugin.Plugin.KnownExtensions.ToList() + Plugin = plugin }); - string result = await dlgDestination.ShowAsync(_view); + Encodings.AddRange(Encoding.GetEncodings().Select(info => new EncodingModel + { + Name = info.Name, + DisplayName = info.GetEncoding().EncodingName + })); - if(result is null) + Encodings.AddRange(Claunia.Encoding.Encoding.GetEncodings().Select(info => new EncodingModel + { + Name = info.Name, + DisplayName = info.DisplayName + })); + + switch(mediaType) + { + case MediaType.CD: + case MediaType.CDDA: + case MediaType.CDG: + case MediaType.CDEG: + case MediaType.CDI: + case MediaType.CDROM: + case MediaType.CDROMXA: + case MediaType.CDPLUS: + case MediaType.CDMO: + case MediaType.CDR: + case MediaType.CDRW: + case MediaType.CDMRW: + case MediaType.VCD: + case MediaType.SVCD: + case MediaType.PCD: + case MediaType.DDCD: + case MediaType.DDCDR: + case MediaType.DDCDRW: + case MediaType.DTSCD: + case MediaType.CDMIDI: + case MediaType.CDV: + case MediaType.CDIREADY: + case MediaType.FMTOWNS: + case MediaType.PS1CD: + case MediaType.PS2CD: + case MediaType.MEGACD: + case MediaType.SATURNCD: + case MediaType.GDROM: + case MediaType.GDR: + case MediaType.MilCD: + case MediaType.SuperCDROM2: + case MediaType.JaguarCD: + case MediaType.ThreeDO: + case MediaType.PCFX: + case MediaType.NeoGeoCD: + case MediaType.CDTV: + case MediaType.CD32: + case MediaType.Playdia: + case MediaType.Pippin: + case MediaType.VideoNow: + case MediaType.VideoNowColor: + case MediaType.VideoNowXp: + case MediaType.CVD: + Track1PregapVisible = true; + + break; + default: + Track1PregapVisible = false; + + break; + } + + _devicePath = devicePath; + } + + public ReactiveCommand StartCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StopCommand { get; } + public ReactiveCommand DestinationCommand { get; } + + public ObservableCollection PluginsList { get; } + public ObservableCollection Encodings { get; } + + public string Title { get; } + + public bool OptionsVisible + { + get => _optionsVisible; + set => this.RaiseAndSetIfChanged(ref _optionsVisible, value); + } + + public ImagePluginModel SelectedPlugin + { + get => _selectedPlugin; + set + { + this.RaiseAndSetIfChanged(ref _selectedPlugin, value); + + Destination = ""; + + if(value is null) { - Destination = ""; - _outputPrefix = null; + DestinationEnabled = false; return; } - if(string.IsNullOrEmpty(Path.GetExtension(result))) - result += SelectedPlugin.Plugin.KnownExtensions.First(); + DestinationEnabled = true; - Destination = result; + if(!value.Plugin.SupportedOptions.Any()) + { + // Hide options + } - _outputPrefix = Path.Combine(Path.GetDirectoryName(result) ?? "", Path.GetFileNameWithoutExtension(result)); + /* TODO: Plugin options + grpOptions.Visible = true; - Resume = true; + var stkOptions = new StackLayout + { + Orientation = Orientation.Vertical + }; + + foreach((string name, Type type, string description, object @default) option in plugin.SupportedOptions) + switch(option.type.ToString()) + { + case "System.Boolean": + var optBoolean = new CheckBox(); + optBoolean.ID = "opt" + option.name; + optBoolean.Text = option.description; + optBoolean.Checked = (bool)option.@default; + stkOptions.Items.Add(optBoolean); + + break; + case "System.SByte": + case "System.Int16": + case "System.Int32": + case "System.Int64": + var stkNumber = new StackLayout(); + stkNumber.Orientation = Orientation.Horizontal; + var optNumber = new NumericStepper(); + optNumber.ID = "opt" + option.name; + optNumber.Value = Convert.ToDouble(option.@default); + stkNumber.Items.Add(optNumber); + var lblNumber = new Label(); + lblNumber.Text = option.description; + stkNumber.Items.Add(lblNumber); + stkOptions.Items.Add(stkNumber); + + break; + case "System.Byte": + case "System.UInt16": + case "System.UInt32": + case "System.UInt64": + var stkUnsigned = new StackLayout(); + stkUnsigned.Orientation = Orientation.Horizontal; + var optUnsigned = new NumericStepper(); + optUnsigned.ID = "opt" + option.name; + optUnsigned.MinValue = 0; + optUnsigned.Value = Convert.ToDouble(option.@default); + stkUnsigned.Items.Add(optUnsigned); + var lblUnsigned = new Label(); + lblUnsigned.Text = option.description; + stkUnsigned.Items.Add(lblUnsigned); + stkOptions.Items.Add(stkUnsigned); + + break; + case "System.Single": + case "System.Double": + var stkFloat = new StackLayout(); + stkFloat.Orientation = Orientation.Horizontal; + var optFloat = new NumericStepper(); + optFloat.ID = "opt" + option.name; + optFloat.DecimalPlaces = 2; + optFloat.Value = Convert.ToDouble(option.@default); + stkFloat.Items.Add(optFloat); + var lblFloat = new Label(); + lblFloat.Text = option.description; + stkFloat.Items.Add(lblFloat); + stkOptions.Items.Add(stkFloat); + + break; + case "System.Guid": + // TODO + break; + case "System.String": + var stkString = new StackLayout(); + stkString.Orientation = Orientation.Horizontal; + var lblString = new Label(); + lblString.Text = option.description; + stkString.Items.Add(lblString); + var optString = new TextBox(); + optString.ID = "opt" + option.name; + optString.Text = (string)option.@default; + stkString.Items.Add(optString); + stkOptions.Items.Add(stkString); + + break; + } + + grpOptions.Content = stkOptions; +*/ } + } - async void CheckResumeFile() + public string FormatReadOnly + { + get => _formatReadOnly; + set => this.RaiseAndSetIfChanged(ref _formatReadOnly, value); + } + + public string Destination + { + get => _destination; + set => this.RaiseAndSetIfChanged(ref _destination, value); + } + + public bool DestinationEnabled + { + get => _destinationEnabled; + set => this.RaiseAndSetIfChanged(ref _destinationEnabled, value); + } + + public bool StopOnError + { + get => _stopOnError; + set => this.RaiseAndSetIfChanged(ref _stopOnError, value); + } + + public bool Force + { + get => _force; + set => this.RaiseAndSetIfChanged(ref _force, value); + } + + public double Retries + { + get => _retries; + set => this.RaiseAndSetIfChanged(ref _retries, value); + } + + public bool Persistent + { + get => _persistent; + set => this.RaiseAndSetIfChanged(ref _persistent, value); + } + + public bool Resume + { + get => _useResume; + set { - _resume = null; - var xs = new XmlSerializer(typeof(Resume)); + this.RaiseAndSetIfChanged(ref _useResume, value); + + if(value == false) + return; + + if(_outputPrefix != null) + CheckResumeFile(); + } + } + + public bool Track1Pregap + { + get => _track1Pregap; + set => this.RaiseAndSetIfChanged(ref _track1Pregap, value); + } + + public bool Track1PregapVisible + { + get => _track1PregapVisible; + set => this.RaiseAndSetIfChanged(ref _track1PregapVisible, value); + } + + public double Skipped + { + get => _skipped; + set => this.RaiseAndSetIfChanged(ref _skipped, value); + } + + public bool Sidecar + { + get => _useSidecar; + set + { + this.RaiseAndSetIfChanged(ref _useSidecar, value); + EncodingVisible = value; + } + } + + public bool EncodingVisible + { + get => _encodingVisible; + set => this.RaiseAndSetIfChanged(ref _encodingVisible, value); + } + + public bool Trim + { + get => _trim; + set => this.RaiseAndSetIfChanged(ref _trim, value); + } + + public bool ExistingMetadata + { + get => _existingMetadata; + set + { + this.RaiseAndSetIfChanged(ref _existingMetadata, value); + + if(value == false) + { + _sidecar = null; + + return; + } + + var dlgMetadata = new OpenFileDialog + { + Title = "Choose existing metadata sidecar" + }; + + dlgMetadata.Filters.Add(new FileDialogFilter + { + Name = "CICM XML metadata", + Extensions = new List(new[] + { + ".xml" + }) + }); + + string[] result = dlgMetadata.ShowAsync(_view).Result; + + if(result?.Length != 1) + { + ExistingMetadata = false; + + return; + } + + var sidecarXs = new XmlSerializer(typeof(CICMMetadataType)); try { - var sr = new StreamReader(_outputPrefix + ".resume.xml"); - _resume = (Resume)xs.Deserialize(sr); + var sr = new StreamReader(result[0]); + _sidecar = (CICMMetadataType)sidecarXs.Deserialize(sr); sr.Close(); } catch { - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", "Incorrect resume file, cannot use it...", ButtonEnum.Ok, - Icon.Error).ShowDialog(_view); + // ReSharper disable AssignmentIsFullyDiscarded + _ = MessageBoxManager. - Resume = false; + // ReSharper restore AssignmentIsFullyDiscarded + GetMessageBoxStandardWindow("Error", "Incorrect metadata sidecar file...", ButtonEnum.Ok, + Icon.Error).ShowDialog(_view).Result; - return; + ExistingMetadata = false; } + } + } - if(_resume == null || - _resume.NextBlock <= _resume.LastBlock || - (_resume.BadBlocks.Count != 0 && !_resume.Tape)) - return; + public EncodingModel SelectedEncoding + { + get => _selectedEncoding; + set => this.RaiseAndSetIfChanged(ref _selectedEncoding, value); + } + public string EncodingEnabled + { + get => _encodingEnabled; + set => this.RaiseAndSetIfChanged(ref _encodingEnabled, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public string Log + { + get => _log; + set => this.RaiseAndSetIfChanged(ref _log, value); + } + + public bool Progress1Visible + { + get => _progress1Visible; + set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); + } + + public string ProgressText + { + get => _progressText; + set => this.RaiseAndSetIfChanged(ref _progressText, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public double ProgressMaxValue + { + get => _progressMaxValue; + set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); + } + + public bool ProgressIndeterminate + { + get => _progressIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); + } + + public bool Progress2Visible + { + get => _progress2Visible; + set => this.RaiseAndSetIfChanged(ref _progress2Visible, value); + } + + public string Progress2Text + { + get => _progress2Text; + set => this.RaiseAndSetIfChanged(ref _progress2Text, value); + } + + public double Progress2Value + { + get => _progress2Value; + set => this.RaiseAndSetIfChanged(ref _progress2Value, value); + } + + public double Progress2MaxValue + { + get => _progress2MaxValue; + set => this.RaiseAndSetIfChanged(ref _progress2MaxValue, value); + } + + public bool Progress2Indeterminate + { + get => _progress2Indeterminate; + set => this.RaiseAndSetIfChanged(ref _progress2Indeterminate, value); + } + + public bool StartVisible + { + get => _startVisible; + set => this.RaiseAndSetIfChanged(ref _startVisible, value); + } + + public bool CloseVisible + { + get => _closeVisible; + set => this.RaiseAndSetIfChanged(ref _closeVisible, value); + } + + public bool StopVisible + { + get => _stopVisible; + set => this.RaiseAndSetIfChanged(ref _stopVisible, value); + } + + public bool StopEnabled + { + get => _stopEnabled; + set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); + } + + async void ExecuteDestinationCommand() + { + if(SelectedPlugin is null) + return; + + var dlgDestination = new SaveFileDialog + { + Title = "Choose destination file" + }; + + dlgDestination.Filters.Add(new FileDialogFilter + { + Name = SelectedPlugin.Plugin.Name, + Extensions = SelectedPlugin.Plugin.KnownExtensions.ToList() + }); + + string result = await dlgDestination.ShowAsync(_view); + + if(result is null) + { + Destination = ""; + _outputPrefix = null; + + return; + } + + if(string.IsNullOrEmpty(Path.GetExtension(result))) + result += SelectedPlugin.Plugin.KnownExtensions.First(); + + Destination = result; + + _outputPrefix = Path.Combine(Path.GetDirectoryName(result) ?? "", Path.GetFileNameWithoutExtension(result)); + + Resume = true; + } + + async void CheckResumeFile() + { + _resume = null; + var xs = new XmlSerializer(typeof(Resume)); + + try + { + var sr = new StreamReader(_outputPrefix + ".resume.xml"); + _resume = (Resume)xs.Deserialize(sr); + sr.Close(); + } + catch + { await MessageBoxManager. - GetMessageBoxStandardWindow("Warning", - "Media already dumped correctly, please choose another destination...", - ButtonEnum.Ok, Icon.Warning).ShowDialog(_view); + GetMessageBoxStandardWindow("Error", "Incorrect resume file, cannot use it...", ButtonEnum.Ok, + Icon.Error).ShowDialog(_view); Resume = false; + + return; } - void ExecuteCloseCommand() => _view.Close(); + if(_resume == null || + _resume.NextBlock <= _resume.LastBlock || + (_resume.BadBlocks.Count != 0 && !_resume.Tape)) + return; - internal void ExecuteStopCommand() + await MessageBoxManager. + GetMessageBoxStandardWindow("Warning", + "Media already dumped correctly, please choose another destination...", + ButtonEnum.Ok, Icon.Warning).ShowDialog(_view); + + Resume = false; + } + + void ExecuteCloseCommand() => _view.Close(); + + internal void ExecuteStopCommand() + { + StopEnabled = false; + _dumper?.Abort(); + } + + void ExecuteStartCommand() + { + Log = ""; + CloseVisible = false; + StartVisible = false; + StopVisible = true; + StopEnabled = true; + ProgressVisible = true; + DestinationEnabled = false; + OptionsVisible = false; + + UpdateStatus("Opening device..."); + + try { - StopEnabled = false; - _dumper?.Abort(); + _dev = new Device(_devicePath); + + if(_dev.IsRemote) + Statistics.AddRemote(_dev.RemoteApplication, _dev.RemoteVersion, _dev.RemoteOperatingSystem, + _dev.RemoteOperatingSystemVersion, _dev.RemoteArchitecture); + + if(_dev.Error) + { + StoppingErrorMessage($"Error {_dev.LastError} opening device."); + + return; + } + } + catch(Exception exception) + { + StoppingErrorMessage($"Exception {exception.Message} opening device."); + + return; } - void ExecuteStartCommand() + Statistics.AddDevice(_dev); + Statistics.AddCommand("dump-media"); + + if(SelectedPlugin is null) { - Log = ""; - CloseVisible = false; - StartVisible = false; - StopVisible = true; - StopEnabled = true; - ProgressVisible = true; - DestinationEnabled = false; - OptionsVisible = false; + StoppingErrorMessage("Cannot open output plugin."); - UpdateStatus("Opening device..."); + return; + } + Encoding encoding = null; + + if(!(SelectedEncoding is null)) try { - _dev = new Device(_devicePath); - - if(_dev.IsRemote) - Statistics.AddRemote(_dev.RemoteApplication, _dev.RemoteVersion, _dev.RemoteOperatingSystem, - _dev.RemoteOperatingSystemVersion, _dev.RemoteArchitecture); - - if(_dev.Error) - { - StoppingErrorMessage($"Error {_dev.LastError} opening device."); - - return; - } + encoding = Claunia.Encoding.Encoding.GetEncoding(SelectedEncoding.Name); } - catch(Exception exception) + catch(ArgumentException) { - StoppingErrorMessage($"Exception {exception.Message} opening device."); + StoppingErrorMessage("Specified encoding is not supported."); return; } - Statistics.AddDevice(_dev); - Statistics.AddCommand("dump-media"); + Dictionary parsedOptions = new Dictionary(); - if(SelectedPlugin is null) + /* TODO: Options + if(grpOptions.Content is StackLayout stkFormatOptions) + foreach(Control option in stkFormatOptions.Children) { - StoppingErrorMessage("Cannot open output plugin."); + string value; - return; + switch(option) + { + case CheckBox optBoolean: + value = optBoolean.Checked?.ToString(); + + break; + case NumericStepper optNumber: + value = optNumber.Value.ToString(CultureInfo.CurrentCulture); + + break; + case TextBox optString: + value = optString.Text; + + break; + default: continue; + } + + string key = option.ID.Substring(3); + + parsedOptions.Add(key, value); } + */ - Encoding encoding = null; + var dumpLog = new DumpLog(_outputPrefix + ".log", _dev, false); - if(!(SelectedEncoding is null)) - try - { - encoding = Claunia.Encoding.Encoding.GetEncoding(SelectedEncoding.Name); - } - catch(ArgumentException) - { - StoppingErrorMessage("Specified encoding is not supported."); + dumpLog.WriteLine("Output image format: {0}.", SelectedPlugin.Name); - return; - } + var errorLog = new ErrorLog(_outputPrefix + ".error.log"); - Dictionary parsedOptions = new Dictionary(); + _dumper = new Dump(Resume, _dev, _devicePath, SelectedPlugin.Plugin, (ushort)Retries, Force, false, + Persistent, StopOnError, _resume, dumpLog, encoding, _outputPrefix, Destination, + parsedOptions, _sidecar, (uint)Skipped, ExistingMetadata == false, Trim == false, + Track1Pregap, true, false, DumpSubchannel.Any, 0, false, false, false, false, false, + true, errorLog, false, 64, true, true, false); - /* TODO: Options - if(grpOptions.Content is StackLayout stkFormatOptions) - foreach(Control option in stkFormatOptions.Children) - { - string value; + new Thread(DoWork).Start(); + } - switch(option) - { - case CheckBox optBoolean: - value = optBoolean.Checked?.ToString(); + void DoWork() + { + _dumper.UpdateStatus += UpdateStatus; + _dumper.ErrorMessage += ErrorMessage; + _dumper.StoppingErrorMessage += StoppingErrorMessage; + _dumper.PulseProgress += PulseProgress; + _dumper.InitProgress += InitProgress; + _dumper.UpdateProgress += UpdateProgress; + _dumper.EndProgress += EndProgress; + _dumper.InitProgress2 += InitProgress2; + _dumper.UpdateProgress2 += UpdateProgress2; + _dumper.EndProgress2 += EndProgress2; - break; - case NumericStepper optNumber: - value = optNumber.Value.ToString(CultureInfo.CurrentCulture); + _dumper.Start(); - break; - case TextBox optString: - value = optString.Text; + _dev.Close(); - break; - default: continue; - } + WorkFinished(); + } - string key = option.ID.Substring(3); + async void WorkFinished() => await Dispatcher.UIThread.InvokeAsync(() => + { + CloseVisible = true; + StopVisible = false; + Progress1Visible = false; + Progress2Visible = false; + }); - parsedOptions.Add(key, value); - } - */ + async void EndProgress2() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Visible = false; + }); - var dumpLog = new DumpLog(_outputPrefix + ".log", _dev, false); - - dumpLog.WriteLine("Output image format: {0}.", SelectedPlugin.Name); - - var errorLog = new ErrorLog(_outputPrefix + ".error.log"); - - _dumper = new Dump(Resume, _dev, _devicePath, SelectedPlugin.Plugin, (ushort)Retries, Force, false, - Persistent, StopOnError, _resume, dumpLog, encoding, _outputPrefix, Destination, - parsedOptions, _sidecar, (uint)Skipped, ExistingMetadata == false, Trim == false, - Track1Pregap, true, false, DumpSubchannel.Any, 0, false, false, false, false, false, - true, errorLog, false, 64, true, true, false); - - new Thread(DoWork).Start(); - } - - void DoWork() + async void UpdateProgress2(string text, long current, long maximum) => + await Dispatcher.UIThread.InvokeAsync(() => { - _dumper.UpdateStatus += UpdateStatus; - _dumper.ErrorMessage += ErrorMessage; - _dumper.StoppingErrorMessage += StoppingErrorMessage; - _dumper.PulseProgress += PulseProgress; - _dumper.InitProgress += InitProgress; - _dumper.UpdateProgress += UpdateProgress; - _dumper.EndProgress += EndProgress; - _dumper.InitProgress2 += InitProgress2; - _dumper.UpdateProgress2 += UpdateProgress2; - _dumper.EndProgress2 += EndProgress2; + Progress2Text = text; + Progress2Indeterminate = false; - _dumper.Start(); - - _dev.Close(); - - WorkFinished(); - } - - async void WorkFinished() => await Dispatcher.UIThread.InvokeAsync(() => - { - CloseVisible = true; - StopVisible = false; - Progress1Visible = false; - Progress2Visible = false; + Progress2MaxValue = maximum; + Progress2Value = current; }); - async void EndProgress2() => await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Visible = false; - }); + async void InitProgress2() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress2Visible = true; + }); - async void UpdateProgress2(string text, long current, long maximum) => - await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Text = text; - Progress2Indeterminate = false; + async void EndProgress() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress1Visible = false; + }); - Progress2MaxValue = maximum; - Progress2Value = current; - }); - - async void InitProgress2() => await Dispatcher.UIThread.InvokeAsync(() => - { - Progress2Visible = true; - }); - - async void EndProgress() => await Dispatcher.UIThread.InvokeAsync(() => - { - Progress1Visible = false; - }); - - async void UpdateProgress(string text, long current, long maximum) => - await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = text; - ProgressIndeterminate = false; - - ProgressMaxValue = maximum; - ProgressValue = current; - }); - - async void InitProgress() => await Dispatcher.UIThread.InvokeAsync(() => - { - Progress1Visible = true; - }); - - async void PulseProgress(string text) => await Dispatcher.UIThread.InvokeAsync(() => + async void UpdateProgress(string text, long current, long maximum) => + await Dispatcher.UIThread.InvokeAsync(() => { ProgressText = text; - ProgressIndeterminate = true; + ProgressIndeterminate = false; + + ProgressMaxValue = maximum; + ProgressValue = current; }); - async void StoppingErrorMessage(string text) => await Dispatcher.UIThread.InvokeAsync(async () => - { - ErrorMessage(text); + async void InitProgress() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress1Visible = true; + }); - await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"{text}", ButtonEnum.Ok, Icon.Error). - ShowDialog(_view); + async void PulseProgress(string text) => await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = text; + ProgressIndeterminate = true; + }); - WorkFinished(); - }); + async void StoppingErrorMessage(string text) => await Dispatcher.UIThread.InvokeAsync(async () => + { + ErrorMessage(text); - async void ErrorMessage(string text) => await Dispatcher.UIThread.InvokeAsync(() => - { - Log += text + Environment.NewLine; - }); + await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"{text}", ButtonEnum.Ok, Icon.Error). + ShowDialog(_view); - async void UpdateStatus(string text) => await Dispatcher.UIThread.InvokeAsync(() => - { - Log += text + Environment.NewLine; - }); - } + WorkFinished(); + }); + + async void ErrorMessage(string text) => await Dispatcher.UIThread.InvokeAsync(() => + { + Log += text + Environment.NewLine; + }); + + async void UpdateStatus(string text) => await Dispatcher.UIThread.InvokeAsync(() => + { + Log += text + Environment.NewLine; + }); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/MediaScanViewModel.cs b/Aaru.Gui/ViewModels/Windows/MediaScanViewModel.cs index 379fa817b..82a3fbb2f 100644 --- a/Aaru.Gui/ViewModels/Windows/MediaScanViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/MediaScanViewModel.cs @@ -44,564 +44,563 @@ using MessageBox.Avalonia.Enums; using OxyPlot; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class MediaScanViewModel : ViewModelBase { - public sealed class MediaScanViewModel : ViewModelBase + readonly Window _view; + string _a; + string _avgSpeed; + Color _axesColor; + string _b; + ulong _blocks; + ulong _blocksToRead; + string _c; + bool _closeVisible; + string _d; + string _devicePath; + string _e; + string _f; + Color _lineColor; + ScanResults _localResults; + string _maxSpeed; + double _maxX; + double _maxY; + string _minSpeed; + double _minX; + double _minY; + bool _progress1Visible; + string _progress2Indeterminate; + string _progress2MaxValue; + string _progress2Text; + string _progress2Value; + string _progress2Visible; + bool _progressIndeterminate; + double _progressMaxValue; + string _progressText; + double _progressValue; + bool _progressVisible; + bool _resultsVisible; + MediaScan _scanner; + bool _startVisible; + double _stepsX; + double _stepsY; + string _stopEnabled; + bool _stopVisible; + string _totalTime; + string _unreadableSectors; + + public MediaScanViewModel(string devicePath, Window view) { - readonly Window _view; - string _a; - string _avgSpeed; - Color _axesColor; - string _b; - ulong _blocks; - ulong _blocksToRead; - string _c; - bool _closeVisible; - string _d; - string _devicePath; - string _e; - string _f; - Color _lineColor; - ScanResults _localResults; - string _maxSpeed; - double _maxX; - double _maxY; - string _minSpeed; - double _minX; - double _minY; - bool _progress1Visible; - string _progress2Indeterminate; - string _progress2MaxValue; - string _progress2Text; - string _progress2Value; - string _progress2Visible; - bool _progressIndeterminate; - double _progressMaxValue; - string _progressText; - double _progressValue; - bool _progressVisible; - bool _resultsVisible; - MediaScan _scanner; - bool _startVisible; - double _stepsX; - double _stepsY; - string _stopEnabled; - bool _stopVisible; - string _totalTime; - string _unreadableSectors; + _devicePath = devicePath; + _view = view; + StopVisible = false; + StartCommand = ReactiveCommand.Create(ExecuteStartCommand); + CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); + StopCommand = ReactiveCommand.Create(ExecuteStopCommand); + StartVisible = true; + CloseVisible = true; + BlockMapList = new ObservableCollection<(ulong block, double duration)>(); + ChartPoints = new ObservableCollection(); + StepsX = double.NaN; + StepsY = double.NaN; + AxesColor = Colors.Black; + LineColor = Colors.Yellow; + } - public MediaScanViewModel(string devicePath, Window view) + public Color AxesColor + { + get => _axesColor; + set => this.RaiseAndSetIfChanged(ref _axesColor, value); + } + + public Color LineColor + { + get => _lineColor; + set => this.RaiseAndSetIfChanged(ref _lineColor, value); + } + + public ObservableCollection<(ulong block, double duration)> BlockMapList { get; } + public ObservableCollection ChartPoints { get; } + + public ulong Blocks + { + get => _blocks; + set => this.RaiseAndSetIfChanged(ref _blocks, value); + } + + public string A + { + get => _a; + set => this.RaiseAndSetIfChanged(ref _a, value); + } + + public string B + { + get => _b; + set => this.RaiseAndSetIfChanged(ref _b, value); + } + + public string C + { + get => _c; + set => this.RaiseAndSetIfChanged(ref _c, value); + } + + public string D + { + get => _d; + set => this.RaiseAndSetIfChanged(ref _d, value); + } + + public string E + { + get => _e; + set => this.RaiseAndSetIfChanged(ref _e, value); + } + + public string F + { + get => _f; + set => this.RaiseAndSetIfChanged(ref _f, value); + } + + public string UnreadableSectors + { + get => _unreadableSectors; + set => this.RaiseAndSetIfChanged(ref _unreadableSectors, value); + } + + public string TotalTime + { + get => _totalTime; + set => this.RaiseAndSetIfChanged(ref _totalTime, value); + } + + public string AvgSpeed + { + get => _avgSpeed; + set => this.RaiseAndSetIfChanged(ref _avgSpeed, value); + } + + public string MaxSpeed + { + get => _maxSpeed; + set => this.RaiseAndSetIfChanged(ref _maxSpeed, value); + } + + public string MinSpeed + { + get => _minSpeed; + set => this.RaiseAndSetIfChanged(ref _minSpeed, value); + } + + public bool ProgressVisible + { + get => _progressVisible; + set => this.RaiseAndSetIfChanged(ref _progressVisible, value); + } + + public bool Progress1Visible + { + get => _progress1Visible; + set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); + } + + public string ProgressText + { + get => _progressText; + set => this.RaiseAndSetIfChanged(ref _progressText, value); + } + + public double ProgressMaxValue + { + get => _progressMaxValue; + set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); + } + + public bool ProgressIndeterminate + { + get => _progressIndeterminate; + set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); + } + + public double ProgressValue + { + get => _progressValue; + set => this.RaiseAndSetIfChanged(ref _progressValue, value); + } + + public bool StartVisible + { + get => _startVisible; + set => this.RaiseAndSetIfChanged(ref _startVisible, value); + } + + public bool CloseVisible + { + get => _closeVisible; + set => this.RaiseAndSetIfChanged(ref _closeVisible, value); + } + + public bool StopVisible + { + get => _stopVisible; + set => this.RaiseAndSetIfChanged(ref _stopVisible, value); + } + + public string StopEnabled + { + get => _stopEnabled; + set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); + } + + public bool ResultsVisible + { + get => _resultsVisible; + set => this.RaiseAndSetIfChanged(ref _resultsVisible, value); + } + + public double MaxY + { + get => _maxY; + set => this.RaiseAndSetIfChanged(ref _maxY, value); + } + + public double MaxX + { + get => _maxX; + set => this.RaiseAndSetIfChanged(ref _maxX, value); + } + + public double MinY + { + get => _minY; + set => this.RaiseAndSetIfChanged(ref _minY, value); + } + + public double MinX + { + get => _minX; + set => this.RaiseAndSetIfChanged(ref _minX, value); + } + + public double StepsY + { + get => _stepsY; + set => this.RaiseAndSetIfChanged(ref _stepsY, value); + } + + public double StepsX + { + get => _stepsX; + set => this.RaiseAndSetIfChanged(ref _stepsX, value); + } + + public string Title { get; } + + public ReactiveCommand StartCommand { get; } + public ReactiveCommand CloseCommand { get; } + public ReactiveCommand StopCommand { get; } + + void ExecuteCloseCommand() => _view.Close(); + + internal void ExecuteStopCommand() => _scanner?.Abort(); + + void ExecuteStartCommand() + { + StopVisible = true; + StartVisible = false; + CloseVisible = false; + ProgressVisible = true; + ResultsVisible = true; + ChartPoints.Clear(); + new Thread(DoWork).Start(); + } + + // TODO: Allow to save MHDD and ImgBurn log files + async void DoWork() + { + if(_devicePath.Length == 2 && + _devicePath[1] == ':' && + _devicePath[0] != '/' && + char.IsLetter(_devicePath[0])) + _devicePath = "\\\\.\\" + char.ToUpper(_devicePath[0]) + ':'; + + var dev = new Device(_devicePath); + + if(dev.IsRemote) + Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, + dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); + + if(dev.Error) { - _devicePath = devicePath; - _view = view; - StopVisible = false; - StartCommand = ReactiveCommand.Create(ExecuteStartCommand); - CloseCommand = ReactiveCommand.Create(ExecuteCloseCommand); - StopCommand = ReactiveCommand.Create(ExecuteStopCommand); - StartVisible = true; - CloseVisible = true; - BlockMapList = new ObservableCollection<(ulong block, double duration)>(); - ChartPoints = new ObservableCollection(); - StepsX = double.NaN; - StepsY = double.NaN; - AxesColor = Colors.Black; - LineColor = Colors.Yellow; - } + await MessageBoxManager. + GetMessageBoxStandardWindow("Error", $"Error {dev.LastError} opening device.", ButtonEnum.Ok, + Icon.Error).ShowDialog(_view); - public Color AxesColor - { - get => _axesColor; - set => this.RaiseAndSetIfChanged(ref _axesColor, value); - } - - public Color LineColor - { - get => _lineColor; - set => this.RaiseAndSetIfChanged(ref _lineColor, value); - } - - public ObservableCollection<(ulong block, double duration)> BlockMapList { get; } - public ObservableCollection ChartPoints { get; } - - public ulong Blocks - { - get => _blocks; - set => this.RaiseAndSetIfChanged(ref _blocks, value); - } - - public string A - { - get => _a; - set => this.RaiseAndSetIfChanged(ref _a, value); - } - - public string B - { - get => _b; - set => this.RaiseAndSetIfChanged(ref _b, value); - } - - public string C - { - get => _c; - set => this.RaiseAndSetIfChanged(ref _c, value); - } - - public string D - { - get => _d; - set => this.RaiseAndSetIfChanged(ref _d, value); - } - - public string E - { - get => _e; - set => this.RaiseAndSetIfChanged(ref _e, value); - } - - public string F - { - get => _f; - set => this.RaiseAndSetIfChanged(ref _f, value); - } - - public string UnreadableSectors - { - get => _unreadableSectors; - set => this.RaiseAndSetIfChanged(ref _unreadableSectors, value); - } - - public string TotalTime - { - get => _totalTime; - set => this.RaiseAndSetIfChanged(ref _totalTime, value); - } - - public string AvgSpeed - { - get => _avgSpeed; - set => this.RaiseAndSetIfChanged(ref _avgSpeed, value); - } - - public string MaxSpeed - { - get => _maxSpeed; - set => this.RaiseAndSetIfChanged(ref _maxSpeed, value); - } - - public string MinSpeed - { - get => _minSpeed; - set => this.RaiseAndSetIfChanged(ref _minSpeed, value); - } - - public bool ProgressVisible - { - get => _progressVisible; - set => this.RaiseAndSetIfChanged(ref _progressVisible, value); - } - - public bool Progress1Visible - { - get => _progress1Visible; - set => this.RaiseAndSetIfChanged(ref _progress1Visible, value); - } - - public string ProgressText - { - get => _progressText; - set => this.RaiseAndSetIfChanged(ref _progressText, value); - } - - public double ProgressMaxValue - { - get => _progressMaxValue; - set => this.RaiseAndSetIfChanged(ref _progressMaxValue, value); - } - - public bool ProgressIndeterminate - { - get => _progressIndeterminate; - set => this.RaiseAndSetIfChanged(ref _progressIndeterminate, value); - } - - public double ProgressValue - { - get => _progressValue; - set => this.RaiseAndSetIfChanged(ref _progressValue, value); - } - - public bool StartVisible - { - get => _startVisible; - set => this.RaiseAndSetIfChanged(ref _startVisible, value); - } - - public bool CloseVisible - { - get => _closeVisible; - set => this.RaiseAndSetIfChanged(ref _closeVisible, value); - } - - public bool StopVisible - { - get => _stopVisible; - set => this.RaiseAndSetIfChanged(ref _stopVisible, value); - } - - public string StopEnabled - { - get => _stopEnabled; - set => this.RaiseAndSetIfChanged(ref _stopEnabled, value); - } - - public bool ResultsVisible - { - get => _resultsVisible; - set => this.RaiseAndSetIfChanged(ref _resultsVisible, value); - } - - public double MaxY - { - get => _maxY; - set => this.RaiseAndSetIfChanged(ref _maxY, value); - } - - public double MaxX - { - get => _maxX; - set => this.RaiseAndSetIfChanged(ref _maxX, value); - } - - public double MinY - { - get => _minY; - set => this.RaiseAndSetIfChanged(ref _minY, value); - } - - public double MinX - { - get => _minX; - set => this.RaiseAndSetIfChanged(ref _minX, value); - } - - public double StepsY - { - get => _stepsY; - set => this.RaiseAndSetIfChanged(ref _stepsY, value); - } - - public double StepsX - { - get => _stepsX; - set => this.RaiseAndSetIfChanged(ref _stepsX, value); - } - - public string Title { get; } - - public ReactiveCommand StartCommand { get; } - public ReactiveCommand CloseCommand { get; } - public ReactiveCommand StopCommand { get; } - - void ExecuteCloseCommand() => _view.Close(); - - internal void ExecuteStopCommand() => _scanner?.Abort(); - - void ExecuteStartCommand() - { - StopVisible = true; - StartVisible = false; - CloseVisible = false; - ProgressVisible = true; - ResultsVisible = true; - ChartPoints.Clear(); - new Thread(DoWork).Start(); - } - - // TODO: Allow to save MHDD and ImgBurn log files - async void DoWork() - { - if(_devicePath.Length == 2 && - _devicePath[1] == ':' && - _devicePath[0] != '/' && - char.IsLetter(_devicePath[0])) - _devicePath = "\\\\.\\" + char.ToUpper(_devicePath[0]) + ':'; - - var dev = new Device(_devicePath); - - if(dev.IsRemote) - Statistics.AddRemote(dev.RemoteApplication, dev.RemoteVersion, dev.RemoteOperatingSystem, - dev.RemoteOperatingSystemVersion, dev.RemoteArchitecture); - - if(dev.Error) - { - await MessageBoxManager. - GetMessageBoxStandardWindow("Error", $"Error {dev.LastError} opening device.", ButtonEnum.Ok, - Icon.Error).ShowDialog(_view); - - StopVisible = false; - StartVisible = true; - CloseVisible = true; - ProgressVisible = false; - - return; - } - - Statistics.AddDevice(dev); - - _localResults = new ScanResults(); - _scanner = new MediaScan(null, null, _devicePath, dev, false); - _scanner.ScanTime += OnScanTime; - _scanner.ScanUnreadable += OnScanUnreadable; - _scanner.UpdateStatus += UpdateStatus; - _scanner.StoppingErrorMessage += StoppingErrorMessage; - _scanner.PulseProgress += PulseProgress; - _scanner.InitProgress += InitProgress; - _scanner.UpdateProgress += UpdateProgress; - _scanner.EndProgress += EndProgress; - _scanner.InitBlockMap += InitBlockMap; - _scanner.ScanSpeed += ScanSpeed; - - ScanResults results = _scanner.Scan(); - - await Dispatcher.UIThread.InvokeAsync(() => - { - TotalTime = - $"Took a total of {results.TotalTime} seconds ({results.ProcessingTime} processing commands)."; - - AvgSpeed = $"Average speed: {results.AvgSpeed:F3} MiB/sec."; - MaxSpeed = $"Fastest speed burst: {results.MaxSpeed:F3} MiB/sec."; - MinSpeed = $"Slowest speed burst: {results.MinSpeed:F3} MiB/sec."; - A = $"{results.A} sectors took less than 3 ms."; - B = $"{results.B} sectors took less than 10 ms but more than 3 ms."; - C = $"{results.C} sectors took less than 50 ms but more than 10 ms."; - D = $"{results.D} sectors took less than 150 ms but more than 50 ms."; - E = $"{results.E} sectors took less than 500 ms but more than 150 ms."; - F = $"{results.F} sectors took more than 500 ms."; - UnreadableSectors = $"{results.UnreadableSectors.Count} sectors could not be read."; - }); - - // TODO: Show list of unreadable sectors - /* - if(results.UnreadableSectors.Count > 0) - foreach(ulong bad in results.UnreadableSectors) - string.Format("Sector {0} could not be read", bad); -*/ - - // TODO: Show results - /* - - if(results.SeekTotal != 0 || results.SeekMin != double.MaxValue || results.SeekMax != double.MinValue) - - string.Format("Testing {0} seeks, longest seek took {1:F3} ms, fastest one took {2:F3} ms. ({3:F3} ms average)", - results.SeekTimes, results.SeekMax, results.SeekMin, results.SeekTotal / 1000); - */ - - Statistics.AddCommand("media-scan"); - - dev.Close(); - WorkFinished(); - } - - async void ScanSpeed(ulong sector, double currentSpeed) => await Dispatcher.UIThread.InvokeAsync(() => - { - if(ChartPoints.Count == 0) - ChartPoints.Add(new DataPoint(0, currentSpeed)); - - ChartPoints.Add(new DataPoint(sector, currentSpeed)); - - if(currentSpeed > MaxY) - MaxY = currentSpeed + (currentSpeed / 10d); - }); - - async void InitBlockMap(ulong blocks, ulong blockSize, ulong blocksToRead, ushort currentProfile) => - await Dispatcher.UIThread.InvokeAsync(() => - { - Blocks = blocks / blocksToRead; - _blocksToRead = blocksToRead; - - MinX = 0; - MinY = 0; - - switch(currentProfile) - { - case 0x0005: // CD and DDCD - case 0x0008: - case 0x0009: - case 0x000A: - case 0x0020: - case 0x0021: - case 0x0022: - if(blocks <= 360000) - MaxX = 360000; - else if(blocks <= 405000) - MaxX = 405000; - else if(blocks <= 445500) - MaxX = 445500; - else - MaxX = blocks; - - StepsX = MaxX / 10; - StepsY = 150 * 4; - MaxY = StepsY * 12.5; - - break; - case 0x0010: // DVD SL - case 0x0011: - case 0x0012: - case 0x0013: - case 0x0014: - case 0x0018: - case 0x001A: - case 0x001B: - MaxX = 2298496; - StepsX = MaxX / 10; - StepsY = 1352.5; - MaxY = StepsY * 18; - - break; - case 0x0015: // DVD DL - case 0x0016: - case 0x0017: - case 0x002A: - case 0x002B: - MaxX = 4173824; - StepsX = MaxX / 10; - StepsY = 1352.5; - MaxY = StepsY * 18; - - break; - case 0x0041: - case 0x0042: - case 0x0043: - case 0x0040: // BD - if(blocks <= 12219392) - MaxX = 12219392; - else if(blocks <= 24438784) - MaxX = 24438784; - else if(blocks <= 48878592) - MaxX = 48878592; - else if(blocks <= 62500864) - MaxX = 62500864; - else - MaxX = blocks; - - StepsX = MaxX / 10; - StepsY = 4394.5; - MaxY = StepsY * 18; - - break; - case 0x0050: // HD DVD - case 0x0051: - case 0x0052: - case 0x0053: - case 0x0058: - case 0x005A: - if(blocks <= 7361599) - MaxX = 7361599; - else if(blocks <= 16305407) - MaxX = 16305407; - else - MaxX = blocks; - - StepsX = MaxX / 10; - StepsY = 4394.5; - MaxY = StepsY * 8; - - break; - default: - MaxX = blocks; - StepsX = MaxX / 10; - StepsY = 625; - MaxY = StepsY; - - break; - } - }); - - async void WorkFinished() => await Dispatcher.UIThread.InvokeAsync(() => - { StopVisible = false; StartVisible = true; CloseVisible = true; ProgressVisible = false; - }); - async void EndProgress() => await Dispatcher.UIThread.InvokeAsync(() => + return; + } + + Statistics.AddDevice(dev); + + _localResults = new ScanResults(); + _scanner = new MediaScan(null, null, _devicePath, dev, false); + _scanner.ScanTime += OnScanTime; + _scanner.ScanUnreadable += OnScanUnreadable; + _scanner.UpdateStatus += UpdateStatus; + _scanner.StoppingErrorMessage += StoppingErrorMessage; + _scanner.PulseProgress += PulseProgress; + _scanner.InitProgress += InitProgress; + _scanner.UpdateProgress += UpdateProgress; + _scanner.EndProgress += EndProgress; + _scanner.InitBlockMap += InitBlockMap; + _scanner.ScanSpeed += ScanSpeed; + + ScanResults results = _scanner.Scan(); + + await Dispatcher.UIThread.InvokeAsync(() => { - Progress1Visible = false; + TotalTime = + $"Took a total of {results.TotalTime} seconds ({results.ProcessingTime} processing commands)."; + + AvgSpeed = $"Average speed: {results.AvgSpeed:F3} MiB/sec."; + MaxSpeed = $"Fastest speed burst: {results.MaxSpeed:F3} MiB/sec."; + MinSpeed = $"Slowest speed burst: {results.MinSpeed:F3} MiB/sec."; + A = $"{results.A} sectors took less than 3 ms."; + B = $"{results.B} sectors took less than 10 ms but more than 3 ms."; + C = $"{results.C} sectors took less than 50 ms but more than 10 ms."; + D = $"{results.D} sectors took less than 150 ms but more than 50 ms."; + E = $"{results.E} sectors took less than 500 ms but more than 150 ms."; + F = $"{results.F} sectors took more than 500 ms."; + UnreadableSectors = $"{results.UnreadableSectors.Count} sectors could not be read."; }); - async void UpdateProgress(string text, long current, long maximum) => - await Dispatcher.UIThread.InvokeAsync(() => + // TODO: Show list of unreadable sectors + /* + if(results.UnreadableSectors.Count > 0) + foreach(ulong bad in results.UnreadableSectors) + string.Format("Sector {0} could not be read", bad); +*/ + + // TODO: Show results + /* + + if(results.SeekTotal != 0 || results.SeekMin != double.MaxValue || results.SeekMax != double.MinValue) + + string.Format("Testing {0} seeks, longest seek took {1:F3} ms, fastest one took {2:F3} ms. ({3:F3} ms average)", + results.SeekTimes, results.SeekMax, results.SeekMin, results.SeekTotal / 1000); + */ + + Statistics.AddCommand("media-scan"); + + dev.Close(); + WorkFinished(); + } + + async void ScanSpeed(ulong sector, double currentSpeed) => await Dispatcher.UIThread.InvokeAsync(() => + { + if(ChartPoints.Count == 0) + ChartPoints.Add(new DataPoint(0, currentSpeed)); + + ChartPoints.Add(new DataPoint(sector, currentSpeed)); + + if(currentSpeed > MaxY) + MaxY = currentSpeed + (currentSpeed / 10d); + }); + + async void InitBlockMap(ulong blocks, ulong blockSize, ulong blocksToRead, ushort currentProfile) => + await Dispatcher.UIThread.InvokeAsync(() => + { + Blocks = blocks / blocksToRead; + _blocksToRead = blocksToRead; + + MinX = 0; + MinY = 0; + + switch(currentProfile) { - ProgressText = text; - ProgressIndeterminate = false; + case 0x0005: // CD and DDCD + case 0x0008: + case 0x0009: + case 0x000A: + case 0x0020: + case 0x0021: + case 0x0022: + if(blocks <= 360000) + MaxX = 360000; + else if(blocks <= 405000) + MaxX = 405000; + else if(blocks <= 445500) + MaxX = 445500; + else + MaxX = blocks; - ProgressMaxValue = maximum; - ProgressValue = current; - }); + StepsX = MaxX / 10; + StepsY = 150 * 4; + MaxY = StepsY * 12.5; - async void InitProgress() => await Dispatcher.UIThread.InvokeAsync(() => - { - Progress1Visible = true; + break; + case 0x0010: // DVD SL + case 0x0011: + case 0x0012: + case 0x0013: + case 0x0014: + case 0x0018: + case 0x001A: + case 0x001B: + MaxX = 2298496; + StepsX = MaxX / 10; + StepsY = 1352.5; + MaxY = StepsY * 18; + + break; + case 0x0015: // DVD DL + case 0x0016: + case 0x0017: + case 0x002A: + case 0x002B: + MaxX = 4173824; + StepsX = MaxX / 10; + StepsY = 1352.5; + MaxY = StepsY * 18; + + break; + case 0x0041: + case 0x0042: + case 0x0043: + case 0x0040: // BD + if(blocks <= 12219392) + MaxX = 12219392; + else if(blocks <= 24438784) + MaxX = 24438784; + else if(blocks <= 48878592) + MaxX = 48878592; + else if(blocks <= 62500864) + MaxX = 62500864; + else + MaxX = blocks; + + StepsX = MaxX / 10; + StepsY = 4394.5; + MaxY = StepsY * 18; + + break; + case 0x0050: // HD DVD + case 0x0051: + case 0x0052: + case 0x0053: + case 0x0058: + case 0x005A: + if(blocks <= 7361599) + MaxX = 7361599; + else if(blocks <= 16305407) + MaxX = 16305407; + else + MaxX = blocks; + + StepsX = MaxX / 10; + StepsY = 4394.5; + MaxY = StepsY * 8; + + break; + default: + MaxX = blocks; + StepsX = MaxX / 10; + StepsY = 625; + MaxY = StepsY; + + break; + } }); - async void PulseProgress(string text) => await Dispatcher.UIThread.InvokeAsync(() => + async void WorkFinished() => await Dispatcher.UIThread.InvokeAsync(() => + { + StopVisible = false; + StartVisible = true; + CloseVisible = true; + ProgressVisible = false; + }); + + async void EndProgress() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress1Visible = false; + }); + + async void UpdateProgress(string text, long current, long maximum) => + await Dispatcher.UIThread.InvokeAsync(() => { ProgressText = text; - ProgressIndeterminate = true; + ProgressIndeterminate = false; + + ProgressMaxValue = maximum; + ProgressValue = current; }); - async void StoppingErrorMessage(string text) => await Dispatcher.UIThread.InvokeAsync(action: async () => - { - ProgressText = text; + async void InitProgress() => await Dispatcher.UIThread.InvokeAsync(() => + { + Progress1Visible = true; + }); - await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"{text}", ButtonEnum.Ok, Icon.Error). - ShowDialog(_view); + async void PulseProgress(string text) => await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = text; + ProgressIndeterminate = true; + }); - WorkFinished(); - }); + async void StoppingErrorMessage(string text) => await Dispatcher.UIThread.InvokeAsync(action: async () => + { + ProgressText = text; - async void UpdateStatus(string text) => await Dispatcher.UIThread.InvokeAsync(() => - { - ProgressText = text; - }); + await MessageBoxManager.GetMessageBoxStandardWindow("Error", $"{text}", ButtonEnum.Ok, Icon.Error). + ShowDialog(_view); - async void OnScanUnreadable(ulong sector) => await Dispatcher.UIThread.InvokeAsync(() => - { - _localResults.Errored += _blocksToRead; - UnreadableSectors = $"{_localResults.Errored} sectors could not be read."; - BlockMapList.Add((sector / _blocksToRead, double.NaN)); - }); + WorkFinished(); + }); - async void OnScanTime(ulong sector, double duration) => await Dispatcher.UIThread.InvokeAsync(() => - { - BlockMapList.Add((sector / _blocksToRead, duration)); + async void UpdateStatus(string text) => await Dispatcher.UIThread.InvokeAsync(() => + { + ProgressText = text; + }); - if(duration < 3) - _localResults.A += _blocksToRead; - else if(duration >= 3 && - duration < 10) - _localResults.B += _blocksToRead; - else if(duration >= 10 && - duration < 50) - _localResults.C += _blocksToRead; - else if(duration >= 50 && - duration < 150) - _localResults.D += _blocksToRead; - else if(duration >= 150 && - duration < 500) - _localResults.E += _blocksToRead; - else if(duration >= 500) - _localResults.F += _blocksToRead; + async void OnScanUnreadable(ulong sector) => await Dispatcher.UIThread.InvokeAsync(() => + { + _localResults.Errored += _blocksToRead; + UnreadableSectors = $"{_localResults.Errored} sectors could not be read."; + BlockMapList.Add((sector / _blocksToRead, double.NaN)); + }); - A = $"{_localResults.A} sectors took less than 3 ms."; - B = $"{_localResults.B} sectors took less than 10 ms but more than 3 ms."; - C = $"{_localResults.C} sectors took less than 50 ms but more than 10 ms."; - D = $"{_localResults.D} sectors took less than 150 ms but more than 50 ms."; - E = $"{_localResults.E} sectors took less than 500 ms but more than 150 ms."; - F = $"{_localResults.F} sectors took more than 500 ms."; - }); - } + async void OnScanTime(ulong sector, double duration) => await Dispatcher.UIThread.InvokeAsync(() => + { + BlockMapList.Add((sector / _blocksToRead, duration)); + + if(duration < 3) + _localResults.A += _blocksToRead; + else if(duration >= 3 && + duration < 10) + _localResults.B += _blocksToRead; + else if(duration >= 10 && + duration < 50) + _localResults.C += _blocksToRead; + else if(duration >= 50 && + duration < 150) + _localResults.D += _blocksToRead; + else if(duration >= 150 && + duration < 500) + _localResults.E += _blocksToRead; + else if(duration >= 500) + _localResults.F += _blocksToRead; + + A = $"{_localResults.A} sectors took less than 3 ms."; + B = $"{_localResults.B} sectors took less than 10 ms but more than 3 ms."; + C = $"{_localResults.C} sectors took less than 50 ms but more than 10 ms."; + D = $"{_localResults.D} sectors took less than 150 ms but more than 50 ms."; + E = $"{_localResults.E} sectors took less than 500 ms but more than 150 ms."; + F = $"{_localResults.F} sectors took more than 500 ms."; + }); } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/SplashWindowViewModel.cs b/Aaru.Gui/ViewModels/Windows/SplashWindowViewModel.cs index 4d3f63973..486530694 100644 --- a/Aaru.Gui/ViewModels/Windows/SplashWindowViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/SplashWindowViewModel.cs @@ -46,250 +46,249 @@ using Avalonia.Threading; using Microsoft.EntityFrameworkCore; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class SplashWindowViewModel : ViewModelBase { - public sealed class SplashWindowViewModel : ViewModelBase + readonly SplashWindow _view; + double _currentProgress; + double _maxProgress; + string _message; + + public SplashWindowViewModel(SplashWindow view) => _view = view; + + public string Message { - readonly SplashWindow _view; - double _currentProgress; - double _maxProgress; - string _message; + get => _message; + set => this.RaiseAndSetIfChanged(ref _message, value); + } - public SplashWindowViewModel(SplashWindow view) => _view = view; + public double MaxProgress + { + get => _maxProgress; + set => this.RaiseAndSetIfChanged(ref _maxProgress, value); + } - public string Message + public double CurrentProgress + { + get => _currentProgress; + set => this.RaiseAndSetIfChanged(ref _currentProgress, value); + } + + internal void OnOpened() + { + Message = "Welcome to Aaru!"; + MaxProgress = 9; + CurrentProgress = 0; + + Dispatcher.UIThread.Post(InitializeConsole); + } + + void InitializeConsole() + { + CurrentProgress++; + Message = "Initializing console..."; + + Task.Run(() => { - get => _message; - set => this.RaiseAndSetIfChanged(ref _message, value); - } + ConsoleHandler.Init(); + AaruConsole.WriteLine("Aaru started!"); - public double MaxProgress + Dispatcher.UIThread.Post(LoadSettings); + }); + } + + void LoadSettings() + { + CurrentProgress++; + Message = "Loading settings..."; + AaruConsole.WriteLine("Loading settings..."); + + Task.Run(() => { - get => _maxProgress; - set => this.RaiseAndSetIfChanged(ref _maxProgress, value); - } + // TODO: Detect there are no settings yet + Settings.Settings.LoadSettings(); - public double CurrentProgress + Dispatcher.UIThread.Post(MigrateLocalDatabase); + }); + } + + void MigrateLocalDatabase() + { + CurrentProgress++; + Message = "Migrating local database..."; + AaruConsole.WriteLine("Migrating local database..."); + + Task.Run(() => { - get => _currentProgress; - set => this.RaiseAndSetIfChanged(ref _currentProgress, value); - } + AaruContext ctx = null; - internal void OnOpened() - { - Message = "Welcome to Aaru!"; - MaxProgress = 9; - CurrentProgress = 0; - - Dispatcher.UIThread.Post(InitializeConsole); - } - - void InitializeConsole() - { - CurrentProgress++; - Message = "Initializing console..."; - - Task.Run(() => + try { - ConsoleHandler.Init(); - AaruConsole.WriteLine("Aaru started!"); - - Dispatcher.UIThread.Post(LoadSettings); - }); - } - - void LoadSettings() - { - CurrentProgress++; - Message = "Loading settings..."; - AaruConsole.WriteLine("Loading settings..."); - - Task.Run(() => + ctx = AaruContext.Create(Settings.Settings.LocalDbPath, false); + ctx.Database.Migrate(); + } + catch(NotSupportedException) { - // TODO: Detect there are no settings yet - Settings.Settings.LoadSettings(); + try + { + ctx?.Database.CloseConnection(); + ctx?.Dispose(); + } + catch(Exception) + { + // Should not ever arrive here, but if it does, keep trying to replace it anyway + } - Dispatcher.UIThread.Post(MigrateLocalDatabase); - }); - } + File.Delete(Settings.Settings.LocalDbPath); + ctx = AaruContext.Create(Settings.Settings.LocalDbPath); + ctx.Database.EnsureCreated(); - void MigrateLocalDatabase() + ctx.Database. + ExecuteSqlRaw("CREATE TABLE IF NOT EXISTS \"__EFMigrationsHistory\" (\"MigrationId\" TEXT PRIMARY KEY, \"ProductVersion\" TEXT)"); + + foreach(string migration in ctx.Database.GetPendingMigrations()) + { + ctx.Database. + ExecuteSqlRaw($"INSERT INTO \"__EFMigrationsHistory\" (MigrationId, ProductVersion) VALUES ('{migration}', '0.0.0')"); + } + + ctx.SaveChanges(); + } + + // Remove duplicates + foreach(var duplicate in ctx.SeenDevices.AsEnumerable()!.GroupBy(a => new + { + a.Manufacturer, + a.Model, + a.Revision, + a.Bus + }).Where(a => a.Count() > 1).Distinct().Select(a => a.Key)) + ctx.RemoveRange(ctx.SeenDevices!. + Where(d => d.Manufacturer == duplicate.Manufacturer && + d.Model == duplicate.Model && d.Revision == duplicate.Revision && + d.Bus == duplicate.Bus).Skip(1)); + + // Remove nulls + ctx.RemoveRange(ctx.SeenDevices!.Where(d => d.Manufacturer == null && d.Model == null && + d.Revision == null)); + + ctx.SaveChanges(); + + Dispatcher.UIThread.Post(UpdateMainDatabase); + }); + } + + void UpdateMainDatabase() + { + CurrentProgress++; + Message = "Updating main database..."; + AaruConsole.WriteLine("Updating main database..."); + + Task.Run(() => { - CurrentProgress++; - Message = "Migrating local database..."; - AaruConsole.WriteLine("Migrating local database..."); + bool mainDbUpdate = false; - Task.Run(() => + if(!File.Exists(Settings.Settings.MainDbPath)) { - AaruContext ctx = null; + mainDbUpdate = true; + + // TODO: Update database + } + + var mainContext = AaruContext.Create(Settings.Settings.MainDbPath, false); + + if(mainContext.Database.GetPendingMigrations().Any()) + { + AaruConsole.WriteLine("New database version, updating..."); try { - ctx = AaruContext.Create(Settings.Settings.LocalDbPath, false); - ctx.Database.Migrate(); + File.Delete(Settings.Settings.MainDbPath); } - catch(NotSupportedException) + catch(Exception) { - try - { - ctx?.Database.CloseConnection(); - ctx?.Dispose(); - } - catch(Exception) - { - // Should not ever arrive here, but if it does, keep trying to replace it anyway - } + AaruConsole. + ErrorWriteLine("Exception trying to remove old database version, cannot continue..."); - File.Delete(Settings.Settings.LocalDbPath); - ctx = AaruContext.Create(Settings.Settings.LocalDbPath); - ctx.Database.EnsureCreated(); + AaruConsole.ErrorWriteLine("Please manually remove file at {0}", Settings.Settings.MainDbPath); - ctx.Database. - ExecuteSqlRaw("CREATE TABLE IF NOT EXISTS \"__EFMigrationsHistory\" (\"MigrationId\" TEXT PRIMARY KEY, \"ProductVersion\" TEXT)"); - - foreach(string migration in ctx.Database.GetPendingMigrations()) - { - ctx.Database. - ExecuteSqlRaw($"INSERT INTO \"__EFMigrationsHistory\" (MigrationId, ProductVersion) VALUES ('{migration}', '0.0.0')"); - } - - ctx.SaveChanges(); + return; } - // Remove duplicates - foreach(var duplicate in ctx.SeenDevices.AsEnumerable()!.GroupBy(a => new - { - a.Manufacturer, - a.Model, - a.Revision, - a.Bus - }).Where(a => a.Count() > 1).Distinct().Select(a => a.Key)) - ctx.RemoveRange(ctx.SeenDevices!. - Where(d => d.Manufacturer == duplicate.Manufacturer && - d.Model == duplicate.Model && d.Revision == duplicate.Revision && - d.Bus == duplicate.Bus).Skip(1)); - - // Remove nulls - ctx.RemoveRange(ctx.SeenDevices!.Where(d => d.Manufacturer == null && d.Model == null && - d.Revision == null)); - - ctx.SaveChanges(); - - Dispatcher.UIThread.Post(UpdateMainDatabase); - }); - } - - void UpdateMainDatabase() - { - CurrentProgress++; - Message = "Updating main database..."; - AaruConsole.WriteLine("Updating main database..."); - - Task.Run(() => - { - bool mainDbUpdate = false; - - if(!File.Exists(Settings.Settings.MainDbPath)) - { - mainDbUpdate = true; - - // TODO: Update database - } - - var mainContext = AaruContext.Create(Settings.Settings.MainDbPath, false); - - if(mainContext.Database.GetPendingMigrations().Any()) - { - AaruConsole.WriteLine("New database version, updating..."); - - try - { - File.Delete(Settings.Settings.MainDbPath); - } - catch(Exception) - { - AaruConsole. - ErrorWriteLine("Exception trying to remove old database version, cannot continue..."); - - AaruConsole.ErrorWriteLine("Please manually remove file at {0}", Settings.Settings.MainDbPath); - - return; - } - - // TODO: Update database - } - - Dispatcher.UIThread.Post(CheckGdprCompliance); - }); - } - - async void CheckGdprCompliance() - { - CurrentProgress++; - Message = "Checking GDPR compliance..."; - AaruConsole.WriteLine("Checking GDPR compliance..."); - - if(Settings.Settings.Current.GdprCompliance < DicSettings.GDPR_LEVEL) - { - var settingsDialog = new SettingsDialog(); - var settingsDialogViewModel = new SettingsViewModel(settingsDialog, true); - settingsDialog.DataContext = settingsDialogViewModel; - await settingsDialog.ShowDialog(_view); + // TODO: Update database } - LoadStatistics(); - } - - void LoadStatistics() - { - CurrentProgress++; - Message = "Loading statistics..."; - AaruConsole.WriteLine("Loading statistics..."); - - Task.Run(() => - { - Statistics.LoadStats(); - - Dispatcher.UIThread.Post(RegisterEncodings); - }); - } - - void RegisterEncodings() - { - CurrentProgress++; - Message = "Registering encodings..."; - AaruConsole.WriteLine("Registering encodings..."); - - Task.Run(() => - { - Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); - - Dispatcher.UIThread.Post(SaveStatistics); - }); - } - - void SaveStatistics() - { - CurrentProgress++; - Message = "Saving statistics..."; - AaruConsole.WriteLine("Saving statistics..."); - - Task.Run(() => - { - Statistics.SaveStats(); - - Dispatcher.UIThread.Post(LoadMainWindow); - }); - } - - void LoadMainWindow() - { - CurrentProgress++; - Message = "Loading main window..."; - AaruConsole.WriteLine("Loading main window..."); - WorkFinished?.Invoke(this, EventArgs.Empty); - } - - internal event EventHandler WorkFinished; + Dispatcher.UIThread.Post(CheckGdprCompliance); + }); } + + async void CheckGdprCompliance() + { + CurrentProgress++; + Message = "Checking GDPR compliance..."; + AaruConsole.WriteLine("Checking GDPR compliance..."); + + if(Settings.Settings.Current.GdprCompliance < DicSettings.GDPR_LEVEL) + { + var settingsDialog = new SettingsDialog(); + var settingsDialogViewModel = new SettingsViewModel(settingsDialog, true); + settingsDialog.DataContext = settingsDialogViewModel; + await settingsDialog.ShowDialog(_view); + } + + LoadStatistics(); + } + + void LoadStatistics() + { + CurrentProgress++; + Message = "Loading statistics..."; + AaruConsole.WriteLine("Loading statistics..."); + + Task.Run(() => + { + Statistics.LoadStats(); + + Dispatcher.UIThread.Post(RegisterEncodings); + }); + } + + void RegisterEncodings() + { + CurrentProgress++; + Message = "Registering encodings..."; + AaruConsole.WriteLine("Registering encodings..."); + + Task.Run(() => + { + Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); + + Dispatcher.UIThread.Post(SaveStatistics); + }); + } + + void SaveStatistics() + { + CurrentProgress++; + Message = "Saving statistics..."; + AaruConsole.WriteLine("Saving statistics..."); + + Task.Run(() => + { + Statistics.SaveStats(); + + Dispatcher.UIThread.Post(LoadMainWindow); + }); + } + + void LoadMainWindow() + { + CurrentProgress++; + Message = "Loading main window..."; + AaruConsole.WriteLine("Loading main window..."); + WorkFinished?.Invoke(this, EventArgs.Empty); + } + + internal event EventHandler WorkFinished; } \ No newline at end of file diff --git a/Aaru.Gui/ViewModels/Windows/ViewSectorViewModel.cs b/Aaru.Gui/ViewModels/Windows/ViewSectorViewModel.cs index 27e045980..0aa0170ea 100644 --- a/Aaru.Gui/ViewModels/Windows/ViewSectorViewModel.cs +++ b/Aaru.Gui/ViewModels/Windows/ViewSectorViewModel.cs @@ -36,72 +36,71 @@ using Aaru.Helpers; using JetBrains.Annotations; using ReactiveUI; -namespace Aaru.Gui.ViewModels.Windows +namespace Aaru.Gui.ViewModels.Windows; + +public sealed class ViewSectorViewModel : ViewModelBase { - public sealed class ViewSectorViewModel : ViewModelBase + const int HEX_COLUMNS = 32; + readonly IMediaImage _inputFormat; + bool _longSectorChecked; + bool _longSectorVisible; + string _printHexText; + double _sectorNumber; + string _title; + string _totalSectorsText; + + public ViewSectorViewModel([NotNull] IMediaImage inputFormat) { - const int HEX_COLUMNS = 32; - readonly IMediaImage _inputFormat; - bool _longSectorChecked; - bool _longSectorVisible; - string _printHexText; - double _sectorNumber; - string _title; - string _totalSectorsText; + _inputFormat = inputFormat; - public ViewSectorViewModel([NotNull] IMediaImage inputFormat) + ErrorNumber errno = inputFormat.ReadSectorLong(0, out _); + + if(errno == ErrorNumber.NoError) + LongSectorChecked = true; + else + LongSectorVisible = false; + + TotalSectorsText = $"of {inputFormat.Info.Sectors}"; + SectorNumber = 0; + } + + public string Title + { + get => _title; + set => this.RaiseAndSetIfChanged(ref _title, value); + } + + public double SectorNumber + { + get => _sectorNumber; + set { - _inputFormat = inputFormat; + this.RaiseAndSetIfChanged(ref _sectorNumber, value); - ErrorNumber errno = inputFormat.ReadSectorLong(0, out _); + byte[] sector; + ErrorNumber errno; + + errno = LongSectorChecked ? _inputFormat.ReadSectorLong((ulong)SectorNumber, out sector) + : _inputFormat.ReadSector((ulong)SectorNumber, out sector); if(errno == ErrorNumber.NoError) - LongSectorChecked = true; - else - LongSectorVisible = false; - - TotalSectorsText = $"of {inputFormat.Info.Sectors}"; - SectorNumber = 0; - } - - public string Title - { - get => _title; - set => this.RaiseAndSetIfChanged(ref _title, value); - } - - public double SectorNumber - { - get => _sectorNumber; - set - { - this.RaiseAndSetIfChanged(ref _sectorNumber, value); - - byte[] sector; - ErrorNumber errno; - - errno = LongSectorChecked ? _inputFormat.ReadSectorLong((ulong)SectorNumber, out sector) - : _inputFormat.ReadSector((ulong)SectorNumber, out sector); - - if(errno == ErrorNumber.NoError) - PrintHexText = PrintHex.ByteArrayToHexArrayString(sector, HEX_COLUMNS); - } - } - - public string TotalSectorsText { get; } - - public bool LongSectorChecked - { - get => _longSectorChecked; - set => this.RaiseAndSetIfChanged(ref _longSectorChecked, value); - } - - public bool LongSectorVisible { get; } - - public string PrintHexText - { - get => _printHexText; - set => this.RaiseAndSetIfChanged(ref _printHexText, value); + PrintHexText = PrintHex.ByteArrayToHexArrayString(sector, HEX_COLUMNS); } } + + public string TotalSectorsText { get; } + + public bool LongSectorChecked + { + get => _longSectorChecked; + set => this.RaiseAndSetIfChanged(ref _longSectorChecked, value); + } + + public bool LongSectorVisible { get; } + + public string PrintHexText + { + get => _printHexText; + set => this.RaiseAndSetIfChanged(ref _printHexText, value); + } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Dialogs/About.xaml.cs b/Aaru.Gui/Views/Dialogs/About.xaml.cs index 341e11420..550698034 100644 --- a/Aaru.Gui/Views/Dialogs/About.xaml.cs +++ b/Aaru.Gui/Views/Dialogs/About.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Dialogs -{ - public sealed class About : Window - { - public About() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Dialogs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class About : Window +{ + public About() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Dialogs/Console.xaml.cs b/Aaru.Gui/Views/Dialogs/Console.xaml.cs index 2d0acea15..f4a9bc60d 100644 --- a/Aaru.Gui/Views/Dialogs/Console.xaml.cs +++ b/Aaru.Gui/Views/Dialogs/Console.xaml.cs @@ -36,26 +36,25 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; using JetBrains.Annotations; -namespace Aaru.Gui.Views.Dialogs +namespace Aaru.Gui.Views.Dialogs; + +public sealed class Console : Window { - public sealed class Console : Window + public Console() { - public Console() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnClosing([NotNull] CancelEventArgs e) - { - e.Cancel = true; - Hide(); + protected override void OnClosing([NotNull] CancelEventArgs e) + { + e.Cancel = true; + Hide(); - base.OnClosing(e); - } + base.OnClosing(e); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Dialogs/Encodings.xaml.cs b/Aaru.Gui/Views/Dialogs/Encodings.xaml.cs index 9ceca3010..64ec09a1d 100644 --- a/Aaru.Gui/Views/Dialogs/Encodings.xaml.cs +++ b/Aaru.Gui/Views/Dialogs/Encodings.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Dialogs -{ - public sealed class Encodings : Window - { - public Encodings() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Dialogs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class Encodings : Window +{ + public Encodings() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Dialogs/LicenseDialog.xaml.cs b/Aaru.Gui/Views/Dialogs/LicenseDialog.xaml.cs index 9ae523edd..f4d0f0412 100644 --- a/Aaru.Gui/Views/Dialogs/LicenseDialog.xaml.cs +++ b/Aaru.Gui/Views/Dialogs/LicenseDialog.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Dialogs -{ - public sealed class LicenseDialog : Window - { - public LicenseDialog() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Dialogs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class LicenseDialog : Window +{ + public LicenseDialog() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Dialogs/PluginsDialog.xaml.cs b/Aaru.Gui/Views/Dialogs/PluginsDialog.xaml.cs index e03c24848..6d20dd0cb 100644 --- a/Aaru.Gui/Views/Dialogs/PluginsDialog.xaml.cs +++ b/Aaru.Gui/Views/Dialogs/PluginsDialog.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Dialogs -{ - public sealed class PluginsDialog : Window - { - public PluginsDialog() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Dialogs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class PluginsDialog : Window +{ + public PluginsDialog() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Dialogs/SettingsDialog.xaml.cs b/Aaru.Gui/Views/Dialogs/SettingsDialog.xaml.cs index f3d966c3e..f640a9a8c 100644 --- a/Aaru.Gui/Views/Dialogs/SettingsDialog.xaml.cs +++ b/Aaru.Gui/Views/Dialogs/SettingsDialog.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Dialogs -{ - public sealed class SettingsDialog : Window - { - public SettingsDialog() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Dialogs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class SettingsDialog : Window +{ + public SettingsDialog() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Dialogs/StatisticsDialog.xaml.cs b/Aaru.Gui/Views/Dialogs/StatisticsDialog.xaml.cs index e6635e129..5d615ceee 100644 --- a/Aaru.Gui/Views/Dialogs/StatisticsDialog.xaml.cs +++ b/Aaru.Gui/Views/Dialogs/StatisticsDialog.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Dialogs -{ - public sealed class StatisticsDialog : Window - { - public StatisticsDialog() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Dialogs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class StatisticsDialog : Window +{ + public StatisticsDialog() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Panels/DeviceInfo.xaml.cs b/Aaru.Gui/Views/Panels/DeviceInfo.xaml.cs index 19a41c33d..ae68e6823 100644 --- a/Aaru.Gui/Views/Panels/DeviceInfo.xaml.cs +++ b/Aaru.Gui/Views/Panels/DeviceInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Panels -{ - public sealed class DeviceInfo : UserControl - { - public DeviceInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Panels; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class DeviceInfo : UserControl +{ + public DeviceInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Panels/FileSystem.xaml.cs b/Aaru.Gui/Views/Panels/FileSystem.xaml.cs index b8bb9d885..e6afd84a1 100644 --- a/Aaru.Gui/Views/Panels/FileSystem.xaml.cs +++ b/Aaru.Gui/Views/Panels/FileSystem.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Panels -{ - public sealed class FileSystem : UserControl - { - public FileSystem() => InitializeComponent(); +namespace Aaru.Gui.Views.Panels; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class FileSystem : UserControl +{ + public FileSystem() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Panels/ImageInfo.xaml.cs b/Aaru.Gui/Views/Panels/ImageInfo.xaml.cs index 0892f0dbc..45a9745a2 100644 --- a/Aaru.Gui/Views/Panels/ImageInfo.xaml.cs +++ b/Aaru.Gui/Views/Panels/ImageInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Panels -{ - public sealed class ImageInfo : UserControl - { - public ImageInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Panels; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class ImageInfo : UserControl +{ + public ImageInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Panels/MediaInfo.xaml.cs b/Aaru.Gui/Views/Panels/MediaInfo.xaml.cs index 6bcb40048..cea7e9527 100644 --- a/Aaru.Gui/Views/Panels/MediaInfo.xaml.cs +++ b/Aaru.Gui/Views/Panels/MediaInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Panels -{ - public sealed class MediaInfo : UserControl - { - public MediaInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Panels; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class MediaInfo : UserControl +{ + public MediaInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Panels/Partition.xaml.cs b/Aaru.Gui/Views/Panels/Partition.xaml.cs index db3c24fd6..988d4f69d 100644 --- a/Aaru.Gui/Views/Panels/Partition.xaml.cs +++ b/Aaru.Gui/Views/Panels/Partition.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Panels -{ - public sealed class Partition : UserControl - { - public Partition() => InitializeComponent(); +namespace Aaru.Gui.Views.Panels; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class Partition : UserControl +{ + public Partition() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Panels/Subdirectory.xaml.cs b/Aaru.Gui/Views/Panels/Subdirectory.xaml.cs index d016b18c6..d501ff033 100644 --- a/Aaru.Gui/Views/Panels/Subdirectory.xaml.cs +++ b/Aaru.Gui/Views/Panels/Subdirectory.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Panels -{ - public sealed class Subdirectory : UserControl - { - public Subdirectory() => InitializeComponent(); +namespace Aaru.Gui.Views.Panels; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class Subdirectory : UserControl +{ + public Subdirectory() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/AtaInfo.xaml.cs b/Aaru.Gui/Views/Tabs/AtaInfo.xaml.cs index d0af0399b..88a86fd1e 100644 --- a/Aaru.Gui/Views/Tabs/AtaInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/AtaInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class AtaInfo : UserControl - { - public AtaInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class AtaInfo : UserControl +{ + public AtaInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/BlurayInfo.xaml.cs b/Aaru.Gui/Views/Tabs/BlurayInfo.xaml.cs index 761434bc6..9e8fbc0ff 100644 --- a/Aaru.Gui/Views/Tabs/BlurayInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/BlurayInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class BlurayInfo : UserControl - { - public BlurayInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class BlurayInfo : UserControl +{ + public BlurayInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/CompactDiscInfo.xaml.cs b/Aaru.Gui/Views/Tabs/CompactDiscInfo.xaml.cs index f1dc82197..67fe3ad42 100644 --- a/Aaru.Gui/Views/Tabs/CompactDiscInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/CompactDiscInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class CompactDiscInfo : UserControl - { - public CompactDiscInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class CompactDiscInfo : UserControl +{ + public CompactDiscInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/DvdInfo.xaml.cs b/Aaru.Gui/Views/Tabs/DvdInfo.xaml.cs index 30ddb2e59..02285efa8 100644 --- a/Aaru.Gui/Views/Tabs/DvdInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/DvdInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class DvdInfo : UserControl - { - public DvdInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class DvdInfo : UserControl +{ + public DvdInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/DvdWritableInfo.xaml.cs b/Aaru.Gui/Views/Tabs/DvdWritableInfo.xaml.cs index d1d4c552d..68e48f914 100644 --- a/Aaru.Gui/Views/Tabs/DvdWritableInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/DvdWritableInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class DvdWritableInfo : UserControl - { - public DvdWritableInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class DvdWritableInfo : UserControl +{ + public DvdWritableInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/PcmciaInfo.xaml.cs b/Aaru.Gui/Views/Tabs/PcmciaInfo.xaml.cs index 2924a748a..9b2f1e5a6 100644 --- a/Aaru.Gui/Views/Tabs/PcmciaInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/PcmciaInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class PcmciaInfo : UserControl - { - public PcmciaInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class PcmciaInfo : UserControl +{ + public PcmciaInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/ScsiInfo.xaml.cs b/Aaru.Gui/Views/Tabs/ScsiInfo.xaml.cs index 734357dfa..0ef594201 100644 --- a/Aaru.Gui/Views/Tabs/ScsiInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/ScsiInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class ScsiInfo : UserControl - { - public ScsiInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class ScsiInfo : UserControl +{ + public ScsiInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/SdMmcInfo.xaml.cs b/Aaru.Gui/Views/Tabs/SdMmcInfo.xaml.cs index da49c2317..ed31e5982 100644 --- a/Aaru.Gui/Views/Tabs/SdMmcInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/SdMmcInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class SdMmcInfo : UserControl - { - public SdMmcInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class SdMmcInfo : UserControl +{ + public SdMmcInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Tabs/XboxInfo.xaml.cs b/Aaru.Gui/Views/Tabs/XboxInfo.xaml.cs index d2e9b8698..4f028d965 100644 --- a/Aaru.Gui/Views/Tabs/XboxInfo.xaml.cs +++ b/Aaru.Gui/Views/Tabs/XboxInfo.xaml.cs @@ -33,12 +33,11 @@ using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Tabs -{ - public sealed class XboxInfo : UserControl - { - public XboxInfo() => InitializeComponent(); +namespace Aaru.Gui.Views.Tabs; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); - } +public sealed class XboxInfo : UserControl +{ + public XboxInfo() => InitializeComponent(); + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/DecodeMediaTags.xaml.cs b/Aaru.Gui/Views/Windows/DecodeMediaTags.xaml.cs index 6aee76c4c..3c95ab733 100644 --- a/Aaru.Gui/Views/Windows/DecodeMediaTags.xaml.cs +++ b/Aaru.Gui/Views/Windows/DecodeMediaTags.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows -{ - public sealed class DecodeMediaTags : Window - { - public DecodeMediaTags() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Windows; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class DecodeMediaTags : Window +{ + public DecodeMediaTags() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/ImageChecksum.xaml.cs b/Aaru.Gui/Views/Windows/ImageChecksum.xaml.cs index 50bbf7a23..0ce8f833f 100644 --- a/Aaru.Gui/Views/Windows/ImageChecksum.xaml.cs +++ b/Aaru.Gui/Views/Windows/ImageChecksum.xaml.cs @@ -36,24 +36,23 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows +namespace Aaru.Gui.Views.Windows; + +public sealed class ImageChecksum : Window { - public sealed class ImageChecksum : Window + public ImageChecksum() { - public ImageChecksum() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnClosing(CancelEventArgs e) - { - (DataContext as ImageChecksumViewModel)?.ExecuteStopCommand(); - base.OnClosing(e); - } + protected override void OnClosing(CancelEventArgs e) + { + (DataContext as ImageChecksumViewModel)?.ExecuteStopCommand(); + base.OnClosing(e); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/ImageConvert.xaml.cs b/Aaru.Gui/Views/Windows/ImageConvert.xaml.cs index bc80a6e67..4a2a32026 100644 --- a/Aaru.Gui/Views/Windows/ImageConvert.xaml.cs +++ b/Aaru.Gui/Views/Windows/ImageConvert.xaml.cs @@ -36,24 +36,23 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows +namespace Aaru.Gui.Views.Windows; + +public sealed class ImageConvert : Window { - public sealed class ImageConvert : Window + public ImageConvert() { - public ImageConvert() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnClosing(CancelEventArgs e) - { - (DataContext as ImageConvertViewModel)?.ExecuteStopCommand(); - base.OnClosing(e); - } + protected override void OnClosing(CancelEventArgs e) + { + (DataContext as ImageConvertViewModel)?.ExecuteStopCommand(); + base.OnClosing(e); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/ImageEntropy.xaml.cs b/Aaru.Gui/Views/Windows/ImageEntropy.xaml.cs index 881e10aee..0acfba0c5 100644 --- a/Aaru.Gui/Views/Windows/ImageEntropy.xaml.cs +++ b/Aaru.Gui/Views/Windows/ImageEntropy.xaml.cs @@ -36,24 +36,23 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows +namespace Aaru.Gui.Views.Windows; + +public sealed class ImageEntropy : Window { - public sealed class ImageEntropy : Window + public ImageEntropy() { - public ImageEntropy() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnClosing(CancelEventArgs e) - { - (DataContext as ImageEntropyViewModel)?.ExecuteStopCommand(); - base.OnClosing(e); - } + protected override void OnClosing(CancelEventArgs e) + { + (DataContext as ImageEntropyViewModel)?.ExecuteStopCommand(); + base.OnClosing(e); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/ImageSidecar.xaml.cs b/Aaru.Gui/Views/Windows/ImageSidecar.xaml.cs index 5b5a0eed5..7a239bf3e 100644 --- a/Aaru.Gui/Views/Windows/ImageSidecar.xaml.cs +++ b/Aaru.Gui/Views/Windows/ImageSidecar.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows -{ - public sealed class ImageSidecar : Window - { - public ImageSidecar() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Windows; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class ImageSidecar : Window +{ + public ImageSidecar() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/ImageVerify.xaml.cs b/Aaru.Gui/Views/Windows/ImageVerify.xaml.cs index e4caec494..b08bb196b 100644 --- a/Aaru.Gui/Views/Windows/ImageVerify.xaml.cs +++ b/Aaru.Gui/Views/Windows/ImageVerify.xaml.cs @@ -36,24 +36,23 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows +namespace Aaru.Gui.Views.Windows; + +public sealed class ImageVerify : Window { - public sealed class ImageVerify : Window + public ImageVerify() { - public ImageVerify() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnClosing(CancelEventArgs e) - { - (DataContext as ImageVerifyViewModel)?.ExecuteStopCommand(); - base.OnClosing(e); - } + protected override void OnClosing(CancelEventArgs e) + { + (DataContext as ImageVerifyViewModel)?.ExecuteStopCommand(); + base.OnClosing(e); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/MainWindow.xaml.cs b/Aaru.Gui/Views/Windows/MainWindow.xaml.cs index 8381e2d1f..65318cbeb 100644 --- a/Aaru.Gui/Views/Windows/MainWindow.xaml.cs +++ b/Aaru.Gui/Views/Windows/MainWindow.xaml.cs @@ -36,25 +36,24 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows +namespace Aaru.Gui.Views.Windows; + +public sealed class MainWindow : Window { - public sealed class MainWindow : Window + public MainWindow() { - public MainWindow() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); + protected override void OnOpened(EventArgs e) + { + base.OnOpened(e); - (DataContext as MainWindowViewModel)?.LoadComplete(); - } + (DataContext as MainWindowViewModel)?.LoadComplete(); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/MediaDump.xaml.cs b/Aaru.Gui/Views/Windows/MediaDump.xaml.cs index 0be58729c..b8474315c 100644 --- a/Aaru.Gui/Views/Windows/MediaDump.xaml.cs +++ b/Aaru.Gui/Views/Windows/MediaDump.xaml.cs @@ -36,24 +36,23 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows +namespace Aaru.Gui.Views.Windows; + +public sealed class MediaDump : Window { - public sealed class MediaDump : Window + public MediaDump() { - public MediaDump() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnClosing(CancelEventArgs e) - { - (DataContext as MediaDumpViewModel)?.ExecuteStopCommand(); - base.OnClosing(e); - } + protected override void OnClosing(CancelEventArgs e) + { + (DataContext as MediaDumpViewModel)?.ExecuteStopCommand(); + base.OnClosing(e); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/MediaScan.xaml.cs b/Aaru.Gui/Views/Windows/MediaScan.xaml.cs index f6dce0776..24bacdba9 100644 --- a/Aaru.Gui/Views/Windows/MediaScan.xaml.cs +++ b/Aaru.Gui/Views/Windows/MediaScan.xaml.cs @@ -36,24 +36,23 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows +namespace Aaru.Gui.Views.Windows; + +public sealed class MediaScan : Window { - public sealed class MediaScan : Window + public MediaScan() { - public MediaScan() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnClosing(CancelEventArgs e) - { - (DataContext as MediaScanViewModel)?.ExecuteStopCommand(); - base.OnClosing(e); - } + protected override void OnClosing(CancelEventArgs e) + { + (DataContext as MediaScanViewModel)?.ExecuteStopCommand(); + base.OnClosing(e); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/SplashWindow.xaml.cs b/Aaru.Gui/Views/Windows/SplashWindow.xaml.cs index 6d10b52f6..fd8b0d968 100644 --- a/Aaru.Gui/Views/Windows/SplashWindow.xaml.cs +++ b/Aaru.Gui/Views/Windows/SplashWindow.xaml.cs @@ -36,24 +36,23 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows +namespace Aaru.Gui.Views.Windows; + +public sealed class SplashWindow : Window { - public sealed class SplashWindow : Window + public SplashWindow() { - public SplashWindow() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif + } - void InitializeComponent() => AvaloniaXamlLoader.Load(this); + void InitializeComponent() => AvaloniaXamlLoader.Load(this); - protected override void OnOpened(EventArgs e) - { - base.OnOpened(e); - (DataContext as SplashWindowViewModel)?.OnOpened(); - } + protected override void OnOpened(EventArgs e) + { + base.OnOpened(e); + (DataContext as SplashWindowViewModel)?.OnOpened(); } } \ No newline at end of file diff --git a/Aaru.Gui/Views/Windows/ViewSector.xaml.cs b/Aaru.Gui/Views/Windows/ViewSector.xaml.cs index 515cbb41b..228275c14 100644 --- a/Aaru.Gui/Views/Windows/ViewSector.xaml.cs +++ b/Aaru.Gui/Views/Windows/ViewSector.xaml.cs @@ -34,18 +34,17 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Markup.Xaml; -namespace Aaru.Gui.Views.Windows -{ - public sealed class ViewSector : Window - { - public ViewSector() - { - InitializeComponent(); - #if DEBUG - this.AttachDevTools(); - #endif - } +namespace Aaru.Gui.Views.Windows; - void InitializeComponent() => AvaloniaXamlLoader.Load(this); +public sealed class ViewSector : Window +{ + public ViewSector() + { + InitializeComponent(); + #if DEBUG + this.AttachDevTools(); + #endif } + + void InitializeComponent() => AvaloniaXamlLoader.Load(this); } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/AaruFormat.cs b/Aaru.Images/AaruFormat/AaruFormat.cs index c2225fcea..979a61e4d 100644 --- a/Aaru.Images/AaruFormat/AaruFormat.cs +++ b/Aaru.Images/AaruFormat/AaruFormat.cs @@ -78,111 +78,110 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; -namespace Aaru.DiscImages -{ - /// Implements reading and writing AaruFormat media images - public sealed partial class AaruFormat : IWritableOpticalImage, IVerifiableImage, IWritableTapeImage - { - bool _alreadyWrittenZero; - /// Cache of uncompressed blocks. - Dictionary _blockCache; - /// Cache of block headers. - Dictionary _blockHeaderCache; - /// Provides checksum for deduplication of sectors. - SHA256 _checksumProvider; - bool _compress; - byte[] _compressedBuffer; - CompressionType _compressionAlgorithm; - /// Provides CRC64. - Crc64Context _crc64; - /// Header of the currently writing block. - BlockHeader _currentBlockHeader; - /// Sector offset of writing position in currently writing block. - uint _currentBlockOffset; - /// Current size in bytes of the block cache - uint _currentCacheSize; - /// Cache of DDT entries. - Dictionary _ddtEntryCache; - bool _deduplicate; - /// On-memory deduplication table indexed by checksum. - Dictionary _deduplicationTable; - /// Dictionary size for compression algorithms - uint _dictionarySize; - /// Block with logical geometry. - GeometryBlock _geometryBlock; - /// Image header. - AaruHeader _header; - /// Image information. - ImageInfo _imageInfo; - /// Image data stream. - Stream _imageStream; - /// Index. - List _index; - /// If set to true, the DDT entries are in-memory. - bool _inMemoryDdt; - ulong _lastWrittenBlock; - Md5Context _md5Provider; - /// Cache of media tags. - Dictionary _mediaTags; - byte[] _mode2Subheaders; - /// If DDT is on-disk, this is the image stream offset at which it starts. - long _outMemoryDdtPosition; - bool _rewinded; - byte[] _sectorCpiMai; - byte[] _sectorDecryptedTitleKey; - /// Cache for data that prefixes the user data on a sector (e.g. sync). - byte[] _sectorPrefix; - uint[] _sectorPrefixDdt; - MemoryStream _sectorPrefixMs; - /// Cache for data that goes side by side with user data (e.g. CompactDisc subchannel). - byte[] _sectorSubchannel; - /// Cache for data that suffixes the user data on a sector (e.g. edc, ecc). - byte[] _sectorSuffix; - uint[] _sectorSuffixDdt; - MemoryStream _sectorSuffixMs; - Sha1Context _sha1Provider; - Sha256Context _sha256Provider; - /// Shift for calculating number of sectors in a block. - byte _shift; - SpamSumContext _spamsumProvider; - /// Cache for bytes to write/rad on-disk. - byte[] _structureBytes; - /// Cache for pointer for marshaling structures. - IntPtr _structurePointer; - Dictionary _tapeDdt; - /// Cache of CompactDisc track's flags - Dictionary _trackFlags; - /// Cache of CompactDisc track's ISRC - Dictionary _trackIsrcs; - /// In-memory deduplication table - ulong[] _userDataDdt; - byte[] _writingBuffer; - int _writingBufferPosition; - bool _writingLong; - ulong _writtenSectors; +namespace Aaru.DiscImages; - public AaruFormat() => _imageInfo = new ImageInfo - { - ReadableSectorTags = new List(), - ReadableMediaTags = new List(), - HasPartitions = false, - HasSessions = false, - Version = null, - Application = "Aaru", - ApplicationVersion = null, - Creator = null, - Comments = null, - MediaManufacturer = null, - MediaModel = null, - MediaSerialNumber = null, - MediaBarcode = null, - MediaPartNumber = null, - MediaSequence = 0, - LastMediaSequence = 0, - DriveManufacturer = null, - DriveModel = null, - DriveSerialNumber = null, - DriveFirmwareRevision = null - }; - } +/// Implements reading and writing AaruFormat media images +public sealed partial class AaruFormat : IWritableOpticalImage, IVerifiableImage, IWritableTapeImage +{ + bool _alreadyWrittenZero; + /// Cache of uncompressed blocks. + Dictionary _blockCache; + /// Cache of block headers. + Dictionary _blockHeaderCache; + /// Provides checksum for deduplication of sectors. + SHA256 _checksumProvider; + bool _compress; + byte[] _compressedBuffer; + CompressionType _compressionAlgorithm; + /// Provides CRC64. + Crc64Context _crc64; + /// Header of the currently writing block. + BlockHeader _currentBlockHeader; + /// Sector offset of writing position in currently writing block. + uint _currentBlockOffset; + /// Current size in bytes of the block cache + uint _currentCacheSize; + /// Cache of DDT entries. + Dictionary _ddtEntryCache; + bool _deduplicate; + /// On-memory deduplication table indexed by checksum. + Dictionary _deduplicationTable; + /// Dictionary size for compression algorithms + uint _dictionarySize; + /// Block with logical geometry. + GeometryBlock _geometryBlock; + /// Image header. + AaruHeader _header; + /// Image information. + ImageInfo _imageInfo; + /// Image data stream. + Stream _imageStream; + /// Index. + List _index; + /// If set to true, the DDT entries are in-memory. + bool _inMemoryDdt; + ulong _lastWrittenBlock; + Md5Context _md5Provider; + /// Cache of media tags. + Dictionary _mediaTags; + byte[] _mode2Subheaders; + /// If DDT is on-disk, this is the image stream offset at which it starts. + long _outMemoryDdtPosition; + bool _rewinded; + byte[] _sectorCpiMai; + byte[] _sectorDecryptedTitleKey; + /// Cache for data that prefixes the user data on a sector (e.g. sync). + byte[] _sectorPrefix; + uint[] _sectorPrefixDdt; + MemoryStream _sectorPrefixMs; + /// Cache for data that goes side by side with user data (e.g. CompactDisc subchannel). + byte[] _sectorSubchannel; + /// Cache for data that suffixes the user data on a sector (e.g. edc, ecc). + byte[] _sectorSuffix; + uint[] _sectorSuffixDdt; + MemoryStream _sectorSuffixMs; + Sha1Context _sha1Provider; + Sha256Context _sha256Provider; + /// Shift for calculating number of sectors in a block. + byte _shift; + SpamSumContext _spamsumProvider; + /// Cache for bytes to write/rad on-disk. + byte[] _structureBytes; + /// Cache for pointer for marshaling structures. + IntPtr _structurePointer; + Dictionary _tapeDdt; + /// Cache of CompactDisc track's flags + Dictionary _trackFlags; + /// Cache of CompactDisc track's ISRC + Dictionary _trackIsrcs; + /// In-memory deduplication table + ulong[] _userDataDdt; + byte[] _writingBuffer; + int _writingBufferPosition; + bool _writingLong; + ulong _writtenSectors; + + public AaruFormat() => _imageInfo = new ImageInfo + { + ReadableSectorTags = new List(), + ReadableMediaTags = new List(), + HasPartitions = false, + HasSessions = false, + Version = null, + Application = "Aaru", + ApplicationVersion = null, + Creator = null, + Comments = null, + MediaManufacturer = null, + MediaModel = null, + MediaSerialNumber = null, + MediaBarcode = null, + MediaPartNumber = null, + MediaSequence = 0, + LastMediaSequence = 0, + DriveManufacturer = null, + DriveModel = null, + DriveSerialNumber = null, + DriveFirmwareRevision = null + }; } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/CdEcc.cs b/Aaru.Images/AaruFormat/CdEcc.cs index 5507bb33d..a90810d66 100644 --- a/Aaru.Images/AaruFormat/CdEcc.cs +++ b/Aaru.Images/AaruFormat/CdEcc.cs @@ -34,326 +34,325 @@ using System; using Aaru.CommonTypes.Enums; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + byte[] _eccBTable; + byte[] _eccFTable; + uint[] _edcTable; + bool _initedEdc; + + void EccInit() { - byte[] _eccBTable; - byte[] _eccFTable; - uint[] _edcTable; - bool _initedEdc; + if(_initedEdc) + return; - void EccInit() + _eccFTable = new byte[256]; + _eccBTable = new byte[256]; + _edcTable = new uint[256]; + + for(uint i = 0; i < 256; i++) { - if(_initedEdc) - return; + uint edc = i; + uint j = (uint)((i << 1) ^ ((i & 0x80) == 0x80 ? 0x11D : 0)); + _eccFTable[i] = (byte)j; + _eccBTable[i ^ j] = (byte)i; - _eccFTable = new byte[256]; - _eccBTable = new byte[256]; - _edcTable = new uint[256]; + for(j = 0; j < 8; j++) + edc = (edc >> 1) ^ ((edc & 1) > 0 ? 0xD8018001 : 0); - for(uint i = 0; i < 256; i++) - { - uint edc = i; - uint j = (uint)((i << 1) ^ ((i & 0x80) == 0x80 ? 0x11D : 0)); - _eccFTable[i] = (byte)j; - _eccBTable[i ^ j] = (byte)i; - - for(j = 0; j < 8; j++) - edc = (edc >> 1) ^ ((edc & 1) > 0 ? 0xD8018001 : 0); - - _edcTable[i] = edc; - } - - _initedEdc = true; + _edcTable[i] = edc; } - bool SuffixIsCorrect(byte[] sector) - { - if(!_initedEdc) - EccInit(); + _initedEdc = true; + } - if(sector[0x814] != 0x00 || // reserved (8 bytes) - sector[0x815] != 0x00 || - sector[0x816] != 0x00 || - sector[0x817] != 0x00 || - sector[0x818] != 0x00 || - sector[0x819] != 0x00 || - sector[0x81A] != 0x00 || - sector[0x81B] != 0x00) + bool SuffixIsCorrect(byte[] sector) + { + if(!_initedEdc) + EccInit(); + + if(sector[0x814] != 0x00 || // reserved (8 bytes) + sector[0x815] != 0x00 || + sector[0x816] != 0x00 || + sector[0x817] != 0x00 || + sector[0x818] != 0x00 || + sector[0x819] != 0x00 || + sector[0x81A] != 0x00 || + sector[0x81B] != 0x00) + return false; + + bool correctEccP = CheckEcc(sector, sector, 86, 24, 2, 86, sector, 0xC, 0x10, 0x81C); + + if(!correctEccP) + return false; + + bool correctEccQ = CheckEcc(sector, sector, 52, 43, 86, 88, sector, 0xC, 0x10, 0x81C + 0xAC); + + if(!correctEccQ) + return false; + + uint storedEdc = BitConverter.ToUInt32(sector, 0x810); + uint edc = 0; + int size = 0x810; + int pos = 0; + + for(; size > 0; size--) + edc = (edc >> 8) ^ _edcTable[(edc ^ sector[pos++]) & 0xFF]; + + uint calculatedEdc = edc; + + return calculatedEdc == storedEdc; + } + + bool SuffixIsCorrectMode2(byte[] sector) + { + if(!_initedEdc) + EccInit(); + + byte[] zeroAddress = new byte[4]; + + bool correctEccP = CheckEcc(zeroAddress, sector, 86, 24, 2, 86, sector, 0, 0x10, 0x81C); + + if(!correctEccP) + return false; + + bool correctEccQ = CheckEcc(zeroAddress, sector, 52, 43, 86, 88, sector, 0, 0x10, 0x81C + 0xAC); + + if(!correctEccQ) + return false; + + uint storedEdc = BitConverter.ToUInt32(sector, 0x818); + uint edc = 0; + int size = 0x808; + int pos = 0x10; + + for(; size > 0; size--) + edc = (edc >> 8) ^ _edcTable[(edc ^ sector[pos++]) & 0xFF]; + + uint calculatedEdc = edc; + + return calculatedEdc == storedEdc; + } + + bool CheckEcc(byte[] address, byte[] data, uint majorCount, uint minorCount, uint majorMult, uint minorInc, + byte[] ecc, int addressOffset, int dataOffset, int eccOffset) + { + uint size = majorCount * minorCount; + uint major; + + for(major = 0; major < majorCount; major++) + { + uint idx = ((major >> 1) * majorMult) + (major & 1); + byte eccA = 0; + byte eccB = 0; + uint minor; + + for(minor = 0; minor < minorCount; minor++) + { + byte temp = idx < 4 ? address[idx + addressOffset] : data[idx + dataOffset - 4]; + idx += minorInc; + + if(idx >= size) + idx -= size; + + eccA ^= temp; + eccB ^= temp; + eccA = _eccFTable[eccA]; + } + + eccA = _eccBTable[_eccFTable[eccA] ^ eccB]; + + if(ecc[major + eccOffset] != eccA || + ecc[major + majorCount + eccOffset] != (eccA ^ eccB)) return false; - - bool correctEccP = CheckEcc(sector, sector, 86, 24, 2, 86, sector, 0xC, 0x10, 0x81C); - - if(!correctEccP) - return false; - - bool correctEccQ = CheckEcc(sector, sector, 52, 43, 86, 88, sector, 0xC, 0x10, 0x81C + 0xAC); - - if(!correctEccQ) - return false; - - uint storedEdc = BitConverter.ToUInt32(sector, 0x810); - uint edc = 0; - int size = 0x810; - int pos = 0; - - for(; size > 0; size--) - edc = (edc >> 8) ^ _edcTable[(edc ^ sector[pos++]) & 0xFF]; - - uint calculatedEdc = edc; - - return calculatedEdc == storedEdc; } - bool SuffixIsCorrectMode2(byte[] sector) + return true; + } + + void WriteEcc(byte[] address, byte[] data, uint majorCount, uint minorCount, uint majorMult, uint minorInc, + ref byte[] ecc, int addressOffset, int dataOffset, int eccOffset) + { + uint size = majorCount * minorCount; + uint major; + + for(major = 0; major < majorCount; major++) { - if(!_initedEdc) - EccInit(); + uint idx = ((major >> 1) * majorMult) + (major & 1); + byte eccA = 0; + byte eccB = 0; + uint minor; - byte[] zeroAddress = new byte[4]; - - bool correctEccP = CheckEcc(zeroAddress, sector, 86, 24, 2, 86, sector, 0, 0x10, 0x81C); - - if(!correctEccP) - return false; - - bool correctEccQ = CheckEcc(zeroAddress, sector, 52, 43, 86, 88, sector, 0, 0x10, 0x81C + 0xAC); - - if(!correctEccQ) - return false; - - uint storedEdc = BitConverter.ToUInt32(sector, 0x818); - uint edc = 0; - int size = 0x808; - int pos = 0x10; - - for(; size > 0; size--) - edc = (edc >> 8) ^ _edcTable[(edc ^ sector[pos++]) & 0xFF]; - - uint calculatedEdc = edc; - - return calculatedEdc == storedEdc; - } - - bool CheckEcc(byte[] address, byte[] data, uint majorCount, uint minorCount, uint majorMult, uint minorInc, - byte[] ecc, int addressOffset, int dataOffset, int eccOffset) - { - uint size = majorCount * minorCount; - uint major; - - for(major = 0; major < majorCount; major++) + for(minor = 0; minor < minorCount; minor++) { - uint idx = ((major >> 1) * majorMult) + (major & 1); - byte eccA = 0; - byte eccB = 0; - uint minor; + byte temp = idx < 4 ? address[idx + addressOffset] : data[idx + dataOffset - 4]; + idx += minorInc; - for(minor = 0; minor < minorCount; minor++) - { - byte temp = idx < 4 ? address[idx + addressOffset] : data[idx + dataOffset - 4]; - idx += minorInc; + if(idx >= size) + idx -= size; - if(idx >= size) - idx -= size; - - eccA ^= temp; - eccB ^= temp; - eccA = _eccFTable[eccA]; - } - - eccA = _eccBTable[_eccFTable[eccA] ^ eccB]; - - if(ecc[major + eccOffset] != eccA || - ecc[major + majorCount + eccOffset] != (eccA ^ eccB)) - return false; + eccA ^= temp; + eccB ^= temp; + eccA = _eccFTable[eccA]; } - return true; - } - - void WriteEcc(byte[] address, byte[] data, uint majorCount, uint minorCount, uint majorMult, uint minorInc, - ref byte[] ecc, int addressOffset, int dataOffset, int eccOffset) - { - uint size = majorCount * minorCount; - uint major; - - for(major = 0; major < majorCount; major++) - { - uint idx = ((major >> 1) * majorMult) + (major & 1); - byte eccA = 0; - byte eccB = 0; - uint minor; - - for(minor = 0; minor < minorCount; minor++) - { - byte temp = idx < 4 ? address[idx + addressOffset] : data[idx + dataOffset - 4]; - idx += minorInc; - - if(idx >= size) - idx -= size; - - eccA ^= temp; - eccB ^= temp; - eccA = _eccFTable[eccA]; - } - - eccA = _eccBTable[_eccFTable[eccA] ^ eccB]; - ecc[major + eccOffset] = eccA; - ecc[major + majorCount + eccOffset] = (byte)(eccA ^ eccB); - } - } - - void EccWriteSector(byte[] address, byte[] data, ref byte[] ecc, int addressOffset, int dataOffset, - int eccOffset) - { - WriteEcc(address, data, 86, 24, 2, 86, ref ecc, addressOffset, dataOffset, eccOffset); // P - WriteEcc(address, data, 52, 43, 86, 88, ref ecc, addressOffset, dataOffset, eccOffset + 0xAC); // Q - } - - static (byte minute, byte second, byte frame) LbaToMsf(long pos) => - ((byte)((pos + 150) / 75 / 60), (byte)((pos + 150) / 75 % 60), (byte)((pos + 150) % 75)); - - void ReconstructPrefix(ref byte[] sector, // must point to a full 2352-byte sector - TrackType type, long lba) - { - // - // Sync - // - sector[0x000] = 0x00; - sector[0x001] = 0xFF; - sector[0x002] = 0xFF; - sector[0x003] = 0xFF; - sector[0x004] = 0xFF; - sector[0x005] = 0xFF; - sector[0x006] = 0xFF; - sector[0x007] = 0xFF; - sector[0x008] = 0xFF; - sector[0x009] = 0xFF; - sector[0x00A] = 0xFF; - sector[0x00B] = 0x00; - - (byte minute, byte second, byte frame) msf = LbaToMsf(lba); - - sector[0x00C] = (byte)(((msf.minute / 10) << 4) + (msf.minute % 10)); - sector[0x00D] = (byte)(((msf.second / 10) << 4) + (msf.second % 10)); - sector[0x00E] = (byte)(((msf.frame / 10) << 4) + (msf.frame % 10)); - - switch(type) - { - case TrackType.CdMode1: - // - // Mode - // - sector[0x00F] = 0x01; - - break; - case TrackType.CdMode2Form1: - case TrackType.CdMode2Form2: - case TrackType.CdMode2Formless: - // - // Mode - // - sector[0x00F] = 0x02; - - // - // Flags - // - sector[0x010] = sector[0x014]; - sector[0x011] = sector[0x015]; - sector[0x012] = sector[0x016]; - sector[0x013] = sector[0x017]; - - break; - default: return; - } - } - - void ReconstructEcc(ref byte[] sector, // must point to a full 2352-byte sector - TrackType type) - { - byte[] computedEdc; - - if(!_initedEdc) - EccInit(); - - switch(type) - { - // - // Compute EDC - // - case TrackType.CdMode1: - computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x810)); - sector[0x810] = computedEdc[0]; - sector[0x811] = computedEdc[1]; - sector[0x812] = computedEdc[2]; - sector[0x813] = computedEdc[3]; - - break; - case TrackType.CdMode2Form1: - computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x808, 0x10)); - sector[0x818] = computedEdc[0]; - sector[0x819] = computedEdc[1]; - sector[0x81A] = computedEdc[2]; - sector[0x81B] = computedEdc[3]; - - break; - case TrackType.CdMode2Form2: - computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x91C, 0x10)); - sector[0x92C] = computedEdc[0]; - sector[0x92D] = computedEdc[1]; - sector[0x92E] = computedEdc[2]; - sector[0x92F] = computedEdc[3]; - - break; - default: return; - } - - byte[] zeroAddress = new byte[4]; - - switch(type) - { - // - // Compute ECC - // - case TrackType.CdMode1: - // - // Reserved - // - sector[0x814] = 0x00; - sector[0x815] = 0x00; - sector[0x816] = 0x00; - sector[0x817] = 0x00; - sector[0x818] = 0x00; - sector[0x819] = 0x00; - sector[0x81A] = 0x00; - sector[0x81B] = 0x00; - EccWriteSector(sector, sector, ref sector, 0xC, 0x10, 0x81C); - - break; - case TrackType.CdMode2Form1: - EccWriteSector(zeroAddress, sector, ref sector, 0, 0x10, 0x81C); - - break; - default: return; - } - - // - // Done - // - } - - uint ComputeEdc(uint edc, byte[] src, int size, int srcOffset = 0) - { - if(!_initedEdc) - EccInit(); - - int pos = srcOffset; - - for(; size > 0; size--) - edc = (edc >> 8) ^ _edcTable[(edc ^ src[pos++]) & 0xFF]; - - return edc; + eccA = _eccBTable[_eccFTable[eccA] ^ eccB]; + ecc[major + eccOffset] = eccA; + ecc[major + majorCount + eccOffset] = (byte)(eccA ^ eccB); } } + + void EccWriteSector(byte[] address, byte[] data, ref byte[] ecc, int addressOffset, int dataOffset, + int eccOffset) + { + WriteEcc(address, data, 86, 24, 2, 86, ref ecc, addressOffset, dataOffset, eccOffset); // P + WriteEcc(address, data, 52, 43, 86, 88, ref ecc, addressOffset, dataOffset, eccOffset + 0xAC); // Q + } + + static (byte minute, byte second, byte frame) LbaToMsf(long pos) => + ((byte)((pos + 150) / 75 / 60), (byte)((pos + 150) / 75 % 60), (byte)((pos + 150) % 75)); + + void ReconstructPrefix(ref byte[] sector, // must point to a full 2352-byte sector + TrackType type, long lba) + { + // + // Sync + // + sector[0x000] = 0x00; + sector[0x001] = 0xFF; + sector[0x002] = 0xFF; + sector[0x003] = 0xFF; + sector[0x004] = 0xFF; + sector[0x005] = 0xFF; + sector[0x006] = 0xFF; + sector[0x007] = 0xFF; + sector[0x008] = 0xFF; + sector[0x009] = 0xFF; + sector[0x00A] = 0xFF; + sector[0x00B] = 0x00; + + (byte minute, byte second, byte frame) msf = LbaToMsf(lba); + + sector[0x00C] = (byte)(((msf.minute / 10) << 4) + (msf.minute % 10)); + sector[0x00D] = (byte)(((msf.second / 10) << 4) + (msf.second % 10)); + sector[0x00E] = (byte)(((msf.frame / 10) << 4) + (msf.frame % 10)); + + switch(type) + { + case TrackType.CdMode1: + // + // Mode + // + sector[0x00F] = 0x01; + + break; + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + case TrackType.CdMode2Formless: + // + // Mode + // + sector[0x00F] = 0x02; + + // + // Flags + // + sector[0x010] = sector[0x014]; + sector[0x011] = sector[0x015]; + sector[0x012] = sector[0x016]; + sector[0x013] = sector[0x017]; + + break; + default: return; + } + } + + void ReconstructEcc(ref byte[] sector, // must point to a full 2352-byte sector + TrackType type) + { + byte[] computedEdc; + + if(!_initedEdc) + EccInit(); + + switch(type) + { + // + // Compute EDC + // + case TrackType.CdMode1: + computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x810)); + sector[0x810] = computedEdc[0]; + sector[0x811] = computedEdc[1]; + sector[0x812] = computedEdc[2]; + sector[0x813] = computedEdc[3]; + + break; + case TrackType.CdMode2Form1: + computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x808, 0x10)); + sector[0x818] = computedEdc[0]; + sector[0x819] = computedEdc[1]; + sector[0x81A] = computedEdc[2]; + sector[0x81B] = computedEdc[3]; + + break; + case TrackType.CdMode2Form2: + computedEdc = BitConverter.GetBytes(ComputeEdc(0, sector, 0x91C, 0x10)); + sector[0x92C] = computedEdc[0]; + sector[0x92D] = computedEdc[1]; + sector[0x92E] = computedEdc[2]; + sector[0x92F] = computedEdc[3]; + + break; + default: return; + } + + byte[] zeroAddress = new byte[4]; + + switch(type) + { + // + // Compute ECC + // + case TrackType.CdMode1: + // + // Reserved + // + sector[0x814] = 0x00; + sector[0x815] = 0x00; + sector[0x816] = 0x00; + sector[0x817] = 0x00; + sector[0x818] = 0x00; + sector[0x819] = 0x00; + sector[0x81A] = 0x00; + sector[0x81B] = 0x00; + EccWriteSector(sector, sector, ref sector, 0xC, 0x10, 0x81C); + + break; + case TrackType.CdMode2Form1: + EccWriteSector(zeroAddress, sector, ref sector, 0, 0x10, 0x81C); + + break; + default: return; + } + + // + // Done + // + } + + uint ComputeEdc(uint edc, byte[] src, int size, int srcOffset = 0) + { + if(!_initedEdc) + EccInit(); + + int pos = srcOffset; + + for(; size > 0; size--) + edc = (edc >> 8) ^ _edcTable[(edc ^ src[pos++]) & 0xFF]; + + return edc; + } } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/ClauniaSubchannelTransform.cs b/Aaru.Images/AaruFormat/ClauniaSubchannelTransform.cs index fd13dcc9f..2904f36e5 100644 --- a/Aaru.Images/AaruFormat/ClauniaSubchannelTransform.cs +++ b/Aaru.Images/AaruFormat/ClauniaSubchannelTransform.cs @@ -33,272 +33,271 @@ using System; using Aaru.Console; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + static byte[] ClauniaSubchannelTransform(byte[] interleaved) { - static byte[] ClauniaSubchannelTransform(byte[] interleaved) + if(interleaved == null) + return null; + + int[] p = new int[interleaved.Length / 8]; + int[] q = new int[interleaved.Length / 8]; + int[] r = new int[interleaved.Length / 8]; + int[] s = new int[interleaved.Length / 8]; + int[] t = new int[interleaved.Length / 8]; + int[] u = new int[interleaved.Length / 8]; + int[] v = new int[interleaved.Length / 8]; + int[] w = new int[interleaved.Length / 8]; + + DateTime start = DateTime.UtcNow; + + for(int i = 0; i < interleaved.Length; i += 8) { - if(interleaved == null) - return null; + p[i / 8] = interleaved[i] & 0x80; + p[i / 8] += (interleaved[i + 1] & 0x80) >> 1; + p[i / 8] += (interleaved[i + 2] & 0x80) >> 2; + p[i / 8] += (interleaved[i + 3] & 0x80) >> 3; + p[i / 8] += (interleaved[i + 4] & 0x80) >> 4; + p[i / 8] += (interleaved[i + 5] & 0x80) >> 5; + p[i / 8] += (interleaved[i + 6] & 0x80) >> 6; + p[i / 8] += (interleaved[i + 7] & 0x80) >> 7; - int[] p = new int[interleaved.Length / 8]; - int[] q = new int[interleaved.Length / 8]; - int[] r = new int[interleaved.Length / 8]; - int[] s = new int[interleaved.Length / 8]; - int[] t = new int[interleaved.Length / 8]; - int[] u = new int[interleaved.Length / 8]; - int[] v = new int[interleaved.Length / 8]; - int[] w = new int[interleaved.Length / 8]; + q[i / 8] = (interleaved[i] & 0x40) << 1; + q[i / 8] += interleaved[i + 1] & 0x40; + q[i / 8] += (interleaved[i + 2] & 0x40) >> 1; + q[i / 8] += (interleaved[i + 3] & 0x40) >> 2; + q[i / 8] += (interleaved[i + 4] & 0x40) >> 3; + q[i / 8] += (interleaved[i + 5] & 0x40) >> 4; + q[i / 8] += (interleaved[i + 6] & 0x40) >> 5; + q[i / 8] += (interleaved[i + 7] & 0x40) >> 6; - DateTime start = DateTime.UtcNow; + r[i / 8] = (interleaved[i] & 0x20) << 2; + r[i / 8] += (interleaved[i + 1] & 0x20) << 1; + r[i / 8] += interleaved[i + 2] & 0x20; + r[i / 8] += (interleaved[i + 3] & 0x20) >> 1; + r[i / 8] += (interleaved[i + 4] & 0x20) >> 2; + r[i / 8] += (interleaved[i + 5] & 0x20) >> 3; + r[i / 8] += (interleaved[i + 6] & 0x20) >> 4; + r[i / 8] += (interleaved[i + 7] & 0x20) >> 5; - for(int i = 0; i < interleaved.Length; i += 8) - { - p[i / 8] = interleaved[i] & 0x80; - p[i / 8] += (interleaved[i + 1] & 0x80) >> 1; - p[i / 8] += (interleaved[i + 2] & 0x80) >> 2; - p[i / 8] += (interleaved[i + 3] & 0x80) >> 3; - p[i / 8] += (interleaved[i + 4] & 0x80) >> 4; - p[i / 8] += (interleaved[i + 5] & 0x80) >> 5; - p[i / 8] += (interleaved[i + 6] & 0x80) >> 6; - p[i / 8] += (interleaved[i + 7] & 0x80) >> 7; + s[i / 8] = (interleaved[i] & 0x10) << 3; + s[i / 8] += (interleaved[i + 1] & 0x10) << 2; + s[i / 8] += (interleaved[i + 2] & 0x10) << 1; + s[i / 8] += interleaved[i + 3] & 0x10; + s[i / 8] += (interleaved[i + 4] & 0x10) >> 1; + s[i / 8] += (interleaved[i + 5] & 0x10) >> 2; + s[i / 8] += (interleaved[i + 6] & 0x10) >> 3; + s[i / 8] += (interleaved[i + 7] & 0x10) >> 4; - q[i / 8] = (interleaved[i] & 0x40) << 1; - q[i / 8] += interleaved[i + 1] & 0x40; - q[i / 8] += (interleaved[i + 2] & 0x40) >> 1; - q[i / 8] += (interleaved[i + 3] & 0x40) >> 2; - q[i / 8] += (interleaved[i + 4] & 0x40) >> 3; - q[i / 8] += (interleaved[i + 5] & 0x40) >> 4; - q[i / 8] += (interleaved[i + 6] & 0x40) >> 5; - q[i / 8] += (interleaved[i + 7] & 0x40) >> 6; + t[i / 8] = (interleaved[i] & 0x08) << 4; + t[i / 8] += (interleaved[i + 1] & 0x08) << 3; + t[i / 8] += (interleaved[i + 2] & 0x08) << 2; + t[i / 8] += (interleaved[i + 3] & 0x08) << 1; + t[i / 8] += interleaved[i + 4] & 0x08; + t[i / 8] += (interleaved[i + 5] & 0x08) >> 1; + t[i / 8] += (interleaved[i + 6] & 0x08) >> 2; + t[i / 8] += (interleaved[i + 7] & 0x08) >> 3; - r[i / 8] = (interleaved[i] & 0x20) << 2; - r[i / 8] += (interleaved[i + 1] & 0x20) << 1; - r[i / 8] += interleaved[i + 2] & 0x20; - r[i / 8] += (interleaved[i + 3] & 0x20) >> 1; - r[i / 8] += (interleaved[i + 4] & 0x20) >> 2; - r[i / 8] += (interleaved[i + 5] & 0x20) >> 3; - r[i / 8] += (interleaved[i + 6] & 0x20) >> 4; - r[i / 8] += (interleaved[i + 7] & 0x20) >> 5; + u[i / 8] = (interleaved[i] & 0x04) << 5; + u[i / 8] += (interleaved[i + 1] & 0x04) << 4; + u[i / 8] += (interleaved[i + 2] & 0x04) << 3; + u[i / 8] += (interleaved[i + 3] & 0x04) << 2; + u[i / 8] += (interleaved[i + 4] & 0x04) << 1; + u[i / 8] += interleaved[i + 5] & 0x04; + u[i / 8] += (interleaved[i + 6] & 0x04) >> 1; + u[i / 8] += (interleaved[i + 7] & 0x04) >> 2; - s[i / 8] = (interleaved[i] & 0x10) << 3; - s[i / 8] += (interleaved[i + 1] & 0x10) << 2; - s[i / 8] += (interleaved[i + 2] & 0x10) << 1; - s[i / 8] += interleaved[i + 3] & 0x10; - s[i / 8] += (interleaved[i + 4] & 0x10) >> 1; - s[i / 8] += (interleaved[i + 5] & 0x10) >> 2; - s[i / 8] += (interleaved[i + 6] & 0x10) >> 3; - s[i / 8] += (interleaved[i + 7] & 0x10) >> 4; + v[i / 8] = (interleaved[i] & 0x02) << 6; + v[i / 8] += (interleaved[i + 1] & 0x02) << 5; + v[i / 8] += (interleaved[i + 2] & 0x02) << 4; + v[i / 8] += (interleaved[i + 3] & 0x02) << 3; + v[i / 8] += (interleaved[i + 4] & 0x02) << 2; + v[i / 8] += (interleaved[i + 5] & 0x02) << 1; + v[i / 8] += interleaved[i + 6] & 0x02; + v[i / 8] += (interleaved[i + 7] & 0x02) >> 1; - t[i / 8] = (interleaved[i] & 0x08) << 4; - t[i / 8] += (interleaved[i + 1] & 0x08) << 3; - t[i / 8] += (interleaved[i + 2] & 0x08) << 2; - t[i / 8] += (interleaved[i + 3] & 0x08) << 1; - t[i / 8] += interleaved[i + 4] & 0x08; - t[i / 8] += (interleaved[i + 5] & 0x08) >> 1; - t[i / 8] += (interleaved[i + 6] & 0x08) >> 2; - t[i / 8] += (interleaved[i + 7] & 0x08) >> 3; - - u[i / 8] = (interleaved[i] & 0x04) << 5; - u[i / 8] += (interleaved[i + 1] & 0x04) << 4; - u[i / 8] += (interleaved[i + 2] & 0x04) << 3; - u[i / 8] += (interleaved[i + 3] & 0x04) << 2; - u[i / 8] += (interleaved[i + 4] & 0x04) << 1; - u[i / 8] += interleaved[i + 5] & 0x04; - u[i / 8] += (interleaved[i + 6] & 0x04) >> 1; - u[i / 8] += (interleaved[i + 7] & 0x04) >> 2; - - v[i / 8] = (interleaved[i] & 0x02) << 6; - v[i / 8] += (interleaved[i + 1] & 0x02) << 5; - v[i / 8] += (interleaved[i + 2] & 0x02) << 4; - v[i / 8] += (interleaved[i + 3] & 0x02) << 3; - v[i / 8] += (interleaved[i + 4] & 0x02) << 2; - v[i / 8] += (interleaved[i + 5] & 0x02) << 1; - v[i / 8] += interleaved[i + 6] & 0x02; - v[i / 8] += (interleaved[i + 7] & 0x02) >> 1; - - w[i / 8] = (interleaved[i] & 0x01) << 7; - w[i / 8] += (interleaved[i + 1] & 0x01) << 6; - w[i / 8] += (interleaved[i + 2] & 0x01) << 5; - w[i / 8] += (interleaved[i + 3] & 0x01) << 4; - w[i / 8] += (interleaved[i + 4] & 0x01) << 3; - w[i / 8] += (interleaved[i + 5] & 0x01) << 2; - w[i / 8] += (interleaved[i + 6] & 0x01) << 1; - w[i / 8] += interleaved[i + 7] & 0x01; - } - - DateTime end = DateTime.UtcNow; - TimeSpan deinterleave = end - start; - - byte[] sequential = new byte[interleaved.Length]; - start = DateTime.UtcNow; - - int qStart = p.Length * 1; - int rStart = p.Length * 2; - int sStart = p.Length * 3; - int tStart = p.Length * 4; - int uStart = p.Length * 5; - int vStart = p.Length * 6; - int wStart = p.Length * 7; - - for(int i = 0; i < p.Length; i++) - { - sequential[i] = (byte)p[i]; - sequential[qStart + i] = (byte)q[i]; - sequential[rStart + i] = (byte)r[i]; - sequential[sStart + i] = (byte)s[i]; - sequential[tStart + i] = (byte)t[i]; - sequential[uStart + i] = (byte)u[i]; - sequential[vStart + i] = (byte)v[i]; - sequential[wStart + i] = (byte)w[i]; - } - - end = DateTime.UtcNow; - TimeSpan sequentialize = end - start; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to deinterleave subchannel.", - deinterleave.TotalMilliseconds); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to sequentialize subchannel.", - sequentialize.TotalMilliseconds); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to transform subchannel.", - deinterleave.TotalMilliseconds + sequentialize.TotalMilliseconds); - - return sequential; + w[i / 8] = (interleaved[i] & 0x01) << 7; + w[i / 8] += (interleaved[i + 1] & 0x01) << 6; + w[i / 8] += (interleaved[i + 2] & 0x01) << 5; + w[i / 8] += (interleaved[i + 3] & 0x01) << 4; + w[i / 8] += (interleaved[i + 4] & 0x01) << 3; + w[i / 8] += (interleaved[i + 5] & 0x01) << 2; + w[i / 8] += (interleaved[i + 6] & 0x01) << 1; + w[i / 8] += interleaved[i + 7] & 0x01; } - static byte[] ClauniaSubchannelUntransform(byte[] sequential) + DateTime end = DateTime.UtcNow; + TimeSpan deinterleave = end - start; + + byte[] sequential = new byte[interleaved.Length]; + start = DateTime.UtcNow; + + int qStart = p.Length * 1; + int rStart = p.Length * 2; + int sStart = p.Length * 3; + int tStart = p.Length * 4; + int uStart = p.Length * 5; + int vStart = p.Length * 6; + int wStart = p.Length * 7; + + for(int i = 0; i < p.Length; i++) { - if(sequential == null) - return null; - - int[] p = new int[sequential.Length / 8]; - int[] q = new int[sequential.Length / 8]; - int[] r = new int[sequential.Length / 8]; - int[] s = new int[sequential.Length / 8]; - int[] t = new int[sequential.Length / 8]; - int[] u = new int[sequential.Length / 8]; - int[] v = new int[sequential.Length / 8]; - int[] w = new int[sequential.Length / 8]; - - int qStart = p.Length * 1; - int rStart = p.Length * 2; - int sStart = p.Length * 3; - int tStart = p.Length * 4; - int uStart = p.Length * 5; - int vStart = p.Length * 6; - int wStart = p.Length * 7; - - DateTime start = DateTime.UtcNow; - - for(int i = 0; i < p.Length; i++) - { - p[i] = sequential[i]; - q[i] = sequential[qStart + i]; - r[i] = sequential[rStart + i]; - s[i] = sequential[sStart + i]; - t[i] = sequential[tStart + i]; - u[i] = sequential[uStart + i]; - v[i] = sequential[vStart + i]; - w[i] = sequential[wStart + i]; - } - - DateTime end = DateTime.UtcNow; - TimeSpan desequentialize = end - start; - - byte[] interleaved = new byte[sequential.Length]; - start = DateTime.UtcNow; - - for(int i = 0; i < interleaved.Length; i += 8) - { - interleaved[i] = (byte)((p[i / 8] & 0x80) == 0x80 ? 0x80 : 0); - interleaved[i + 1] += (byte)((p[i / 8] & 0x40) == 0x40 ? 0x80 : 0); - interleaved[i + 2] += (byte)((p[i / 8] & 0x20) == 0x20 ? 0x80 : 0); - interleaved[i + 3] += (byte)((p[i / 8] & 0x10) == 0x10 ? 0x80 : 0); - interleaved[i + 4] += (byte)((p[i / 8] & 0x08) == 0x08 ? 0x80 : 0); - interleaved[i + 5] += (byte)((p[i / 8] & 0x04) == 0x04 ? 0x80 : 0); - interleaved[i + 6] += (byte)((p[i / 8] & 0x02) == 0x02 ? 0x80 : 0); - interleaved[i + 7] += (byte)((p[i / 8] & 0x01) == 0x01 ? 0x80 : 0); - - interleaved[i] += (byte)((q[i / 8] & 0x80) == 0x80 ? 0x40 : 0); - interleaved[i + 1] += (byte)((q[i / 8] & 0x40) == 0x40 ? 0x40 : 0); - interleaved[i + 2] += (byte)((q[i / 8] & 0x20) == 0x20 ? 0x40 : 0); - interleaved[i + 3] += (byte)((q[i / 8] & 0x10) == 0x10 ? 0x40 : 0); - interleaved[i + 4] += (byte)((q[i / 8] & 0x08) == 0x08 ? 0x40 : 0); - interleaved[i + 5] += (byte)((q[i / 8] & 0x04) == 0x04 ? 0x40 : 0); - interleaved[i + 6] += (byte)((q[i / 8] & 0x02) == 0x02 ? 0x40 : 0); - interleaved[i + 7] += (byte)((q[i / 8] & 0x01) == 0x01 ? 0x40 : 0); - - interleaved[i] += (byte)((r[i / 8] & 0x80) == 0x80 ? 0x20 : 0); - interleaved[i + 1] += (byte)((r[i / 8] & 0x40) == 0x40 ? 0x20 : 0); - interleaved[i + 2] += (byte)((r[i / 8] & 0x20) == 0x20 ? 0x20 : 0); - interleaved[i + 3] += (byte)((r[i / 8] & 0x10) == 0x10 ? 0x20 : 0); - interleaved[i + 4] += (byte)((r[i / 8] & 0x08) == 0x08 ? 0x20 : 0); - interleaved[i + 5] += (byte)((r[i / 8] & 0x04) == 0x04 ? 0x20 : 0); - interleaved[i + 6] += (byte)((r[i / 8] & 0x02) == 0x02 ? 0x20 : 0); - interleaved[i + 7] += (byte)((r[i / 8] & 0x01) == 0x01 ? 0x20 : 0); - - interleaved[i] += (byte)((s[i / 8] & 0x80) == 0x80 ? 0x10 : 0); - interleaved[i + 1] += (byte)((s[i / 8] & 0x40) == 0x40 ? 0x10 : 0); - interleaved[i + 2] += (byte)((s[i / 8] & 0x20) == 0x20 ? 0x10 : 0); - interleaved[i + 3] += (byte)((s[i / 8] & 0x10) == 0x10 ? 0x10 : 0); - interleaved[i + 4] += (byte)((s[i / 8] & 0x08) == 0x08 ? 0x10 : 0); - interleaved[i + 5] += (byte)((s[i / 8] & 0x04) == 0x04 ? 0x10 : 0); - interleaved[i + 6] += (byte)((s[i / 8] & 0x02) == 0x02 ? 0x10 : 0); - interleaved[i + 7] += (byte)((s[i / 8] & 0x01) == 0x01 ? 0x10 : 0); - - interleaved[i] += (byte)((t[i / 8] & 0x80) == 0x80 ? 0x08 : 0); - interleaved[i + 1] += (byte)((t[i / 8] & 0x40) == 0x40 ? 0x08 : 0); - interleaved[i + 2] += (byte)((t[i / 8] & 0x20) == 0x20 ? 0x08 : 0); - interleaved[i + 3] += (byte)((t[i / 8] & 0x10) == 0x10 ? 0x08 : 0); - interleaved[i + 4] += (byte)((t[i / 8] & 0x08) == 0x08 ? 0x08 : 0); - interleaved[i + 5] += (byte)((t[i / 8] & 0x04) == 0x04 ? 0x08 : 0); - interleaved[i + 6] += (byte)((t[i / 8] & 0x02) == 0x02 ? 0x08 : 0); - interleaved[i + 7] += (byte)((t[i / 8] & 0x01) == 0x01 ? 0x08 : 0); - - interleaved[i] += (byte)((u[i / 8] & 0x80) == 0x80 ? 0x04 : 0); - interleaved[i + 1] += (byte)((u[i / 8] & 0x40) == 0x40 ? 0x04 : 0); - interleaved[i + 2] += (byte)((u[i / 8] & 0x20) == 0x20 ? 0x04 : 0); - interleaved[i + 3] += (byte)((u[i / 8] & 0x10) == 0x10 ? 0x04 : 0); - interleaved[i + 4] += (byte)((u[i / 8] & 0x08) == 0x08 ? 0x04 : 0); - interleaved[i + 5] += (byte)((u[i / 8] & 0x04) == 0x04 ? 0x04 : 0); - interleaved[i + 6] += (byte)((u[i / 8] & 0x02) == 0x02 ? 0x04 : 0); - interleaved[i + 7] += (byte)((u[i / 8] & 0x01) == 0x01 ? 0x04 : 0); - - interleaved[i] += (byte)((v[i / 8] & 0x80) == 0x80 ? 0x02 : 0); - interleaved[i + 1] += (byte)((v[i / 8] & 0x40) == 0x40 ? 0x02 : 0); - interleaved[i + 2] += (byte)((v[i / 8] & 0x20) == 0x20 ? 0x02 : 0); - interleaved[i + 3] += (byte)((v[i / 8] & 0x10) == 0x10 ? 0x02 : 0); - interleaved[i + 4] += (byte)((v[i / 8] & 0x08) == 0x08 ? 0x02 : 0); - interleaved[i + 5] += (byte)((v[i / 8] & 0x04) == 0x04 ? 0x02 : 0); - interleaved[i + 6] += (byte)((v[i / 8] & 0x02) == 0x02 ? 0x02 : 0); - interleaved[i + 7] += (byte)((v[i / 8] & 0x01) == 0x01 ? 0x02 : 0); - - interleaved[i] += (byte)((w[i / 8] & 0x80) == 0x80 ? 0x01 : 0); - interleaved[i + 1] += (byte)((w[i / 8] & 0x40) == 0x40 ? 0x01 : 0); - interleaved[i + 2] += (byte)((w[i / 8] & 0x20) == 0x20 ? 0x01 : 0); - interleaved[i + 3] += (byte)((w[i / 8] & 0x10) == 0x10 ? 0x01 : 0); - interleaved[i + 4] += (byte)((w[i / 8] & 0x08) == 0x08 ? 0x01 : 0); - interleaved[i + 5] += (byte)((w[i / 8] & 0x04) == 0x04 ? 0x01 : 0); - interleaved[i + 6] += (byte)((w[i / 8] & 0x02) == 0x02 ? 0x01 : 0); - interleaved[i + 7] += (byte)((w[i / 8] & 0x01) == 0x01 ? 0x01 : 0); - } - - end = DateTime.UtcNow; - TimeSpan interleave = end - start; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to de-sequentialize subchannel.", - desequentialize.TotalMilliseconds); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to interleave subchannel.", - interleave.TotalMilliseconds); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to untransform subchannel.", - interleave.TotalMilliseconds + desequentialize.TotalMilliseconds); - - return interleaved; + sequential[i] = (byte)p[i]; + sequential[qStart + i] = (byte)q[i]; + sequential[rStart + i] = (byte)r[i]; + sequential[sStart + i] = (byte)s[i]; + sequential[tStart + i] = (byte)t[i]; + sequential[uStart + i] = (byte)u[i]; + sequential[vStart + i] = (byte)v[i]; + sequential[wStart + i] = (byte)w[i]; } + + end = DateTime.UtcNow; + TimeSpan sequentialize = end - start; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to deinterleave subchannel.", + deinterleave.TotalMilliseconds); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to sequentialize subchannel.", + sequentialize.TotalMilliseconds); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to transform subchannel.", + deinterleave.TotalMilliseconds + sequentialize.TotalMilliseconds); + + return sequential; + } + + static byte[] ClauniaSubchannelUntransform(byte[] sequential) + { + if(sequential == null) + return null; + + int[] p = new int[sequential.Length / 8]; + int[] q = new int[sequential.Length / 8]; + int[] r = new int[sequential.Length / 8]; + int[] s = new int[sequential.Length / 8]; + int[] t = new int[sequential.Length / 8]; + int[] u = new int[sequential.Length / 8]; + int[] v = new int[sequential.Length / 8]; + int[] w = new int[sequential.Length / 8]; + + int qStart = p.Length * 1; + int rStart = p.Length * 2; + int sStart = p.Length * 3; + int tStart = p.Length * 4; + int uStart = p.Length * 5; + int vStart = p.Length * 6; + int wStart = p.Length * 7; + + DateTime start = DateTime.UtcNow; + + for(int i = 0; i < p.Length; i++) + { + p[i] = sequential[i]; + q[i] = sequential[qStart + i]; + r[i] = sequential[rStart + i]; + s[i] = sequential[sStart + i]; + t[i] = sequential[tStart + i]; + u[i] = sequential[uStart + i]; + v[i] = sequential[vStart + i]; + w[i] = sequential[wStart + i]; + } + + DateTime end = DateTime.UtcNow; + TimeSpan desequentialize = end - start; + + byte[] interleaved = new byte[sequential.Length]; + start = DateTime.UtcNow; + + for(int i = 0; i < interleaved.Length; i += 8) + { + interleaved[i] = (byte)((p[i / 8] & 0x80) == 0x80 ? 0x80 : 0); + interleaved[i + 1] += (byte)((p[i / 8] & 0x40) == 0x40 ? 0x80 : 0); + interleaved[i + 2] += (byte)((p[i / 8] & 0x20) == 0x20 ? 0x80 : 0); + interleaved[i + 3] += (byte)((p[i / 8] & 0x10) == 0x10 ? 0x80 : 0); + interleaved[i + 4] += (byte)((p[i / 8] & 0x08) == 0x08 ? 0x80 : 0); + interleaved[i + 5] += (byte)((p[i / 8] & 0x04) == 0x04 ? 0x80 : 0); + interleaved[i + 6] += (byte)((p[i / 8] & 0x02) == 0x02 ? 0x80 : 0); + interleaved[i + 7] += (byte)((p[i / 8] & 0x01) == 0x01 ? 0x80 : 0); + + interleaved[i] += (byte)((q[i / 8] & 0x80) == 0x80 ? 0x40 : 0); + interleaved[i + 1] += (byte)((q[i / 8] & 0x40) == 0x40 ? 0x40 : 0); + interleaved[i + 2] += (byte)((q[i / 8] & 0x20) == 0x20 ? 0x40 : 0); + interleaved[i + 3] += (byte)((q[i / 8] & 0x10) == 0x10 ? 0x40 : 0); + interleaved[i + 4] += (byte)((q[i / 8] & 0x08) == 0x08 ? 0x40 : 0); + interleaved[i + 5] += (byte)((q[i / 8] & 0x04) == 0x04 ? 0x40 : 0); + interleaved[i + 6] += (byte)((q[i / 8] & 0x02) == 0x02 ? 0x40 : 0); + interleaved[i + 7] += (byte)((q[i / 8] & 0x01) == 0x01 ? 0x40 : 0); + + interleaved[i] += (byte)((r[i / 8] & 0x80) == 0x80 ? 0x20 : 0); + interleaved[i + 1] += (byte)((r[i / 8] & 0x40) == 0x40 ? 0x20 : 0); + interleaved[i + 2] += (byte)((r[i / 8] & 0x20) == 0x20 ? 0x20 : 0); + interleaved[i + 3] += (byte)((r[i / 8] & 0x10) == 0x10 ? 0x20 : 0); + interleaved[i + 4] += (byte)((r[i / 8] & 0x08) == 0x08 ? 0x20 : 0); + interleaved[i + 5] += (byte)((r[i / 8] & 0x04) == 0x04 ? 0x20 : 0); + interleaved[i + 6] += (byte)((r[i / 8] & 0x02) == 0x02 ? 0x20 : 0); + interleaved[i + 7] += (byte)((r[i / 8] & 0x01) == 0x01 ? 0x20 : 0); + + interleaved[i] += (byte)((s[i / 8] & 0x80) == 0x80 ? 0x10 : 0); + interleaved[i + 1] += (byte)((s[i / 8] & 0x40) == 0x40 ? 0x10 : 0); + interleaved[i + 2] += (byte)((s[i / 8] & 0x20) == 0x20 ? 0x10 : 0); + interleaved[i + 3] += (byte)((s[i / 8] & 0x10) == 0x10 ? 0x10 : 0); + interleaved[i + 4] += (byte)((s[i / 8] & 0x08) == 0x08 ? 0x10 : 0); + interleaved[i + 5] += (byte)((s[i / 8] & 0x04) == 0x04 ? 0x10 : 0); + interleaved[i + 6] += (byte)((s[i / 8] & 0x02) == 0x02 ? 0x10 : 0); + interleaved[i + 7] += (byte)((s[i / 8] & 0x01) == 0x01 ? 0x10 : 0); + + interleaved[i] += (byte)((t[i / 8] & 0x80) == 0x80 ? 0x08 : 0); + interleaved[i + 1] += (byte)((t[i / 8] & 0x40) == 0x40 ? 0x08 : 0); + interleaved[i + 2] += (byte)((t[i / 8] & 0x20) == 0x20 ? 0x08 : 0); + interleaved[i + 3] += (byte)((t[i / 8] & 0x10) == 0x10 ? 0x08 : 0); + interleaved[i + 4] += (byte)((t[i / 8] & 0x08) == 0x08 ? 0x08 : 0); + interleaved[i + 5] += (byte)((t[i / 8] & 0x04) == 0x04 ? 0x08 : 0); + interleaved[i + 6] += (byte)((t[i / 8] & 0x02) == 0x02 ? 0x08 : 0); + interleaved[i + 7] += (byte)((t[i / 8] & 0x01) == 0x01 ? 0x08 : 0); + + interleaved[i] += (byte)((u[i / 8] & 0x80) == 0x80 ? 0x04 : 0); + interleaved[i + 1] += (byte)((u[i / 8] & 0x40) == 0x40 ? 0x04 : 0); + interleaved[i + 2] += (byte)((u[i / 8] & 0x20) == 0x20 ? 0x04 : 0); + interleaved[i + 3] += (byte)((u[i / 8] & 0x10) == 0x10 ? 0x04 : 0); + interleaved[i + 4] += (byte)((u[i / 8] & 0x08) == 0x08 ? 0x04 : 0); + interleaved[i + 5] += (byte)((u[i / 8] & 0x04) == 0x04 ? 0x04 : 0); + interleaved[i + 6] += (byte)((u[i / 8] & 0x02) == 0x02 ? 0x04 : 0); + interleaved[i + 7] += (byte)((u[i / 8] & 0x01) == 0x01 ? 0x04 : 0); + + interleaved[i] += (byte)((v[i / 8] & 0x80) == 0x80 ? 0x02 : 0); + interleaved[i + 1] += (byte)((v[i / 8] & 0x40) == 0x40 ? 0x02 : 0); + interleaved[i + 2] += (byte)((v[i / 8] & 0x20) == 0x20 ? 0x02 : 0); + interleaved[i + 3] += (byte)((v[i / 8] & 0x10) == 0x10 ? 0x02 : 0); + interleaved[i + 4] += (byte)((v[i / 8] & 0x08) == 0x08 ? 0x02 : 0); + interleaved[i + 5] += (byte)((v[i / 8] & 0x04) == 0x04 ? 0x02 : 0); + interleaved[i + 6] += (byte)((v[i / 8] & 0x02) == 0x02 ? 0x02 : 0); + interleaved[i + 7] += (byte)((v[i / 8] & 0x01) == 0x01 ? 0x02 : 0); + + interleaved[i] += (byte)((w[i / 8] & 0x80) == 0x80 ? 0x01 : 0); + interleaved[i + 1] += (byte)((w[i / 8] & 0x40) == 0x40 ? 0x01 : 0); + interleaved[i + 2] += (byte)((w[i / 8] & 0x20) == 0x20 ? 0x01 : 0); + interleaved[i + 3] += (byte)((w[i / 8] & 0x10) == 0x10 ? 0x01 : 0); + interleaved[i + 4] += (byte)((w[i / 8] & 0x08) == 0x08 ? 0x01 : 0); + interleaved[i + 5] += (byte)((w[i / 8] & 0x04) == 0x04 ? 0x01 : 0); + interleaved[i + 6] += (byte)((w[i / 8] & 0x02) == 0x02 ? 0x01 : 0); + interleaved[i + 7] += (byte)((w[i / 8] & 0x01) == 0x01 ? 0x01 : 0); + } + + end = DateTime.UtcNow; + TimeSpan interleave = end - start; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to de-sequentialize subchannel.", + desequentialize.TotalMilliseconds); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to interleave subchannel.", + interleave.TotalMilliseconds); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0}ms to untransform subchannel.", + interleave.TotalMilliseconds + desequentialize.TotalMilliseconds); + + return interleaved; } } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Constants.cs b/Aaru.Images/AaruFormat/Constants.cs index fb263e007..5ce540ace 100644 --- a/Aaru.Images/AaruFormat/Constants.cs +++ b/Aaru.Images/AaruFormat/Constants.cs @@ -30,39 +30,38 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat - { - /// Old magic identifier = "DICMFRMT". - const ulong DIC_MAGIC = 0x544D52464D434944; - /// Magic identifier = "AARUFRMT". - const ulong AARU_MAGIC = 0x544D524655524141; - /// - /// Image format version. A change in this number indicates an incompatible change to the format that prevents - /// older implementations from reading it correctly, if at all. - /// - const byte AARUFMT_VERSION_V1 = 1; - /// Adds new index format with 64-bit entries counter - const byte AARUFMT_VERSION = 2; - /// Maximum read cache size, 256MiB. - const uint MAX_CACHE_SIZE = 256 * 1024 * 1024; - /// Size in bytes of LZMA properties. - const int LZMA_PROPERTIES_LENGTH = 5; - /// Maximum number of entries for the DDT cache. - const int MAX_DDT_ENTRY_CACHE = 16000000; - /// How many samples are contained in a RedBook sector. - const int SAMPLES_PER_SECTOR = 588; - /// Maximum number of samples for a FLAC block. Bigger than 4608 gives no benefit. - const int MAX_FLAKE_BLOCK = 4608; - /// - /// Minimum number of samples for a FLAC block. does not support it to be - /// smaller than 256. - /// - const int MIN_FLAKE_BLOCK = 256; - /// This mask is to check for flags in CompactDisc suffix/prefix DDT - const uint CD_XFIX_MASK = 0xFF000000; - /// This mask is to check for position in CompactDisc suffix/prefix deduplicated block - const uint CD_DFIX_MASK = 0x00FFFFFF; - } + /// Old magic identifier = "DICMFRMT". + const ulong DIC_MAGIC = 0x544D52464D434944; + /// Magic identifier = "AARUFRMT". + const ulong AARU_MAGIC = 0x544D524655524141; + /// + /// Image format version. A change in this number indicates an incompatible change to the format that prevents + /// older implementations from reading it correctly, if at all. + /// + const byte AARUFMT_VERSION_V1 = 1; + /// Adds new index format with 64-bit entries counter + const byte AARUFMT_VERSION = 2; + /// Maximum read cache size, 256MiB. + const uint MAX_CACHE_SIZE = 256 * 1024 * 1024; + /// Size in bytes of LZMA properties. + const int LZMA_PROPERTIES_LENGTH = 5; + /// Maximum number of entries for the DDT cache. + const int MAX_DDT_ENTRY_CACHE = 16000000; + /// How many samples are contained in a RedBook sector. + const int SAMPLES_PER_SECTOR = 588; + /// Maximum number of samples for a FLAC block. Bigger than 4608 gives no benefit. + const int MAX_FLAKE_BLOCK = 4608; + /// + /// Minimum number of samples for a FLAC block. does not support it to be + /// smaller than 256. + /// + const int MIN_FLAKE_BLOCK = 256; + /// This mask is to check for flags in CompactDisc suffix/prefix DDT + const uint CD_XFIX_MASK = 0xFF000000; + /// This mask is to check for position in CompactDisc suffix/prefix deduplicated block + const uint CD_DFIX_MASK = 0x00FFFFFF; } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Enums.cs b/Aaru.Images/AaruFormat/Enums.cs index 03ccbe3e5..264c72868 100644 --- a/Aaru.Images/AaruFormat/Enums.cs +++ b/Aaru.Images/AaruFormat/Enums.cs @@ -32,241 +32,240 @@ // ReSharper disable UnusedMember.Local -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + /// List of known compression types + enum CompressionType : ushort { - /// List of known compression types - enum CompressionType : ushort - { - /// Not compressed - None = 0, - /// LZMA - Lzma = 1, - /// FLAC - Flac = 2, - /// LZMA in Claunia Subchannel Transform processed data - LzmaClauniaSubchannelTransform = 3 - } + /// Not compressed + None = 0, + /// LZMA + Lzma = 1, + /// FLAC + Flac = 2, + /// LZMA in Claunia Subchannel Transform processed data + LzmaClauniaSubchannelTransform = 3 + } - /// List of known data types - enum DataType : ushort - { - /// No data - NoData = 0, - /// User data - UserData = 1, - /// CompactDisc partial Table of Contents - CompactDiscPartialToc = 2, - /// CompactDisc session information - CompactDiscSessionInfo = 3, - /// CompactDisc Table of Contents - CompactDiscToc = 4, - /// CompactDisc Power Management Area - CompactDiscPma = 5, - /// CompactDisc Absolute Time In Pregroove - CompactDiscAtip = 6, - /// CompactDisc Lead-in's CD-Text - CompactDiscLeadInCdText = 7, - /// DVD Physical Format Information - DvdPfi = 8, - /// DVD Lead-in's Copyright Management Information - DvdLeadInCmi = 9, - /// DVD Disc Key - DvdDiscKey = 10, - /// DVD Burst Cutting Area - DvdBca = 11, - /// DVD DMI - DvdDmi = 12, - /// DVD Media Identifier - DvdMediaIdentifier = 13, - /// DVD Media Key Block - DvdMediaKeyBlock = 14, - /// DVD-RAM Disc Definition Structure - DvdRamDds = 15, - /// DVD-RAM Medium Status - DvdRamMediumStatus = 16, - /// DVD-RAM Spare Area Information - DvdRamSpareArea = 17, - /// DVD-R RMD - DvdRRmd = 18, - /// DVD-R Pre-recorded Information - DvdRPrerecordedInfo = 19, - /// DVD-R Media Identifier - DvdRMediaIdentifier = 20, - /// DVD-R Physical Format Information - DvdRPfi = 21, - /// DVD ADress In Pregroove - DvdAdip = 22, - /// HD DVD Copy Protection Information - HdDvdCpi = 23, - /// HD DVD Medium Status - HdDvdMediumStatus = 24, - /// DVD DL Layer Capacity - DvdDlLayerCapacity = 25, - /// DVD DL Middle Zone Address - DvdDlMiddleZoneAddress = 26, - /// DVD DL Jump Interval Size - DvdDlJumpIntervalSize = 27, - /// DVD DL Manual Layer Jump LBA - DvdDlManualLayerJumpLba = 28, - /// Bluray Disc Information - BlurayDi = 29, - /// Bluray Burst Cutting Area - BlurayBca = 30, - /// Bluray Disc Definition Structure - BlurayDds = 31, - /// Bluray Cartridge Status - BlurayCartridgeStatus = 32, - /// Bluray Spare Area Information - BluraySpareArea = 33, - /// AACS Volume Identifier - AacsVolumeIdentifier = 34, - /// AACS Serial Number - AacsSerialNumber = 35, - /// AACS Media Identifier - AacsMediaIdentifier = 36, - /// AACS Media Key Block - AacsMediaKeyBlock = 37, - /// AACS Data Keys - AacsDataKeys = 38, - /// AACS LBA Extents - AacsLbaExtents = 39, - /// CPRM Media Key Block - CprmMediaKeyBlock = 40, - /// Recognized Layers - HybridRecognizedLayers = 41, - /// MMC Write Protection - ScsiMmcWriteProtection = 42, - /// MMC Disc Information - ScsiMmcDiscInformation = 43, - /// MMC Track Resources Information - ScsiMmcTrackResourcesInformation = 44, - /// MMC POW Resources Information - ScsiMmcPowResourcesInformation = 45, - /// SCSI INQUIRY RESPONSE - ScsiInquiry = 46, - /// SCSI MODE PAGE 2Ah - ScsiModePage2A = 47, - /// ATA IDENTIFY response - AtaIdentify = 48, - /// ATAPI IDENTIFY response - AtapiIdentify = 49, - /// PCMCIA CIS - PcmciaCis = 50, - /// SecureDigital CID - SecureDigitalCid = 51, - /// SecureDigital CSD - SecureDigitalCsd = 52, - /// SecureDigital SCR - SecureDigitalScr = 53, - /// SecureDigital OCR - SecureDigitalOcr = 54, - /// MultiMediaCard CID - MultiMediaCardCid = 55, - /// MultiMediaCard CSD - MultiMediaCardCsd = 56, - /// MultiMediaCard OCR - MultiMediaCardOcr = 57, - /// MultiMediaCard Extended CSD - MultiMediaCardExtendedCsd = 58, - /// Xbox Security Sector - XboxSecuritySector = 59, - /// Floppy Lead-out - FloppyLeadOut = 60, - /// Dvd Disc Control Block - DvdDiscControlBlock = 61, - /// CompactDisc First track pregap - CompactDiscFirstTrackPregap = 62, - /// CompactDisc Lead-out - CompactDiscLeadOut = 63, - /// SCSI MODE SENSE (6) response - ScsiModeSense6 = 64, - /// SCSI MODE SENSE (10) response - ScsiModeSense10 = 65, - /// USB descriptors - UsbDescriptors = 66, - /// Xbox DMI - XboxDmi = 67, - /// Xbox Physical Format Information - XboxPfi = 68, - /// CompactDisc sector prefix (sync, header - CdSectorPrefix = 69, - /// CompactDisc sector suffix (edc, ecc p, ecc q) - CdSectorSuffix = 70, - /// CompactDisc subchannel - CdSectorSubchannel = 71, - /// Apple Profile (20 byte) tag - AppleProfileTag = 72, - /// Apple Sony (12 byte) tag - AppleSonyTag = 73, - /// Priam Data Tower (24 byte) tag - PriamDataTowerTag = 74, - /// CompactDisc Media Catalogue Number (as in Lead-in), 13 bytes, ASCII - CompactDiscMediaCatalogueNumber = 75, - /// CompactDisc sector prefix (sync, header), only incorrect stored - CdSectorPrefixCorrected = 76, - /// CompactDisc sector suffix (edc, ecc p, ecc q), only incorrect stored - CdSectorSuffixCorrected = 77, - /// CompactDisc MODE 2 subheader - CompactDiscMode2Subheader = 78, - /// CompactDisc Lead-in - CompactDiscLeadIn = 79, - /// Decrypted DVD Disc Key - DvdDiscKeyDecrypted = 80, - /// DVD CPI_MAI - DvdSectorCpiMai = 81, - /// Decrypted DVD Title Key - DvdSectorTitleKeyDecrypted = 82 - } + /// List of known data types + enum DataType : ushort + { + /// No data + NoData = 0, + /// User data + UserData = 1, + /// CompactDisc partial Table of Contents + CompactDiscPartialToc = 2, + /// CompactDisc session information + CompactDiscSessionInfo = 3, + /// CompactDisc Table of Contents + CompactDiscToc = 4, + /// CompactDisc Power Management Area + CompactDiscPma = 5, + /// CompactDisc Absolute Time In Pregroove + CompactDiscAtip = 6, + /// CompactDisc Lead-in's CD-Text + CompactDiscLeadInCdText = 7, + /// DVD Physical Format Information + DvdPfi = 8, + /// DVD Lead-in's Copyright Management Information + DvdLeadInCmi = 9, + /// DVD Disc Key + DvdDiscKey = 10, + /// DVD Burst Cutting Area + DvdBca = 11, + /// DVD DMI + DvdDmi = 12, + /// DVD Media Identifier + DvdMediaIdentifier = 13, + /// DVD Media Key Block + DvdMediaKeyBlock = 14, + /// DVD-RAM Disc Definition Structure + DvdRamDds = 15, + /// DVD-RAM Medium Status + DvdRamMediumStatus = 16, + /// DVD-RAM Spare Area Information + DvdRamSpareArea = 17, + /// DVD-R RMD + DvdRRmd = 18, + /// DVD-R Pre-recorded Information + DvdRPrerecordedInfo = 19, + /// DVD-R Media Identifier + DvdRMediaIdentifier = 20, + /// DVD-R Physical Format Information + DvdRPfi = 21, + /// DVD ADress In Pregroove + DvdAdip = 22, + /// HD DVD Copy Protection Information + HdDvdCpi = 23, + /// HD DVD Medium Status + HdDvdMediumStatus = 24, + /// DVD DL Layer Capacity + DvdDlLayerCapacity = 25, + /// DVD DL Middle Zone Address + DvdDlMiddleZoneAddress = 26, + /// DVD DL Jump Interval Size + DvdDlJumpIntervalSize = 27, + /// DVD DL Manual Layer Jump LBA + DvdDlManualLayerJumpLba = 28, + /// Bluray Disc Information + BlurayDi = 29, + /// Bluray Burst Cutting Area + BlurayBca = 30, + /// Bluray Disc Definition Structure + BlurayDds = 31, + /// Bluray Cartridge Status + BlurayCartridgeStatus = 32, + /// Bluray Spare Area Information + BluraySpareArea = 33, + /// AACS Volume Identifier + AacsVolumeIdentifier = 34, + /// AACS Serial Number + AacsSerialNumber = 35, + /// AACS Media Identifier + AacsMediaIdentifier = 36, + /// AACS Media Key Block + AacsMediaKeyBlock = 37, + /// AACS Data Keys + AacsDataKeys = 38, + /// AACS LBA Extents + AacsLbaExtents = 39, + /// CPRM Media Key Block + CprmMediaKeyBlock = 40, + /// Recognized Layers + HybridRecognizedLayers = 41, + /// MMC Write Protection + ScsiMmcWriteProtection = 42, + /// MMC Disc Information + ScsiMmcDiscInformation = 43, + /// MMC Track Resources Information + ScsiMmcTrackResourcesInformation = 44, + /// MMC POW Resources Information + ScsiMmcPowResourcesInformation = 45, + /// SCSI INQUIRY RESPONSE + ScsiInquiry = 46, + /// SCSI MODE PAGE 2Ah + ScsiModePage2A = 47, + /// ATA IDENTIFY response + AtaIdentify = 48, + /// ATAPI IDENTIFY response + AtapiIdentify = 49, + /// PCMCIA CIS + PcmciaCis = 50, + /// SecureDigital CID + SecureDigitalCid = 51, + /// SecureDigital CSD + SecureDigitalCsd = 52, + /// SecureDigital SCR + SecureDigitalScr = 53, + /// SecureDigital OCR + SecureDigitalOcr = 54, + /// MultiMediaCard CID + MultiMediaCardCid = 55, + /// MultiMediaCard CSD + MultiMediaCardCsd = 56, + /// MultiMediaCard OCR + MultiMediaCardOcr = 57, + /// MultiMediaCard Extended CSD + MultiMediaCardExtendedCsd = 58, + /// Xbox Security Sector + XboxSecuritySector = 59, + /// Floppy Lead-out + FloppyLeadOut = 60, + /// Dvd Disc Control Block + DvdDiscControlBlock = 61, + /// CompactDisc First track pregap + CompactDiscFirstTrackPregap = 62, + /// CompactDisc Lead-out + CompactDiscLeadOut = 63, + /// SCSI MODE SENSE (6) response + ScsiModeSense6 = 64, + /// SCSI MODE SENSE (10) response + ScsiModeSense10 = 65, + /// USB descriptors + UsbDescriptors = 66, + /// Xbox DMI + XboxDmi = 67, + /// Xbox Physical Format Information + XboxPfi = 68, + /// CompactDisc sector prefix (sync, header + CdSectorPrefix = 69, + /// CompactDisc sector suffix (edc, ecc p, ecc q) + CdSectorSuffix = 70, + /// CompactDisc subchannel + CdSectorSubchannel = 71, + /// Apple Profile (20 byte) tag + AppleProfileTag = 72, + /// Apple Sony (12 byte) tag + AppleSonyTag = 73, + /// Priam Data Tower (24 byte) tag + PriamDataTowerTag = 74, + /// CompactDisc Media Catalogue Number (as in Lead-in), 13 bytes, ASCII + CompactDiscMediaCatalogueNumber = 75, + /// CompactDisc sector prefix (sync, header), only incorrect stored + CdSectorPrefixCorrected = 76, + /// CompactDisc sector suffix (edc, ecc p, ecc q), only incorrect stored + CdSectorSuffixCorrected = 77, + /// CompactDisc MODE 2 subheader + CompactDiscMode2Subheader = 78, + /// CompactDisc Lead-in + CompactDiscLeadIn = 79, + /// Decrypted DVD Disc Key + DvdDiscKeyDecrypted = 80, + /// DVD CPI_MAI + DvdSectorCpiMai = 81, + /// Decrypted DVD Title Key + DvdSectorTitleKeyDecrypted = 82 + } - /// List of known blocks types - enum BlockType : uint - { - /// Block containing data - DataBlock = 0x4B4C4244, - /// Block containing a deduplication table - DeDuplicationTable = 0x2A544444, - /// Block containing the index - Index = 0x58444E49, - /// Block containing the index - Index2 = 0x32584449, - /// Block containing logical geometry - GeometryBlock = 0x4D4F4547, - /// Block containing metadata - MetadataBlock = 0x4154454D, - /// Block containing optical disc tracks - TracksBlock = 0x534B5254, - /// Block containing CICM XML metadata - CicmBlock = 0x4D434943, - /// Block containing contents checksums - ChecksumBlock = 0x4D534B43, - /// TODO: Block containing data position measurements - DataPositionMeasurementBlock = 0x2A4D5044, - /// TODO: Block containing a snapshot index - SnapshotBlock = 0x50414E53, - /// TODO: Block containing how to locate the parent image - ParentBlock = 0x50524E54, - /// Block containing an array of hardware used to create the image - DumpHardwareBlock = 0x2A504D44, - /// Block containing list of files for a tape image - TapeFileBlock = 0x454C4654, - /// Block containing list of partitions for a tape image - TapePartitionBlock = 0x54425054, - /// Block containing list of indexes for Compact Disc tracks - CompactDiscIndexesBlock = 0x58494443 - } + /// List of known blocks types + enum BlockType : uint + { + /// Block containing data + DataBlock = 0x4B4C4244, + /// Block containing a deduplication table + DeDuplicationTable = 0x2A544444, + /// Block containing the index + Index = 0x58444E49, + /// Block containing the index + Index2 = 0x32584449, + /// Block containing logical geometry + GeometryBlock = 0x4D4F4547, + /// Block containing metadata + MetadataBlock = 0x4154454D, + /// Block containing optical disc tracks + TracksBlock = 0x534B5254, + /// Block containing CICM XML metadata + CicmBlock = 0x4D434943, + /// Block containing contents checksums + ChecksumBlock = 0x4D534B43, + /// TODO: Block containing data position measurements + DataPositionMeasurementBlock = 0x2A4D5044, + /// TODO: Block containing a snapshot index + SnapshotBlock = 0x50414E53, + /// TODO: Block containing how to locate the parent image + ParentBlock = 0x50524E54, + /// Block containing an array of hardware used to create the image + DumpHardwareBlock = 0x2A504D44, + /// Block containing list of files for a tape image + TapeFileBlock = 0x454C4654, + /// Block containing list of partitions for a tape image + TapePartitionBlock = 0x54425054, + /// Block containing list of indexes for Compact Disc tracks + CompactDiscIndexesBlock = 0x58494443 + } - enum ChecksumAlgorithm : byte - { - Invalid = 0, Md5 = 1, Sha1 = 2, - Sha256 = 3, SpamSum = 4 - } + enum ChecksumAlgorithm : byte + { + Invalid = 0, Md5 = 1, Sha1 = 2, + Sha256 = 3, SpamSum = 4 + } - enum CdFixFlags : uint - { - NotDumped = 0x10000000, Correct = 0x20000000, Mode2Form1Ok = 0x30000000, - Mode2Form2Ok = 0x40000000, Mode2Form2NoCrc = 0x50000000 - } + enum CdFixFlags : uint + { + NotDumped = 0x10000000, Correct = 0x20000000, Mode2Form1Ok = 0x30000000, + Mode2Form2Ok = 0x40000000, Mode2Form2NoCrc = 0x50000000 } } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Helpers.cs b/Aaru.Images/AaruFormat/Helpers.cs index 4b2ccce07..447829125 100644 --- a/Aaru.Images/AaruFormat/Helpers.cs +++ b/Aaru.Images/AaruFormat/Helpers.cs @@ -38,410 +38,409 @@ using Aaru.CommonTypes.Structs.Devices.SCSI; using Aaru.Decoders.SecureDigital; using Aaru.Helpers; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + /// Checks for media tags that may contain metadata and sets it up if not already set + void SetMetadataFromTags() { - /// Checks for media tags that may contain metadata and sets it up if not already set - void SetMetadataFromTags() + // Search for SecureDigital CID + if(_mediaTags.TryGetValue(MediaTagType.SD_CID, out byte[] sdCid)) { - // Search for SecureDigital CID - if(_mediaTags.TryGetValue(MediaTagType.SD_CID, out byte[] sdCid)) - { - CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(sdCid); + CID decoded = Decoders.SecureDigital.Decoders.DecodeCID(sdCid); - if(string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer)) - _imageInfo.DriveManufacturer = VendorString.Prettify(decoded.Manufacturer); + if(string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer)) + _imageInfo.DriveManufacturer = VendorString.Prettify(decoded.Manufacturer); - if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) - _imageInfo.DriveModel = decoded.ProductName; - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveFirmwareRevision)) - _imageInfo.DriveFirmwareRevision = - $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveSerialNumber)) - _imageInfo.DriveSerialNumber = $"{decoded.ProductSerialNumber}"; - } - - // Search for MultiMediaCard CID - if(_mediaTags.TryGetValue(MediaTagType.MMC_CID, out byte[] mmcCid)) - { - Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(mmcCid); - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer)) - _imageInfo.DriveManufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) - _imageInfo.DriveModel = decoded.ProductName; - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveFirmwareRevision)) - _imageInfo.DriveFirmwareRevision = - $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveSerialNumber)) - _imageInfo.DriveSerialNumber = $"{decoded.ProductSerialNumber}"; - } - - // Search for SCSI INQUIRY - if(_mediaTags.TryGetValue(MediaTagType.SCSI_INQUIRY, out byte[] scsiInquiry)) - { - Inquiry? nullableInquiry = Inquiry.Decode(scsiInquiry); - - if(nullableInquiry.HasValue) - { - Inquiry inquiry = nullableInquiry.Value; - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer)) - _imageInfo.DriveManufacturer = StringHandlers.CToString(inquiry.VendorIdentification)?.Trim(); - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) - _imageInfo.DriveModel = StringHandlers.CToString(inquiry.ProductIdentification)?.Trim(); - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveFirmwareRevision)) - _imageInfo.DriveFirmwareRevision = - StringHandlers.CToString(inquiry.ProductRevisionLevel)?.Trim(); - } - } - - // Search for ATA or ATAPI IDENTIFY - if(!_mediaTags.TryGetValue(MediaTagType.ATA_IDENTIFY, out byte[] ataIdentify) && - !_mediaTags.TryGetValue(MediaTagType.ATAPI_IDENTIFY, out ataIdentify)) - return; - - Identify.IdentifyDevice? nullableIdentify = CommonTypes.Structs.Devices.ATA.Identify.Decode(ataIdentify); - - if(!nullableIdentify.HasValue) - return; - - Identify.IdentifyDevice identify = nullableIdentify.Value; - - string[] separated = identify.Model.Split(' '); - - if(separated.Length == 1) - if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) - _imageInfo.DriveModel = separated[0]; - else - { - if(string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer)) - _imageInfo.DriveManufacturer = separated[0]; - - if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) - _imageInfo.DriveModel = separated[^1]; - } + if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) + _imageInfo.DriveModel = decoded.ProductName; if(string.IsNullOrWhiteSpace(_imageInfo.DriveFirmwareRevision)) - _imageInfo.DriveFirmwareRevision = identify.FirmwareRevision; + _imageInfo.DriveFirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; if(string.IsNullOrWhiteSpace(_imageInfo.DriveSerialNumber)) - _imageInfo.DriveSerialNumber = identify.SerialNumber; + _imageInfo.DriveSerialNumber = $"{decoded.ProductSerialNumber}"; } - // Get the CICM XML media type from Aaru media type - static XmlMediaType GetXmlMediaType(MediaType type) + // Search for MultiMediaCard CID + if(_mediaTags.TryGetValue(MediaTagType.MMC_CID, out byte[] mmcCid)) { - switch(type) + Decoders.MMC.CID decoded = Decoders.MMC.Decoders.DecodeCID(mmcCid); + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer)) + _imageInfo.DriveManufacturer = Decoders.MMC.VendorString.Prettify(decoded.Manufacturer); + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) + _imageInfo.DriveModel = decoded.ProductName; + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveFirmwareRevision)) + _imageInfo.DriveFirmwareRevision = + $"{(decoded.ProductRevision & 0xF0) >> 4:X2}.{decoded.ProductRevision & 0x0F:X2}"; + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveSerialNumber)) + _imageInfo.DriveSerialNumber = $"{decoded.ProductSerialNumber}"; + } + + // Search for SCSI INQUIRY + if(_mediaTags.TryGetValue(MediaTagType.SCSI_INQUIRY, out byte[] scsiInquiry)) + { + Inquiry? nullableInquiry = Inquiry.Decode(scsiInquiry); + + if(nullableInquiry.HasValue) { - case MediaType.CD: - case MediaType.CDDA: - case MediaType.CDG: - case MediaType.CDEG: - case MediaType.CDI: - case MediaType.CDIREADY: - case MediaType.CDROM: - case MediaType.CDROMXA: - case MediaType.CDPLUS: - case MediaType.CDMO: - case MediaType.CDR: - case MediaType.CDRW: - case MediaType.CDMRW: - case MediaType.VCD: - case MediaType.SVCD: - case MediaType.PCD: - case MediaType.SACD: - case MediaType.DDCD: - case MediaType.DDCDR: - case MediaType.DDCDRW: - case MediaType.DTSCD: - case MediaType.CDMIDI: - case MediaType.CDV: - case MediaType.DVDROM: - case MediaType.DVDR: - case MediaType.DVDRW: - case MediaType.DVDPR: - case MediaType.DVDPRW: - case MediaType.DVDPRWDL: - case MediaType.DVDRDL: - case MediaType.DVDPRDL: - case MediaType.DVDRAM: - case MediaType.DVDRWDL: - case MediaType.DVDDownload: - case MediaType.HDDVDROM: - case MediaType.HDDVDRAM: - case MediaType.HDDVDR: - case MediaType.HDDVDRW: - case MediaType.HDDVDRDL: - case MediaType.HDDVDRWDL: - case MediaType.BDROM: - case MediaType.UHDBD: - case MediaType.BDR: - case MediaType.BDRE: - case MediaType.BDRXL: - case MediaType.BDREXL: - case MediaType.EVD: - case MediaType.FVD: - case MediaType.HVD: - case MediaType.CBHD: - case MediaType.HDVMD: - case MediaType.VCDHD: - case MediaType.SVOD: - case MediaType.FDDVD: - case MediaType.LD: - case MediaType.LDROM: - case MediaType.CRVdisc: - case MediaType.LDROM2: - case MediaType.LVROM: - case MediaType.MegaLD: - case MediaType.PS1CD: - case MediaType.PS2CD: - case MediaType.PS2DVD: - case MediaType.PS3DVD: - case MediaType.PS3BD: - case MediaType.PS4BD: - case MediaType.PS5BD: - case MediaType.UMD: - case MediaType.XGD: - case MediaType.XGD2: - case MediaType.XGD3: - case MediaType.XGD4: - case MediaType.MEGACD: - case MediaType.SATURNCD: - case MediaType.GDROM: - case MediaType.GDR: - case MediaType.SuperCDROM2: - case MediaType.JaguarCD: - case MediaType.ThreeDO: - case MediaType.PCFX: - case MediaType.NeoGeoCD: - case MediaType.GOD: - case MediaType.WOD: - case MediaType.WUOD: - case MediaType.CDTV: - case MediaType.CD32: - case MediaType.Nuon: - case MediaType.Playdia: - case MediaType.Pippin: - case MediaType.FMTOWNS: - case MediaType.MilCD: - case MediaType.VideoNow: - case MediaType.VideoNowColor: - case MediaType.VideoNowXp: - case MediaType.CVD: return XmlMediaType.OpticalDisc; - default: return XmlMediaType.BlockMedia; + Inquiry inquiry = nullableInquiry.Value; + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer)) + _imageInfo.DriveManufacturer = StringHandlers.CToString(inquiry.VendorIdentification)?.Trim(); + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) + _imageInfo.DriveModel = StringHandlers.CToString(inquiry.ProductIdentification)?.Trim(); + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveFirmwareRevision)) + _imageInfo.DriveFirmwareRevision = + StringHandlers.CToString(inquiry.ProductRevisionLevel)?.Trim(); } } - // Gets a DDT entry - ulong GetDdtEntry(ulong sectorAddress) + // Search for ATA or ATAPI IDENTIFY + if(!_mediaTags.TryGetValue(MediaTagType.ATA_IDENTIFY, out byte[] ataIdentify) && + !_mediaTags.TryGetValue(MediaTagType.ATAPI_IDENTIFY, out ataIdentify)) + return; + + Identify.IdentifyDevice? nullableIdentify = CommonTypes.Structs.Devices.ATA.Identify.Decode(ataIdentify); + + if(!nullableIdentify.HasValue) + return; + + Identify.IdentifyDevice identify = nullableIdentify.Value; + + string[] separated = identify.Model.Split(' '); + + if(separated.Length == 1) + if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) + _imageInfo.DriveModel = separated[0]; + else + { + if(string.IsNullOrWhiteSpace(_imageInfo.DriveManufacturer)) + _imageInfo.DriveManufacturer = separated[0]; + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveModel)) + _imageInfo.DriveModel = separated[^1]; + } + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveFirmwareRevision)) + _imageInfo.DriveFirmwareRevision = identify.FirmwareRevision; + + if(string.IsNullOrWhiteSpace(_imageInfo.DriveSerialNumber)) + _imageInfo.DriveSerialNumber = identify.SerialNumber; + } + + // Get the CICM XML media type from Aaru media type + static XmlMediaType GetXmlMediaType(MediaType type) + { + switch(type) { - if(_inMemoryDdt) - return _userDataDdt[sectorAddress]; + case MediaType.CD: + case MediaType.CDDA: + case MediaType.CDG: + case MediaType.CDEG: + case MediaType.CDI: + case MediaType.CDIREADY: + case MediaType.CDROM: + case MediaType.CDROMXA: + case MediaType.CDPLUS: + case MediaType.CDMO: + case MediaType.CDR: + case MediaType.CDRW: + case MediaType.CDMRW: + case MediaType.VCD: + case MediaType.SVCD: + case MediaType.PCD: + case MediaType.SACD: + case MediaType.DDCD: + case MediaType.DDCDR: + case MediaType.DDCDRW: + case MediaType.DTSCD: + case MediaType.CDMIDI: + case MediaType.CDV: + case MediaType.DVDROM: + case MediaType.DVDR: + case MediaType.DVDRW: + case MediaType.DVDPR: + case MediaType.DVDPRW: + case MediaType.DVDPRWDL: + case MediaType.DVDRDL: + case MediaType.DVDPRDL: + case MediaType.DVDRAM: + case MediaType.DVDRWDL: + case MediaType.DVDDownload: + case MediaType.HDDVDROM: + case MediaType.HDDVDRAM: + case MediaType.HDDVDR: + case MediaType.HDDVDRW: + case MediaType.HDDVDRDL: + case MediaType.HDDVDRWDL: + case MediaType.BDROM: + case MediaType.UHDBD: + case MediaType.BDR: + case MediaType.BDRE: + case MediaType.BDRXL: + case MediaType.BDREXL: + case MediaType.EVD: + case MediaType.FVD: + case MediaType.HVD: + case MediaType.CBHD: + case MediaType.HDVMD: + case MediaType.VCDHD: + case MediaType.SVOD: + case MediaType.FDDVD: + case MediaType.LD: + case MediaType.LDROM: + case MediaType.CRVdisc: + case MediaType.LDROM2: + case MediaType.LVROM: + case MediaType.MegaLD: + case MediaType.PS1CD: + case MediaType.PS2CD: + case MediaType.PS2DVD: + case MediaType.PS3DVD: + case MediaType.PS3BD: + case MediaType.PS4BD: + case MediaType.PS5BD: + case MediaType.UMD: + case MediaType.XGD: + case MediaType.XGD2: + case MediaType.XGD3: + case MediaType.XGD4: + case MediaType.MEGACD: + case MediaType.SATURNCD: + case MediaType.GDROM: + case MediaType.GDR: + case MediaType.SuperCDROM2: + case MediaType.JaguarCD: + case MediaType.ThreeDO: + case MediaType.PCFX: + case MediaType.NeoGeoCD: + case MediaType.GOD: + case MediaType.WOD: + case MediaType.WUOD: + case MediaType.CDTV: + case MediaType.CD32: + case MediaType.Nuon: + case MediaType.Playdia: + case MediaType.Pippin: + case MediaType.FMTOWNS: + case MediaType.MilCD: + case MediaType.VideoNow: + case MediaType.VideoNowColor: + case MediaType.VideoNowXp: + case MediaType.CVD: return XmlMediaType.OpticalDisc; + default: return XmlMediaType.BlockMedia; + } + } - if(_ddtEntryCache.TryGetValue(sectorAddress, out ulong entry)) - return entry; - - long oldPosition = _imageStream.Position; - _imageStream.Position = _outMemoryDdtPosition + Marshal.SizeOf(); - _imageStream.Position += (long)(sectorAddress * sizeof(ulong)); - byte[] temp = new byte[sizeof(ulong)]; - _imageStream.Read(temp, 0, sizeof(ulong)); - _imageStream.Position = oldPosition; - entry = BitConverter.ToUInt64(temp, 0); - - if(_ddtEntryCache.Count >= MAX_DDT_ENTRY_CACHE) - _ddtEntryCache.Clear(); - - _ddtEntryCache.Add(sectorAddress, entry); + // Gets a DDT entry + ulong GetDdtEntry(ulong sectorAddress) + { + if(_inMemoryDdt) + return _userDataDdt[sectorAddress]; + if(_ddtEntryCache.TryGetValue(sectorAddress, out ulong entry)) return entry; + + long oldPosition = _imageStream.Position; + _imageStream.Position = _outMemoryDdtPosition + Marshal.SizeOf(); + _imageStream.Position += (long)(sectorAddress * sizeof(ulong)); + byte[] temp = new byte[sizeof(ulong)]; + _imageStream.Read(temp, 0, sizeof(ulong)); + _imageStream.Position = oldPosition; + entry = BitConverter.ToUInt64(temp, 0); + + if(_ddtEntryCache.Count >= MAX_DDT_ENTRY_CACHE) + _ddtEntryCache.Clear(); + + _ddtEntryCache.Add(sectorAddress, entry); + + return entry; + } + + // Sets a DDT entry + void SetDdtEntry(ulong sectorAddress, ulong pointer) + { + if(_inMemoryDdt) + { + if(IsTape) + _tapeDdt[sectorAddress] = pointer; + else + _userDataDdt[sectorAddress] = pointer; + + return; } - // Sets a DDT entry - void SetDdtEntry(ulong sectorAddress, ulong pointer) + long oldPosition = _imageStream.Position; + _imageStream.Position = _outMemoryDdtPosition + Marshal.SizeOf(); + _imageStream.Position += (long)(sectorAddress * sizeof(ulong)); + _imageStream.Write(BitConverter.GetBytes(pointer), 0, sizeof(ulong)); + _imageStream.Position = oldPosition; + } + + // Converts between image data type and Aaru media tag type + static MediaTagType GetMediaTagTypeForDataType(DataType type) + { + switch(type) { - if(_inMemoryDdt) - { - if(IsTape) - _tapeDdt[sectorAddress] = pointer; - else - _userDataDdt[sectorAddress] = pointer; - - return; - } - - long oldPosition = _imageStream.Position; - _imageStream.Position = _outMemoryDdtPosition + Marshal.SizeOf(); - _imageStream.Position += (long)(sectorAddress * sizeof(ulong)); - _imageStream.Write(BitConverter.GetBytes(pointer), 0, sizeof(ulong)); - _imageStream.Position = oldPosition; + case DataType.CompactDiscPartialToc: return MediaTagType.CD_TOC; + case DataType.CompactDiscSessionInfo: return MediaTagType.CD_SessionInfo; + case DataType.CompactDiscToc: return MediaTagType.CD_FullTOC; + case DataType.CompactDiscPma: return MediaTagType.CD_PMA; + case DataType.CompactDiscAtip: return MediaTagType.CD_ATIP; + case DataType.CompactDiscLeadInCdText: return MediaTagType.CD_TEXT; + case DataType.DvdPfi: return MediaTagType.DVD_PFI; + case DataType.DvdLeadInCmi: return MediaTagType.DVD_CMI; + case DataType.DvdDiscKey: return MediaTagType.DVD_DiscKey; + case DataType.DvdBca: return MediaTagType.DVD_BCA; + case DataType.DvdDmi: return MediaTagType.DVD_DMI; + case DataType.DvdMediaIdentifier: return MediaTagType.DVD_MediaIdentifier; + case DataType.DvdMediaKeyBlock: return MediaTagType.DVD_MKB; + case DataType.DvdRamDds: return MediaTagType.DVDRAM_DDS; + case DataType.DvdRamMediumStatus: return MediaTagType.DVDRAM_MediumStatus; + case DataType.DvdRamSpareArea: return MediaTagType.DVDRAM_SpareArea; + case DataType.DvdRRmd: return MediaTagType.DVDR_RMD; + case DataType.DvdRPrerecordedInfo: return MediaTagType.DVDR_PreRecordedInfo; + case DataType.DvdRMediaIdentifier: return MediaTagType.DVDR_MediaIdentifier; + case DataType.DvdRPfi: return MediaTagType.DVDR_PFI; + case DataType.DvdAdip: return MediaTagType.DVD_ADIP; + case DataType.HdDvdCpi: return MediaTagType.HDDVD_CPI; + case DataType.HdDvdMediumStatus: return MediaTagType.HDDVD_MediumStatus; + case DataType.DvdDlLayerCapacity: return MediaTagType.DVDDL_LayerCapacity; + case DataType.DvdDlMiddleZoneAddress: return MediaTagType.DVDDL_MiddleZoneAddress; + case DataType.DvdDlJumpIntervalSize: return MediaTagType.DVDDL_JumpIntervalSize; + case DataType.DvdDlManualLayerJumpLba: return MediaTagType.DVDDL_ManualLayerJumpLBA; + case DataType.BlurayDi: return MediaTagType.BD_DI; + case DataType.BlurayBca: return MediaTagType.BD_BCA; + case DataType.BlurayDds: return MediaTagType.BD_DDS; + case DataType.BlurayCartridgeStatus: return MediaTagType.BD_CartridgeStatus; + case DataType.BluraySpareArea: return MediaTagType.BD_SpareArea; + case DataType.AacsVolumeIdentifier: return MediaTagType.AACS_VolumeIdentifier; + case DataType.AacsSerialNumber: return MediaTagType.AACS_SerialNumber; + case DataType.AacsMediaIdentifier: return MediaTagType.AACS_MediaIdentifier; + case DataType.AacsMediaKeyBlock: return MediaTagType.AACS_MKB; + case DataType.AacsDataKeys: return MediaTagType.AACS_DataKeys; + case DataType.AacsLbaExtents: return MediaTagType.AACS_LBAExtents; + case DataType.CprmMediaKeyBlock: return MediaTagType.AACS_CPRM_MKB; + case DataType.HybridRecognizedLayers: return MediaTagType.Hybrid_RecognizedLayers; + case DataType.ScsiMmcWriteProtection: return MediaTagType.MMC_WriteProtection; + case DataType.ScsiMmcDiscInformation: return MediaTagType.MMC_DiscInformation; + case DataType.ScsiMmcTrackResourcesInformation: return MediaTagType.MMC_TrackResourcesInformation; + case DataType.ScsiMmcPowResourcesInformation: return MediaTagType.MMC_POWResourcesInformation; + case DataType.ScsiInquiry: return MediaTagType.SCSI_INQUIRY; + case DataType.ScsiModePage2A: return MediaTagType.SCSI_MODEPAGE_2A; + case DataType.AtaIdentify: return MediaTagType.ATA_IDENTIFY; + case DataType.AtapiIdentify: return MediaTagType.ATAPI_IDENTIFY; + case DataType.PcmciaCis: return MediaTagType.PCMCIA_CIS; + case DataType.SecureDigitalCid: return MediaTagType.SD_CID; + case DataType.SecureDigitalCsd: return MediaTagType.SD_CSD; + case DataType.SecureDigitalScr: return MediaTagType.SD_SCR; + case DataType.SecureDigitalOcr: return MediaTagType.SD_OCR; + case DataType.MultiMediaCardCid: return MediaTagType.MMC_CID; + case DataType.MultiMediaCardCsd: return MediaTagType.MMC_CSD; + case DataType.MultiMediaCardOcr: return MediaTagType.MMC_OCR; + case DataType.MultiMediaCardExtendedCsd: return MediaTagType.MMC_ExtendedCSD; + case DataType.XboxSecuritySector: return MediaTagType.Xbox_SecuritySector; + case DataType.FloppyLeadOut: return MediaTagType.Floppy_LeadOut; + case DataType.DvdDiscControlBlock: return MediaTagType.DCB; + case DataType.CompactDiscFirstTrackPregap: return MediaTagType.CD_FirstTrackPregap; + case DataType.CompactDiscLeadOut: return MediaTagType.CD_LeadOut; + case DataType.ScsiModeSense6: return MediaTagType.SCSI_MODESENSE_6; + case DataType.ScsiModeSense10: return MediaTagType.SCSI_MODESENSE_10; + case DataType.UsbDescriptors: return MediaTagType.USB_Descriptors; + case DataType.XboxDmi: return MediaTagType.Xbox_DMI; + case DataType.XboxPfi: return MediaTagType.Xbox_PFI; + case DataType.CompactDiscMediaCatalogueNumber: return MediaTagType.CD_MCN; + case DataType.CompactDiscLeadIn: return MediaTagType.CD_LeadIn; + case DataType.DvdDiscKeyDecrypted: return MediaTagType.DVD_DiscKey_Decrypted; + default: throw new ArgumentOutOfRangeException(); } + } - // Converts between image data type and Aaru media tag type - static MediaTagType GetMediaTagTypeForDataType(DataType type) + // Converts between Aaru media tag type and image data type + static DataType GetDataTypeForMediaTag(MediaTagType tag) + { + switch(tag) { - switch(type) - { - case DataType.CompactDiscPartialToc: return MediaTagType.CD_TOC; - case DataType.CompactDiscSessionInfo: return MediaTagType.CD_SessionInfo; - case DataType.CompactDiscToc: return MediaTagType.CD_FullTOC; - case DataType.CompactDiscPma: return MediaTagType.CD_PMA; - case DataType.CompactDiscAtip: return MediaTagType.CD_ATIP; - case DataType.CompactDiscLeadInCdText: return MediaTagType.CD_TEXT; - case DataType.DvdPfi: return MediaTagType.DVD_PFI; - case DataType.DvdLeadInCmi: return MediaTagType.DVD_CMI; - case DataType.DvdDiscKey: return MediaTagType.DVD_DiscKey; - case DataType.DvdBca: return MediaTagType.DVD_BCA; - case DataType.DvdDmi: return MediaTagType.DVD_DMI; - case DataType.DvdMediaIdentifier: return MediaTagType.DVD_MediaIdentifier; - case DataType.DvdMediaKeyBlock: return MediaTagType.DVD_MKB; - case DataType.DvdRamDds: return MediaTagType.DVDRAM_DDS; - case DataType.DvdRamMediumStatus: return MediaTagType.DVDRAM_MediumStatus; - case DataType.DvdRamSpareArea: return MediaTagType.DVDRAM_SpareArea; - case DataType.DvdRRmd: return MediaTagType.DVDR_RMD; - case DataType.DvdRPrerecordedInfo: return MediaTagType.DVDR_PreRecordedInfo; - case DataType.DvdRMediaIdentifier: return MediaTagType.DVDR_MediaIdentifier; - case DataType.DvdRPfi: return MediaTagType.DVDR_PFI; - case DataType.DvdAdip: return MediaTagType.DVD_ADIP; - case DataType.HdDvdCpi: return MediaTagType.HDDVD_CPI; - case DataType.HdDvdMediumStatus: return MediaTagType.HDDVD_MediumStatus; - case DataType.DvdDlLayerCapacity: return MediaTagType.DVDDL_LayerCapacity; - case DataType.DvdDlMiddleZoneAddress: return MediaTagType.DVDDL_MiddleZoneAddress; - case DataType.DvdDlJumpIntervalSize: return MediaTagType.DVDDL_JumpIntervalSize; - case DataType.DvdDlManualLayerJumpLba: return MediaTagType.DVDDL_ManualLayerJumpLBA; - case DataType.BlurayDi: return MediaTagType.BD_DI; - case DataType.BlurayBca: return MediaTagType.BD_BCA; - case DataType.BlurayDds: return MediaTagType.BD_DDS; - case DataType.BlurayCartridgeStatus: return MediaTagType.BD_CartridgeStatus; - case DataType.BluraySpareArea: return MediaTagType.BD_SpareArea; - case DataType.AacsVolumeIdentifier: return MediaTagType.AACS_VolumeIdentifier; - case DataType.AacsSerialNumber: return MediaTagType.AACS_SerialNumber; - case DataType.AacsMediaIdentifier: return MediaTagType.AACS_MediaIdentifier; - case DataType.AacsMediaKeyBlock: return MediaTagType.AACS_MKB; - case DataType.AacsDataKeys: return MediaTagType.AACS_DataKeys; - case DataType.AacsLbaExtents: return MediaTagType.AACS_LBAExtents; - case DataType.CprmMediaKeyBlock: return MediaTagType.AACS_CPRM_MKB; - case DataType.HybridRecognizedLayers: return MediaTagType.Hybrid_RecognizedLayers; - case DataType.ScsiMmcWriteProtection: return MediaTagType.MMC_WriteProtection; - case DataType.ScsiMmcDiscInformation: return MediaTagType.MMC_DiscInformation; - case DataType.ScsiMmcTrackResourcesInformation: return MediaTagType.MMC_TrackResourcesInformation; - case DataType.ScsiMmcPowResourcesInformation: return MediaTagType.MMC_POWResourcesInformation; - case DataType.ScsiInquiry: return MediaTagType.SCSI_INQUIRY; - case DataType.ScsiModePage2A: return MediaTagType.SCSI_MODEPAGE_2A; - case DataType.AtaIdentify: return MediaTagType.ATA_IDENTIFY; - case DataType.AtapiIdentify: return MediaTagType.ATAPI_IDENTIFY; - case DataType.PcmciaCis: return MediaTagType.PCMCIA_CIS; - case DataType.SecureDigitalCid: return MediaTagType.SD_CID; - case DataType.SecureDigitalCsd: return MediaTagType.SD_CSD; - case DataType.SecureDigitalScr: return MediaTagType.SD_SCR; - case DataType.SecureDigitalOcr: return MediaTagType.SD_OCR; - case DataType.MultiMediaCardCid: return MediaTagType.MMC_CID; - case DataType.MultiMediaCardCsd: return MediaTagType.MMC_CSD; - case DataType.MultiMediaCardOcr: return MediaTagType.MMC_OCR; - case DataType.MultiMediaCardExtendedCsd: return MediaTagType.MMC_ExtendedCSD; - case DataType.XboxSecuritySector: return MediaTagType.Xbox_SecuritySector; - case DataType.FloppyLeadOut: return MediaTagType.Floppy_LeadOut; - case DataType.DvdDiscControlBlock: return MediaTagType.DCB; - case DataType.CompactDiscFirstTrackPregap: return MediaTagType.CD_FirstTrackPregap; - case DataType.CompactDiscLeadOut: return MediaTagType.CD_LeadOut; - case DataType.ScsiModeSense6: return MediaTagType.SCSI_MODESENSE_6; - case DataType.ScsiModeSense10: return MediaTagType.SCSI_MODESENSE_10; - case DataType.UsbDescriptors: return MediaTagType.USB_Descriptors; - case DataType.XboxDmi: return MediaTagType.Xbox_DMI; - case DataType.XboxPfi: return MediaTagType.Xbox_PFI; - case DataType.CompactDiscMediaCatalogueNumber: return MediaTagType.CD_MCN; - case DataType.CompactDiscLeadIn: return MediaTagType.CD_LeadIn; - case DataType.DvdDiscKeyDecrypted: return MediaTagType.DVD_DiscKey_Decrypted; - default: throw new ArgumentOutOfRangeException(); - } - } - - // Converts between Aaru media tag type and image data type - static DataType GetDataTypeForMediaTag(MediaTagType tag) - { - switch(tag) - { - case MediaTagType.CD_TOC: return DataType.CompactDiscPartialToc; - case MediaTagType.CD_SessionInfo: return DataType.CompactDiscSessionInfo; - case MediaTagType.CD_FullTOC: return DataType.CompactDiscToc; - case MediaTagType.CD_PMA: return DataType.CompactDiscPma; - case MediaTagType.CD_ATIP: return DataType.CompactDiscAtip; - case MediaTagType.CD_TEXT: return DataType.CompactDiscLeadInCdText; - case MediaTagType.DVD_PFI: return DataType.DvdPfi; - case MediaTagType.DVD_CMI: return DataType.DvdLeadInCmi; - case MediaTagType.DVD_DiscKey: return DataType.DvdDiscKey; - case MediaTagType.DVD_BCA: return DataType.DvdBca; - case MediaTagType.DVD_DMI: return DataType.DvdDmi; - case MediaTagType.DVD_MediaIdentifier: return DataType.DvdMediaIdentifier; - case MediaTagType.DVD_MKB: return DataType.DvdMediaKeyBlock; - case MediaTagType.DVDRAM_DDS: return DataType.DvdRamDds; - case MediaTagType.DVDRAM_MediumStatus: return DataType.DvdRamMediumStatus; - case MediaTagType.DVDRAM_SpareArea: return DataType.DvdRamSpareArea; - case MediaTagType.DVDR_RMD: return DataType.DvdRRmd; - case MediaTagType.DVDR_PreRecordedInfo: return DataType.DvdRPrerecordedInfo; - case MediaTagType.DVDR_MediaIdentifier: return DataType.DvdRMediaIdentifier; - case MediaTagType.DVDR_PFI: return DataType.DvdRPfi; - case MediaTagType.DVD_ADIP: return DataType.DvdAdip; - case MediaTagType.HDDVD_CPI: return DataType.HdDvdCpi; - case MediaTagType.HDDVD_MediumStatus: return DataType.HdDvdMediumStatus; - case MediaTagType.DVDDL_LayerCapacity: return DataType.DvdDlLayerCapacity; - case MediaTagType.DVDDL_MiddleZoneAddress: return DataType.DvdDlMiddleZoneAddress; - case MediaTagType.DVDDL_JumpIntervalSize: return DataType.DvdDlJumpIntervalSize; - case MediaTagType.DVDDL_ManualLayerJumpLBA: return DataType.DvdDlManualLayerJumpLba; - case MediaTagType.BD_DI: return DataType.BlurayDi; - case MediaTagType.BD_BCA: return DataType.BlurayBca; - case MediaTagType.BD_DDS: return DataType.BlurayDds; - case MediaTagType.BD_CartridgeStatus: return DataType.BlurayCartridgeStatus; - case MediaTagType.BD_SpareArea: return DataType.BluraySpareArea; - case MediaTagType.AACS_VolumeIdentifier: return DataType.AacsVolumeIdentifier; - case MediaTagType.AACS_SerialNumber: return DataType.AacsSerialNumber; - case MediaTagType.AACS_MediaIdentifier: return DataType.AacsMediaIdentifier; - case MediaTagType.AACS_MKB: return DataType.AacsMediaKeyBlock; - case MediaTagType.AACS_DataKeys: return DataType.AacsDataKeys; - case MediaTagType.AACS_LBAExtents: return DataType.AacsLbaExtents; - case MediaTagType.AACS_CPRM_MKB: return DataType.CprmMediaKeyBlock; - case MediaTagType.Hybrid_RecognizedLayers: return DataType.HybridRecognizedLayers; - case MediaTagType.MMC_WriteProtection: return DataType.ScsiMmcWriteProtection; - case MediaTagType.MMC_DiscInformation: return DataType.ScsiMmcDiscInformation; - case MediaTagType.MMC_TrackResourcesInformation: return DataType.ScsiMmcTrackResourcesInformation; - case MediaTagType.MMC_POWResourcesInformation: return DataType.ScsiMmcPowResourcesInformation; - case MediaTagType.SCSI_INQUIRY: return DataType.ScsiInquiry; - case MediaTagType.SCSI_MODEPAGE_2A: return DataType.ScsiModePage2A; - case MediaTagType.ATA_IDENTIFY: return DataType.AtaIdentify; - case MediaTagType.ATAPI_IDENTIFY: return DataType.AtapiIdentify; - case MediaTagType.PCMCIA_CIS: return DataType.PcmciaCis; - case MediaTagType.SD_CID: return DataType.SecureDigitalCid; - case MediaTagType.SD_CSD: return DataType.SecureDigitalCsd; - case MediaTagType.SD_SCR: return DataType.SecureDigitalScr; - case MediaTagType.SD_OCR: return DataType.SecureDigitalOcr; - case MediaTagType.MMC_CID: return DataType.MultiMediaCardCid; - case MediaTagType.MMC_CSD: return DataType.MultiMediaCardCsd; - case MediaTagType.MMC_OCR: return DataType.MultiMediaCardOcr; - case MediaTagType.MMC_ExtendedCSD: return DataType.MultiMediaCardExtendedCsd; - case MediaTagType.Xbox_SecuritySector: return DataType.XboxSecuritySector; - case MediaTagType.Floppy_LeadOut: return DataType.FloppyLeadOut; - case MediaTagType.DCB: return DataType.DvdDiscControlBlock; - case MediaTagType.CD_FirstTrackPregap: return DataType.CompactDiscFirstTrackPregap; - case MediaTagType.CD_LeadOut: return DataType.CompactDiscLeadOut; - case MediaTagType.SCSI_MODESENSE_6: return DataType.ScsiModeSense6; - case MediaTagType.SCSI_MODESENSE_10: return DataType.ScsiModeSense10; - case MediaTagType.USB_Descriptors: return DataType.UsbDescriptors; - case MediaTagType.Xbox_DMI: return DataType.XboxDmi; - case MediaTagType.Xbox_PFI: return DataType.XboxPfi; - case MediaTagType.CD_MCN: return DataType.CompactDiscMediaCatalogueNumber; - case MediaTagType.CD_LeadIn: return DataType.CompactDiscLeadIn; - case MediaTagType.DVD_DiscKey_Decrypted: return DataType.DvdDiscKeyDecrypted; - default: throw new ArgumentOutOfRangeException(nameof(tag), tag, null); - } + case MediaTagType.CD_TOC: return DataType.CompactDiscPartialToc; + case MediaTagType.CD_SessionInfo: return DataType.CompactDiscSessionInfo; + case MediaTagType.CD_FullTOC: return DataType.CompactDiscToc; + case MediaTagType.CD_PMA: return DataType.CompactDiscPma; + case MediaTagType.CD_ATIP: return DataType.CompactDiscAtip; + case MediaTagType.CD_TEXT: return DataType.CompactDiscLeadInCdText; + case MediaTagType.DVD_PFI: return DataType.DvdPfi; + case MediaTagType.DVD_CMI: return DataType.DvdLeadInCmi; + case MediaTagType.DVD_DiscKey: return DataType.DvdDiscKey; + case MediaTagType.DVD_BCA: return DataType.DvdBca; + case MediaTagType.DVD_DMI: return DataType.DvdDmi; + case MediaTagType.DVD_MediaIdentifier: return DataType.DvdMediaIdentifier; + case MediaTagType.DVD_MKB: return DataType.DvdMediaKeyBlock; + case MediaTagType.DVDRAM_DDS: return DataType.DvdRamDds; + case MediaTagType.DVDRAM_MediumStatus: return DataType.DvdRamMediumStatus; + case MediaTagType.DVDRAM_SpareArea: return DataType.DvdRamSpareArea; + case MediaTagType.DVDR_RMD: return DataType.DvdRRmd; + case MediaTagType.DVDR_PreRecordedInfo: return DataType.DvdRPrerecordedInfo; + case MediaTagType.DVDR_MediaIdentifier: return DataType.DvdRMediaIdentifier; + case MediaTagType.DVDR_PFI: return DataType.DvdRPfi; + case MediaTagType.DVD_ADIP: return DataType.DvdAdip; + case MediaTagType.HDDVD_CPI: return DataType.HdDvdCpi; + case MediaTagType.HDDVD_MediumStatus: return DataType.HdDvdMediumStatus; + case MediaTagType.DVDDL_LayerCapacity: return DataType.DvdDlLayerCapacity; + case MediaTagType.DVDDL_MiddleZoneAddress: return DataType.DvdDlMiddleZoneAddress; + case MediaTagType.DVDDL_JumpIntervalSize: return DataType.DvdDlJumpIntervalSize; + case MediaTagType.DVDDL_ManualLayerJumpLBA: return DataType.DvdDlManualLayerJumpLba; + case MediaTagType.BD_DI: return DataType.BlurayDi; + case MediaTagType.BD_BCA: return DataType.BlurayBca; + case MediaTagType.BD_DDS: return DataType.BlurayDds; + case MediaTagType.BD_CartridgeStatus: return DataType.BlurayCartridgeStatus; + case MediaTagType.BD_SpareArea: return DataType.BluraySpareArea; + case MediaTagType.AACS_VolumeIdentifier: return DataType.AacsVolumeIdentifier; + case MediaTagType.AACS_SerialNumber: return DataType.AacsSerialNumber; + case MediaTagType.AACS_MediaIdentifier: return DataType.AacsMediaIdentifier; + case MediaTagType.AACS_MKB: return DataType.AacsMediaKeyBlock; + case MediaTagType.AACS_DataKeys: return DataType.AacsDataKeys; + case MediaTagType.AACS_LBAExtents: return DataType.AacsLbaExtents; + case MediaTagType.AACS_CPRM_MKB: return DataType.CprmMediaKeyBlock; + case MediaTagType.Hybrid_RecognizedLayers: return DataType.HybridRecognizedLayers; + case MediaTagType.MMC_WriteProtection: return DataType.ScsiMmcWriteProtection; + case MediaTagType.MMC_DiscInformation: return DataType.ScsiMmcDiscInformation; + case MediaTagType.MMC_TrackResourcesInformation: return DataType.ScsiMmcTrackResourcesInformation; + case MediaTagType.MMC_POWResourcesInformation: return DataType.ScsiMmcPowResourcesInformation; + case MediaTagType.SCSI_INQUIRY: return DataType.ScsiInquiry; + case MediaTagType.SCSI_MODEPAGE_2A: return DataType.ScsiModePage2A; + case MediaTagType.ATA_IDENTIFY: return DataType.AtaIdentify; + case MediaTagType.ATAPI_IDENTIFY: return DataType.AtapiIdentify; + case MediaTagType.PCMCIA_CIS: return DataType.PcmciaCis; + case MediaTagType.SD_CID: return DataType.SecureDigitalCid; + case MediaTagType.SD_CSD: return DataType.SecureDigitalCsd; + case MediaTagType.SD_SCR: return DataType.SecureDigitalScr; + case MediaTagType.SD_OCR: return DataType.SecureDigitalOcr; + case MediaTagType.MMC_CID: return DataType.MultiMediaCardCid; + case MediaTagType.MMC_CSD: return DataType.MultiMediaCardCsd; + case MediaTagType.MMC_OCR: return DataType.MultiMediaCardOcr; + case MediaTagType.MMC_ExtendedCSD: return DataType.MultiMediaCardExtendedCsd; + case MediaTagType.Xbox_SecuritySector: return DataType.XboxSecuritySector; + case MediaTagType.Floppy_LeadOut: return DataType.FloppyLeadOut; + case MediaTagType.DCB: return DataType.DvdDiscControlBlock; + case MediaTagType.CD_FirstTrackPregap: return DataType.CompactDiscFirstTrackPregap; + case MediaTagType.CD_LeadOut: return DataType.CompactDiscLeadOut; + case MediaTagType.SCSI_MODESENSE_6: return DataType.ScsiModeSense6; + case MediaTagType.SCSI_MODESENSE_10: return DataType.ScsiModeSense10; + case MediaTagType.USB_Descriptors: return DataType.UsbDescriptors; + case MediaTagType.Xbox_DMI: return DataType.XboxDmi; + case MediaTagType.Xbox_PFI: return DataType.XboxPfi; + case MediaTagType.CD_MCN: return DataType.CompactDiscMediaCatalogueNumber; + case MediaTagType.CD_LeadIn: return DataType.CompactDiscLeadIn; + case MediaTagType.DVD_DiscKey_Decrypted: return DataType.DvdDiscKeyDecrypted; + default: throw new ArgumentOutOfRangeException(nameof(tag), tag, null); } } } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Identify.cs b/Aaru.Images/AaruFormat/Identify.cs index 88dc712d4..de6ca9b8e 100644 --- a/Aaru.Images/AaruFormat/Identify.cs +++ b/Aaru.Images/AaruFormat/Identify.cs @@ -34,25 +34,24 @@ using System.IO; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + /// + public bool Identify(IFilter imageFilter) { - /// - public bool Identify(IFilter imageFilter) - { - _imageStream = imageFilter.GetDataForkStream(); - _imageStream.Seek(0, SeekOrigin.Begin); + _imageStream = imageFilter.GetDataForkStream(); + _imageStream.Seek(0, SeekOrigin.Begin); - if(_imageStream.Length < Marshal.SizeOf()) - return false; + if(_imageStream.Length < Marshal.SizeOf()) + return false; - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - _header = Marshal.ByteArrayToStructureLittleEndian(_structureBytes); + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + _header = Marshal.ByteArrayToStructureLittleEndian(_structureBytes); - return (_header.identifier == DIC_MAGIC || _header.identifier == AARU_MAGIC) && - _header.imageMajorVersion <= AARUFMT_VERSION; - } + return (_header.identifier == DIC_MAGIC || _header.identifier == AARU_MAGIC) && + _header.imageMajorVersion <= AARUFMT_VERSION; } } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Properties.cs b/Aaru.Images/AaruFormat/Properties.cs index 13d0d8a2a..bb664a519 100644 --- a/Aaru.Images/AaruFormat/Properties.cs +++ b/Aaru.Images/AaruFormat/Properties.cs @@ -38,78 +38,77 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; using Schemas; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + /// + public OpticalImageCapabilities OpticalCapabilities => OpticalImageCapabilities.CanStoreAudioTracks | + OpticalImageCapabilities.CanStoreDataTracks | + OpticalImageCapabilities.CanStorePregaps | + OpticalImageCapabilities.CanStoreSubchannelRw | + OpticalImageCapabilities.CanStoreSessions | + OpticalImageCapabilities.CanStoreIsrc | + OpticalImageCapabilities.CanStoreCdText | + OpticalImageCapabilities.CanStoreMcn | + OpticalImageCapabilities.CanStoreRawData | + OpticalImageCapabilities.CanStoreCookedData | + OpticalImageCapabilities.CanStoreMultipleTracks | + OpticalImageCapabilities.CanStoreNotCdSessions | + OpticalImageCapabilities.CanStoreNotCdTracks | + OpticalImageCapabilities.CanStoreIndexes; + /// + public ImageInfo Info => _imageInfo; + /// + public string Name => "Aaru Format"; + /// + public Guid Id => new Guid("49360069-1784-4A2F-B723-0C844D610B0A"); + /// + public string Format => "Aaru"; + /// + public string Author => "Natalia Portillo"; + /// + public List Partitions { get; private set; } + /// + public List Tracks { get; private set; } + /// + public List Sessions { get; private set; } + /// + public List DumpHardware { get; private set; } + /// + public CICMMetadataType CicmMetadata { get; private set; } + /// + public IEnumerable SupportedMediaTags => + Enum.GetValues(typeof(MediaTagType)).Cast(); + /// + public IEnumerable SupportedSectorTags => + Enum.GetValues(typeof(SectorTagType)).Cast(); + /// + public IEnumerable SupportedMediaTypes => Enum.GetValues(typeof(MediaType)).Cast(); + /// + public IEnumerable<(string name, Type type, string description, object @default)> SupportedOptions => new[] { - /// - public OpticalImageCapabilities OpticalCapabilities => OpticalImageCapabilities.CanStoreAudioTracks | - OpticalImageCapabilities.CanStoreDataTracks | - OpticalImageCapabilities.CanStorePregaps | - OpticalImageCapabilities.CanStoreSubchannelRw | - OpticalImageCapabilities.CanStoreSessions | - OpticalImageCapabilities.CanStoreIsrc | - OpticalImageCapabilities.CanStoreCdText | - OpticalImageCapabilities.CanStoreMcn | - OpticalImageCapabilities.CanStoreRawData | - OpticalImageCapabilities.CanStoreCookedData | - OpticalImageCapabilities.CanStoreMultipleTracks | - OpticalImageCapabilities.CanStoreNotCdSessions | - OpticalImageCapabilities.CanStoreNotCdTracks | - OpticalImageCapabilities.CanStoreIndexes; - /// - public ImageInfo Info => _imageInfo; - /// - public string Name => "Aaru Format"; - /// - public Guid Id => new Guid("49360069-1784-4A2F-B723-0C844D610B0A"); - /// - public string Format => "Aaru"; - /// - public string Author => "Natalia Portillo"; - /// - public List Partitions { get; private set; } - /// - public List Tracks { get; private set; } - /// - public List Sessions { get; private set; } - /// - public List DumpHardware { get; private set; } - /// - public CICMMetadataType CicmMetadata { get; private set; } - /// - public IEnumerable SupportedMediaTags => - Enum.GetValues(typeof(MediaTagType)).Cast(); - /// - public IEnumerable SupportedSectorTags => - Enum.GetValues(typeof(SectorTagType)).Cast(); - /// - public IEnumerable SupportedMediaTypes => Enum.GetValues(typeof(MediaType)).Cast(); - /// - public IEnumerable<(string name, Type type, string description, object @default)> SupportedOptions => new[] - { - ("sectors_per_block", typeof(uint), - "How many sectors to store per block (will be rounded to next power of two)", 4096U), - ("dictionary", typeof(uint), "Size, in bytes, of the LZMA dictionary", (uint)(1 << 25)), - ("max_ddt_size", typeof(uint), - "Maximum size, in mebibytes, for in-memory DDT. If image needs a bigger one, it will be on-disk", 256U), - ("md5", typeof(bool), "Calculate and store MD5 of image's user data", false), - ("sha1", typeof(bool), "Calculate and store SHA1 of image's user data", false), - ("sha256", typeof(bool), "Calculate and store SHA256 of image's user data", false), - ("spamsum", typeof(bool), "Calculate and store SpamSum of image's user data", false), - ("deduplicate", typeof(bool), - "Store only unique sectors. This consumes more memory and is slower, but it's enabled by default", true), - ("compress", typeof(bool), "Compress user data blocks. Other blocks will always be compressed", - (object)true) - }; - /// - public IEnumerable KnownExtensions => new[] - { - ".dicf", ".aaru", ".aaruformat", ".aaruf", ".aif" - }; - /// - public bool IsWriting { get; private set; } - /// - public string ErrorMessage { get; private set; } - } + ("sectors_per_block", typeof(uint), + "How many sectors to store per block (will be rounded to next power of two)", 4096U), + ("dictionary", typeof(uint), "Size, in bytes, of the LZMA dictionary", (uint)(1 << 25)), + ("max_ddt_size", typeof(uint), + "Maximum size, in mebibytes, for in-memory DDT. If image needs a bigger one, it will be on-disk", 256U), + ("md5", typeof(bool), "Calculate and store MD5 of image's user data", false), + ("sha1", typeof(bool), "Calculate and store SHA1 of image's user data", false), + ("sha256", typeof(bool), "Calculate and store SHA256 of image's user data", false), + ("spamsum", typeof(bool), "Calculate and store SpamSum of image's user data", false), + ("deduplicate", typeof(bool), + "Store only unique sectors. This consumes more memory and is slower, but it's enabled by default", true), + ("compress", typeof(bool), "Compress user data blocks. Other blocks will always be compressed", + (object)true) + }; + /// + public IEnumerable KnownExtensions => new[] + { + ".dicf", ".aaru", ".aaruformat", ".aaruf", ".aif" + }; + /// + public bool IsWriting { get; private set; } + /// + public string ErrorMessage { get; private set; } } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Read.cs b/Aaru.Images/AaruFormat/Read.cs index f72191866..159eab985 100644 --- a/Aaru.Images/AaruFormat/Read.cs +++ b/Aaru.Images/AaruFormat/Read.cs @@ -52,1659 +52,1975 @@ using Marshal = Aaru.Helpers.Marshal; using Session = Aaru.CommonTypes.Structs.Session; using TrackType = Aaru.CommonTypes.Enums.TrackType; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + /// + public ErrorNumber Open(IFilter imageFilter) { - /// - public ErrorNumber Open(IFilter imageFilter) + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); + + _imageStream = imageFilter.GetDataForkStream(); + _imageStream.Seek(0, SeekOrigin.Begin); + + if(_imageStream.Length < Marshal.SizeOf()) + return ErrorNumber.InvalidArgument; + + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + _header = Marshal.ByteArrayToStructureLittleEndian(_structureBytes); + + if(_header.imageMajorVersion > AARUFMT_VERSION) { - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); + AaruConsole.ErrorWriteLine($"Image version {_header.imageMajorVersion} not recognized."); - _imageStream = imageFilter.GetDataForkStream(); - _imageStream.Seek(0, SeekOrigin.Begin); + return ErrorNumber.NotSupported; + } - if(_imageStream.Length < Marshal.SizeOf()) - return ErrorNumber.InvalidArgument; + _imageInfo.Application = _header.application; + _imageInfo.ApplicationVersion = $"{_header.applicationMajorVersion}.{_header.applicationMinorVersion}"; + _imageInfo.Version = $"{_header.imageMajorVersion}.{_header.imageMinorVersion}"; + _imageInfo.MediaType = _header.mediaType; - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - _header = Marshal.ByteArrayToStructureLittleEndian(_structureBytes); + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); - if(_header.imageMajorVersion > AARUFMT_VERSION) - { - AaruConsole.ErrorWriteLine($"Image version {_header.imageMajorVersion} not recognized."); + // Read the index header + _imageStream.Position = (long)_header.indexOffset; + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + IndexHeader idxHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - return ErrorNumber.NotSupported; - } + if(idxHeader.identifier != BlockType.Index && + idxHeader.identifier != BlockType.Index2) + { + AaruConsole.ErrorWriteLine("Index not found!"); - _imageInfo.Application = _header.application; - _imageInfo.ApplicationVersion = $"{_header.applicationMajorVersion}.{_header.applicationMinorVersion}"; - _imageInfo.Version = $"{_header.imageMajorVersion}.{_header.imageMinorVersion}"; - _imageInfo.MediaType = _header.mediaType; + return ErrorNumber.InvalidArgument; + } - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); - - // Read the index header + if(idxHeader.identifier == BlockType.Index2) + { _imageStream.Position = (long)_header.indexOffset; - _structureBytes = new byte[Marshal.SizeOf()]; + _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - IndexHeader idxHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); + IndexHeader2 idxHeader2 = Marshal.SpanToStructureLittleEndian(_structureBytes); - if(idxHeader.identifier != BlockType.Index && - idxHeader.identifier != BlockType.Index2) + AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", + _header.indexOffset, idxHeader2.entries); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + // Fill in-memory index + _index = new List(); + + for(ulong i = 0; i < idxHeader2.entries; i++) { - AaruConsole.ErrorWriteLine("Index not found!"); - - return ErrorNumber.InvalidArgument; - } - - if(idxHeader.identifier == BlockType.Index2) - { - _imageStream.Position = (long)_header.indexOffset; - _structureBytes = new byte[Marshal.SizeOf()]; + _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - IndexHeader2 idxHeader2 = Marshal.SpanToStructureLittleEndian(_structureBytes); + IndexEntry entry = Marshal.SpanToStructureLittleEndian(_structureBytes); - AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", - _header.indexOffset, idxHeader2.entries); + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Block type {0} with data type {1} is indexed to be at {2}", + entry.blockType, entry.dataType, entry.offset); - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); + _index.Add(entry); + } + } + else + { + AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", + _header.indexOffset, idxHeader.entries); - // Fill in-memory index - _index = new List(); + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); - for(ulong i = 0; i < idxHeader2.entries; i++) - { - _structureBytes = new byte[Marshal.SizeOf()]; + // Fill in-memory index + _index = new List(); + + for(ushort i = 0; i < idxHeader.entries; i++) + { + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + IndexEntry entry = Marshal.SpanToStructureLittleEndian(_structureBytes); + + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Block type {0} with data type {1} is indexed to be at {2}", + entry.blockType, entry.dataType, entry.offset); + + _index.Add(entry); + } + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); + + _imageInfo.ImageSize = 0; + + bool foundUserDataDdt = false; + _mediaTags = new Dictionary(); + List compactDiscIndexes = null; + + foreach(IndexEntry entry in _index) + { + _imageStream.Position = (long)entry.offset; + + switch(entry.blockType) + { + case BlockType.DataBlock: + // NOP block, skip + if(entry.dataType == DataType.NoData) + break; + + _imageStream.Position = (long)entry.offset; + + _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - IndexEntry entry = Marshal.SpanToStructureLittleEndian(_structureBytes); + BlockHeader blockHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); + _imageInfo.ImageSize += blockHeader.cmpLength; - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Block type {0} with data type {1} is indexed to be at {2}", - entry.blockType, entry.dataType, entry.offset); - - _index.Add(entry); - } - } - else - { - AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", - _header.indexOffset, idxHeader.entries); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - // Fill in-memory index - _index = new List(); - - for(ushort i = 0; i < idxHeader.entries; i++) - { - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - IndexEntry entry = Marshal.SpanToStructureLittleEndian(_structureBytes); - - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Block type {0} with data type {1} is indexed to be at {2}", - entry.blockType, entry.dataType, entry.offset); - - _index.Add(entry); - } - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); - - _imageInfo.ImageSize = 0; - - bool foundUserDataDdt = false; - _mediaTags = new Dictionary(); - List compactDiscIndexes = null; - - foreach(IndexEntry entry in _index) - { - _imageStream.Position = (long)entry.offset; - - switch(entry.blockType) - { - case BlockType.DataBlock: - // NOP block, skip - if(entry.dataType == DataType.NoData) - break; - - _imageStream.Position = (long)entry.offset; - - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - BlockHeader blockHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - _imageInfo.ImageSize += blockHeader.cmpLength; - - // Unused, skip - if(entry.dataType == DataType.UserData) - { - if(blockHeader.sectorSize > _imageInfo.SectorSize) - _imageInfo.SectorSize = blockHeader.sectorSize; - - break; - } - - if(blockHeader.identifier != entry.blockType) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Incorrect identifier for data block at position {0}", - entry.offset); - - break; - } - - if(blockHeader.type != entry.dataType) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Expected block with data type {0} at position {1} but found data type {2}", - entry.dataType, entry.offset, blockHeader.type); - - break; - } - - byte[] data; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Found data block type {0} at position {1}", - entry.dataType, entry.offset); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - // Decompress media tag - if(blockHeader.compression == CompressionType.Lzma || - blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform) - { - if(blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform && - entry.dataType != DataType.CdSectorSubchannel) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Invalid compression type {0} for block with data type {1}, continuing...", - blockHeader.compression, entry.dataType); - - break; - } - - DateTime startDecompress = DateTime.Now; - byte[] compressedTag = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; - byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; - _imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); - _imageStream.Read(compressedTag, 0, compressedTag.Length); - data = new byte[blockHeader.length]; - int decompressedLength = LZMA.DecodeBuffer(compressedTag, data, lzmaProperties); - - if(decompressedLength != blockHeader.length) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Error decompressing block, should be {0} bytes but got {1} bytes.", - blockHeader.length, decompressedLength); - - return ErrorNumber.InOutError; - } - - if(blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform) - data = ClauniaSubchannelUntransform(data); - - DateTime endDecompress = DateTime.Now; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to decompress block", - (endDecompress - startDecompress).TotalSeconds); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - } - else if(blockHeader.compression == CompressionType.None) - { - data = new byte[blockHeader.length]; - _imageStream.Read(data, 0, (int)blockHeader.length); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - } - else - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Found unknown compression type {0}, continuing...", - (ushort)blockHeader.compression); - - break; - } - - // Check CRC, if not correct, skip it - Crc64Context.Data(data, out byte[] blockCrc); - - if(BitConverter.ToUInt64(blockCrc, 0) != blockHeader.crc64 && - blockHeader.crc64 != 0) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", - BitConverter.ToUInt64(blockCrc, 0), blockHeader.crc64); - - break; - } - - // Check if it's not a media tag, but a sector tag, and fill the appropriate table then - switch(entry.dataType) - { - case DataType.CdSectorPrefix: - case DataType.CdSectorPrefixCorrected: - if(entry.dataType == DataType.CdSectorPrefixCorrected) - { - _sectorPrefixMs ??= new MemoryStream(); - _sectorPrefixMs.Write(data, 0, data.Length); - } - else - _sectorPrefix = data; - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync); - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case DataType.CdSectorSuffix: - case DataType.CdSectorSuffixCorrected: - if(entry.dataType == DataType.CdSectorSuffixCorrected) - { - _sectorSuffixMs ??= new MemoryStream(); - _sectorSuffixMs.Write(data, 0, data.Length); - } - else - _sectorSuffix = data; - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader); - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEcc)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEcc); - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccP)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccP); - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccQ)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccQ); - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case DataType.CdSectorSubchannel: - _sectorSubchannel = data; - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case DataType.AppleProfileTag: - case DataType.AppleSonyTag: - case DataType.PriamDataTowerTag: - _sectorSubchannel = data; - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case DataType.CompactDiscMode2Subheader: - _mode2Subheaders = data; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case DataType.DvdSectorCpiMai: - _sectorCpiMai = data; - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdCmi)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.DvdCmi); - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdTitleKey)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.DvdTitleKey); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case DataType.DvdSectorTitleKeyDecrypted: - _sectorDecryptedTitleKey = data; - - if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdTitleKeyDecrypted)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.DvdTitleKeyDecrypted); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - default: - MediaTagType mediaTagType = GetMediaTagTypeForDataType(blockHeader.type); - - if(_mediaTags.ContainsKey(mediaTagType)) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Media tag type {0} duplicated, removing previous entry...", - mediaTagType); - - _mediaTags.Remove(mediaTagType); - } - - _mediaTags.Add(mediaTagType, data); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - } - - break; - case BlockType.DeDuplicationTable: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - DdtHeader ddtHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - _imageInfo.ImageSize += ddtHeader.cmpLength; - - if(ddtHeader.identifier != BlockType.DeDuplicationTable) - break; - - switch(entry.dataType) - { - case DataType.UserData: - _imageInfo.Sectors = ddtHeader.entries; - _shift = ddtHeader.shift; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - // Check for DDT compression - switch(ddtHeader.compression) - { - case CompressionType.Lzma: - AaruConsole.DebugWriteLine("Aaru Format plugin", "Decompressing DDT..."); - DateTime ddtStart = DateTime.UtcNow; - byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; - byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; - _imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); - _imageStream.Read(compressedDdt, 0, compressedDdt.Length); - byte[] decompressedDdt = new byte[ddtHeader.length]; - - ulong decompressedLength = - (ulong)LZMA.DecodeBuffer(compressedDdt, decompressedDdt, lzmaProperties); - - if(decompressedLength != ddtHeader.length) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Error decompressing DDT, should be {0} bytes but got {1} bytes.", - ddtHeader.length, decompressedLength); - - return ErrorNumber.InOutError; - } - - _userDataDdt = MemoryMarshal.Cast(decompressedDdt).ToArray(); - DateTime ddtEnd = DateTime.UtcNow; - _inMemoryDdt = true; - - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Took {0} seconds to decompress DDT", - (ddtEnd - ddtStart).TotalSeconds); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case CompressionType.None: - _inMemoryDdt = false; - _outMemoryDdtPosition = (long)entry.offset; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - default: - AaruConsole. - ErrorWriteLine($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); - - return ErrorNumber.NotSupported; - } - - foundUserDataDdt = true; - - break; - case DataType.CdSectorPrefixCorrected: - case DataType.CdSectorSuffixCorrected: - { - byte[] decompressedDdt = new byte[ddtHeader.length]; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - // Check for DDT compression - switch(ddtHeader.compression) - { - case CompressionType.Lzma: - AaruConsole.DebugWriteLine("Aaru Format plugin", "Decompressing DDT..."); - DateTime ddtStart = DateTime.UtcNow; - byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; - byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; - _imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); - _imageStream.Read(compressedDdt, 0, compressedDdt.Length); - - ulong decompressedLength = - (ulong)LZMA.DecodeBuffer(compressedDdt, decompressedDdt, lzmaProperties); - - DateTime ddtEnd = DateTime.UtcNow; - - if(decompressedLength != ddtHeader.length) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Error decompressing DDT, should be {0} bytes but got {1} bytes.", - ddtHeader.length, decompressedLength); - - return ErrorNumber.InOutError; - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Took {0} seconds to decompress DDT", - (ddtEnd - ddtStart).TotalSeconds); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case CompressionType.None: - _imageStream.Read(decompressedDdt, 0, decompressedDdt.Length); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - default: - AaruConsole. - ErrorWriteLine($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); - - return ErrorNumber.NotSupported; - } - - uint[] cdDdt = MemoryMarshal.Cast(decompressedDdt).ToArray(); - - switch(entry.dataType) - { - case DataType.CdSectorPrefixCorrected: - _sectorPrefixDdt = cdDdt; - _sectorPrefixMs = new MemoryStream(); - - break; - case DataType.CdSectorSuffixCorrected: - _sectorSuffixDdt = cdDdt; - _sectorSuffixMs = new MemoryStream(); - - break; - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - } - } - - break; - - // Logical geometry block. It doesn't have a CRC coz, well, it's not so important - case BlockType.GeometryBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - _geometryBlock = Marshal.SpanToStructureLittleEndian(_structureBytes); - - if(_geometryBlock.identifier == BlockType.GeometryBlock) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Geometry set to {0} cylinders {1} heads {2} sectors per track", - _geometryBlock.cylinders, _geometryBlock.heads, - _geometryBlock.sectorsPerTrack); - - _imageInfo.Cylinders = _geometryBlock.cylinders; - _imageInfo.Heads = _geometryBlock.heads; - _imageInfo.SectorsPerTrack = _geometryBlock.sectorsPerTrack; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - } - - break; - - // Metadata block - case BlockType.MetadataBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - MetadataBlock metadataBlock = - Marshal.SpanToStructureLittleEndian(_structureBytes); - - if(metadataBlock.identifier != entry.blockType) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Incorrect identifier for data block at position {0}", - entry.offset); - - break; - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Found metadata block at position {0}", - entry.offset); - - byte[] metadata = new byte[metadataBlock.blockSize]; - _imageStream.Position = (long)entry.offset; - _imageStream.Read(metadata, 0, metadata.Length); - - if(metadataBlock.mediaSequence > 0 && - metadataBlock.lastMediaSequence > 0) - { - _imageInfo.MediaSequence = metadataBlock.mediaSequence; - _imageInfo.LastMediaSequence = metadataBlock.lastMediaSequence; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media sequence as {0} of {1}", - _imageInfo.MediaSequence, _imageInfo.LastMediaSequence); - } - - if(metadataBlock.creatorLength > 0 && - metadataBlock.creatorLength + metadataBlock.creatorOffset <= metadata.Length) - { - _imageInfo.Creator = Encoding.Unicode.GetString(metadata, (int)metadataBlock.creatorOffset, - (int)(metadataBlock.creatorLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting creator: {0}", - _imageInfo.Creator); - } - - if(metadataBlock.commentsOffset > 0 && - metadataBlock.commentsLength + metadataBlock.commentsOffset <= metadata.Length) - { - _imageInfo.Comments = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.commentsOffset, - (int)(metadataBlock.commentsLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting comments: {0}", - _imageInfo.Comments); - } - - if(metadataBlock.mediaTitleOffset > 0 && - metadataBlock.mediaTitleLength + metadataBlock.mediaTitleOffset <= metadata.Length) - { - _imageInfo.MediaTitle = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaTitleOffset, - (int)(metadataBlock.mediaTitleLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media title: {0}", - _imageInfo.MediaTitle); - } - - if(metadataBlock.mediaManufacturerOffset > 0 && - metadataBlock.mediaManufacturerLength + metadataBlock.mediaManufacturerOffset <= - metadata.Length) - { - _imageInfo.MediaManufacturer = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaManufacturerOffset, - (int)(metadataBlock.mediaManufacturerLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media manufacturer: {0}", - _imageInfo.MediaManufacturer); - } - - if(metadataBlock.mediaModelOffset > 0 && - metadataBlock.mediaModelLength + metadataBlock.mediaModelOffset <= metadata.Length) - { - _imageInfo.MediaModel = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaModelOffset, - (int)(metadataBlock.mediaModelLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media model: {0}", - _imageInfo.MediaModel); - } - - if(metadataBlock.mediaSerialNumberOffset > 0 && - metadataBlock.mediaSerialNumberLength + metadataBlock.mediaSerialNumberOffset <= - metadata.Length) - { - _imageInfo.MediaSerialNumber = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaSerialNumberOffset, - (int)(metadataBlock.mediaSerialNumberLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media serial number: {0}", - _imageInfo.MediaSerialNumber); - } - - if(metadataBlock.mediaBarcodeOffset > 0 && - metadataBlock.mediaBarcodeLength + metadataBlock.mediaBarcodeOffset <= metadata.Length) - { - _imageInfo.MediaBarcode = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaBarcodeOffset, - (int)(metadataBlock.mediaBarcodeLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media barcode: {0}", - _imageInfo.MediaBarcode); - } - - if(metadataBlock.mediaPartNumberOffset > 0 && - metadataBlock.mediaPartNumberLength + metadataBlock.mediaPartNumberOffset <= metadata.Length) - { - _imageInfo.MediaPartNumber = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaPartNumberOffset, - (int)(metadataBlock.mediaPartNumberLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media part number: {0}", - _imageInfo.MediaPartNumber); - } - - if(metadataBlock.driveManufacturerOffset > 0 && - metadataBlock.driveManufacturerLength + metadataBlock.driveManufacturerOffset <= - metadata.Length) - { - _imageInfo.DriveManufacturer = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveManufacturerOffset, - (int)(metadataBlock.driveManufacturerLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive manufacturer: {0}", - _imageInfo.DriveManufacturer); - } - - if(metadataBlock.driveModelOffset > 0 && - metadataBlock.driveModelLength + metadataBlock.driveModelOffset <= metadata.Length) - { - _imageInfo.DriveModel = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveModelOffset, - (int)(metadataBlock.driveModelLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive model: {0}", - _imageInfo.DriveModel); - } - - if(metadataBlock.driveSerialNumberOffset > 0 && - metadataBlock.driveSerialNumberLength + metadataBlock.driveSerialNumberOffset <= - metadata.Length) - { - _imageInfo.DriveSerialNumber = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveSerialNumberOffset, - (int)(metadataBlock.driveSerialNumberLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive serial number: {0}", - _imageInfo.DriveSerialNumber); - } - - if(metadataBlock.driveFirmwareRevisionOffset > 0 && - metadataBlock.driveFirmwareRevisionLength + metadataBlock.driveFirmwareRevisionOffset <= - metadata.Length) - { - _imageInfo.DriveFirmwareRevision = - Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveFirmwareRevisionOffset, - (int)(metadataBlock.driveFirmwareRevisionLength - 2)); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive firmware revision: {0}", - _imageInfo.DriveFirmwareRevision); - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - - // Optical disc tracks block - case BlockType.TracksBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - TracksHeader tracksHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - - if(tracksHeader.identifier != BlockType.TracksBlock) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Incorrect identifier for tracks block at position {0}", - entry.offset); - - break; - } - - _structureBytes = new byte[Marshal.SizeOf() * tracksHeader.entries]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - Crc64Context.Data(_structureBytes, out byte[] trksCrc); - - if(BitConverter.ToUInt64(trksCrc, 0) != tracksHeader.crc64) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", - BitConverter.ToUInt64(trksCrc, 0), tracksHeader.crc64); - - break; - } - - _imageStream.Position -= _structureBytes.Length; - - Tracks = new List(); - _trackFlags = new Dictionary(); - _trackIsrcs = new Dictionary(); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Found {0} tracks at position {0}", - tracksHeader.entries, entry.offset); - - for(ushort i = 0; i < tracksHeader.entries; i++) - { - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - TrackEntry trackEntry = - Marshal.ByteArrayToStructureLittleEndian(_structureBytes); - - Tracks.Add(new Track - { - Sequence = trackEntry.sequence, - Type = trackEntry.type, - StartSector = (ulong)trackEntry.start, - EndSector = (ulong)trackEntry.end, - Pregap = (ulong)trackEntry.pregap, - Session = trackEntry.session, - File = imageFilter.Filename, - FileType = "BINARY", - Filter = imageFilter - }); - - if(trackEntry.type == TrackType.Data) - continue; - - _trackFlags.Add(trackEntry.sequence, trackEntry.flags); - - if(!string.IsNullOrEmpty(trackEntry.isrc)) - _trackIsrcs.Add(trackEntry.sequence, trackEntry.isrc); - } - - if(_trackFlags.Count > 0 && - !_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackFlags)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackFlags); - - if(_trackIsrcs.Count > 0 && - !_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackIsrc)) - _imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackIsrc); - - _imageInfo.HasPartitions = true; - _imageInfo.HasSessions = true; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - - // CICM XML metadata block - case BlockType.CicmBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - CicmMetadataBlock cicmBlock = - Marshal.SpanToStructureLittleEndian(_structureBytes); - - if(cicmBlock.identifier != BlockType.CicmBlock) - break; - - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Found CICM XML metadata block at position {0}", entry.offset); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - byte[] cicmBytes = new byte[cicmBlock.length]; - _imageStream.Read(cicmBytes, 0, cicmBytes.Length); - var cicmMs = new MemoryStream(cicmBytes); - var cicmXs = new XmlSerializer(typeof(CICMMetadataType)); - - try - { - var sr = new StreamReader(cicmMs); - CicmMetadata = (CICMMetadataType)cicmXs.Deserialize(sr); - sr.Close(); - } - catch(XmlException ex) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Exception {0} processing CICM XML metadata block", ex.Message); - - CicmMetadata = null; - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - - // Dump hardware block - case BlockType.DumpHardwareBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - DumpHardwareHeader dumpBlock = - Marshal.SpanToStructureLittleEndian(_structureBytes); - - if(dumpBlock.identifier != BlockType.DumpHardwareBlock) - break; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Found dump hardware block at position {0}", - entry.offset); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - _structureBytes = new byte[dumpBlock.length]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - Crc64Context.Data(_structureBytes, out byte[] dumpCrc); - - if(BitConverter.ToUInt64(dumpCrc, 0) != dumpBlock.crc64) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", - BitConverter.ToUInt64(dumpCrc, 0), dumpBlock.crc64); - - break; - } - - _imageStream.Position -= _structureBytes.Length; - - DumpHardware = new List(); - - for(ushort i = 0; i < dumpBlock.entries; i++) - { - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - DumpHardwareEntry dumpEntry = - Marshal.SpanToStructureLittleEndian(_structureBytes); - - var dump = new DumpHardwareType - { - Software = new SoftwareType(), - Extents = new ExtentType[dumpEntry.extents] - }; - - byte[] tmp; - - if(dumpEntry.manufacturerLength > 0) - { - tmp = new byte[dumpEntry.manufacturerLength - 1]; - _imageStream.Read(tmp, 0, tmp.Length); - _imageStream.Position += 1; - dump.Manufacturer = Encoding.UTF8.GetString(tmp); - } - - if(dumpEntry.modelLength > 0) - { - tmp = new byte[dumpEntry.modelLength - 1]; - _imageStream.Read(tmp, 0, tmp.Length); - _imageStream.Position += 1; - dump.Model = Encoding.UTF8.GetString(tmp); - } - - if(dumpEntry.revisionLength > 0) - { - tmp = new byte[dumpEntry.revisionLength - 1]; - _imageStream.Read(tmp, 0, tmp.Length); - _imageStream.Position += 1; - dump.Revision = Encoding.UTF8.GetString(tmp); - } - - if(dumpEntry.firmwareLength > 0) - { - tmp = new byte[dumpEntry.firmwareLength - 1]; - _imageStream.Read(tmp, 0, tmp.Length); - _imageStream.Position += 1; - dump.Firmware = Encoding.UTF8.GetString(tmp); - } - - if(dumpEntry.serialLength > 0) - { - tmp = new byte[dumpEntry.serialLength - 1]; - _imageStream.Read(tmp, 0, tmp.Length); - _imageStream.Position += 1; - dump.Serial = Encoding.UTF8.GetString(tmp); - } - - if(dumpEntry.softwareNameLength > 0) - { - tmp = new byte[dumpEntry.softwareNameLength - 1]; - _imageStream.Read(tmp, 0, tmp.Length); - _imageStream.Position += 1; - dump.Software.Name = Encoding.UTF8.GetString(tmp); - } - - if(dumpEntry.softwareVersionLength > 0) - { - tmp = new byte[dumpEntry.softwareVersionLength - 1]; - _imageStream.Read(tmp, 0, tmp.Length); - _imageStream.Position += 1; - dump.Software.Version = Encoding.UTF8.GetString(tmp); - } - - if(dumpEntry.softwareOperatingSystemLength > 0) - { - tmp = new byte[dumpEntry.softwareOperatingSystemLength - 1]; - _imageStream.Read(tmp, 0, tmp.Length); - _imageStream.Position += 1; - dump.Software.OperatingSystem = Encoding.UTF8.GetString(tmp); - } - - tmp = new byte[16]; - - for(uint j = 0; j < dumpEntry.extents; j++) - { - _imageStream.Read(tmp, 0, tmp.Length); - - dump.Extents[j] = new ExtentType - { - Start = BitConverter.ToUInt64(tmp, 0), - End = BitConverter.ToUInt64(tmp, 8) - }; - } - - dump.Extents = dump.Extents.OrderBy(t => t.Start).ToArray(); - - if(dump.Extents.Length > 0) - DumpHardware.Add(dump); - } - - if(DumpHardware.Count == 0) - DumpHardware = null; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - - // Tape partition block - case BlockType.TapePartitionBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - TapePartitionHeader partitionHeader = - Marshal.SpanToStructureLittleEndian(_structureBytes); - - if(partitionHeader.identifier != BlockType.TapePartitionBlock) - break; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Found tape partition block at position {0}", - entry.offset); - - byte[] tapePartitionBytes = new byte[partitionHeader.length]; - _imageStream.Read(tapePartitionBytes, 0, tapePartitionBytes.Length); - - Span tapePartitions = - MemoryMarshal.Cast(tapePartitionBytes); - - TapePartitions = new List(); - - foreach(TapePartitionEntry tapePartition in tapePartitions) - TapePartitions.Add(new TapePartition - { - FirstBlock = tapePartition.FirstBlock, - LastBlock = tapePartition.LastBlock, - Number = tapePartition.Number - }); - - IsTape = true; - - break; - - // Tape file block - case BlockType.TapeFileBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - TapeFileHeader fileHeader = - Marshal.SpanToStructureLittleEndian(_structureBytes); - - if(fileHeader.identifier != BlockType.TapeFileBlock) - break; - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Found tape file block at position {0}", - entry.offset); - - byte[] tapeFileBytes = new byte[fileHeader.length]; - _imageStream.Read(tapeFileBytes, 0, tapeFileBytes.Length); - Span tapeFiles = MemoryMarshal.Cast(tapeFileBytes); - Files = new List(); - - foreach(TapeFileEntry file in tapeFiles) - Files.Add(new TapeFile - { - FirstBlock = file.FirstBlock, - LastBlock = file.LastBlock, - Partition = file.Partition, - File = file.File - }); - - IsTape = true; - - break; - - // Optical disc tracks block - case BlockType.CompactDiscIndexesBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - CompactDiscIndexesHeader indexesHeader = - Marshal.SpanToStructureLittleEndian(_structureBytes); - - if(indexesHeader.identifier != BlockType.CompactDiscIndexesBlock) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Incorrect identifier for compact disc indexes block at position {0}", - entry.offset); - - break; - } - - _structureBytes = new byte[Marshal.SizeOf() * indexesHeader.entries]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - Crc64Context.Data(_structureBytes, out byte[] idsxCrc); - - if(BitConverter.ToUInt64(idsxCrc, 0) != indexesHeader.crc64) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", - BitConverter.ToUInt64(idsxCrc, 0), indexesHeader.crc64); - - break; - } - - _imageStream.Position -= _structureBytes.Length; - - compactDiscIndexes = new List(); - - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Found {0} compact disc indexes at position {0}", - indexesHeader.entries, entry.offset); - - for(ushort i = 0; i < indexesHeader.entries; i++) - { - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - - compactDiscIndexes.Add(Marshal. - ByteArrayToStructureLittleEndian< - CompactDiscIndexEntry>(_structureBytes)); - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - } - } - - if(!foundUserDataDdt) - { - AaruConsole.ErrorWriteLine("Could not find user data deduplication table."); - - return ErrorNumber.InvalidArgument; - } - - _imageInfo.CreationTime = DateTime.FromFileTimeUtc(_header.creationTime); - AaruConsole.DebugWriteLine("Aaru Format plugin", "Image created on {0}", _imageInfo.CreationTime); - _imageInfo.LastModificationTime = DateTime.FromFileTimeUtc(_header.lastWrittenTime); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Image last written on {0}", - _imageInfo.LastModificationTime); - - _imageInfo.XmlMediaType = GetXmlMediaType(_header.mediaType); - - if(_geometryBlock.identifier != BlockType.GeometryBlock && - _imageInfo.XmlMediaType == XmlMediaType.BlockMedia) - { - _imageInfo.Cylinders = (uint)(_imageInfo.Sectors / 16 / 63); - _imageInfo.Heads = 16; - _imageInfo.SectorsPerTrack = 63; - } - - _imageInfo.ReadableMediaTags.AddRange(_mediaTags.Keys); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); - - // Initialize caches - _blockCache = new Dictionary(); - _blockHeaderCache = new Dictionary(); - _currentCacheSize = 0; - - if(!_inMemoryDdt) - _ddtEntryCache = new Dictionary(); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); - - // Initialize tracks, sessions and partitions - if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) - { - if(Tracks != null) - { - bool leadOutFixed = false; - bool sessionPregapFixed = false; - - if(_mediaTags.TryGetValue(MediaTagType.CD_FullTOC, out byte[] fullToc)) + // Unused, skip + if(entry.dataType == DataType.UserData) { - byte[] tmp = new byte[fullToc.Length + 2]; - Array.Copy(fullToc, 0, tmp, 2, fullToc.Length); - tmp[0] = (byte)(fullToc.Length >> 8); - tmp[1] = (byte)(fullToc.Length & 0xFF); + if(blockHeader.sectorSize > _imageInfo.SectorSize) + _imageInfo.SectorSize = blockHeader.sectorSize; - FullTOC.CDFullTOC? decodedFullToc = FullTOC.Decode(tmp); + break; + } - if(decodedFullToc.HasValue) + if(blockHeader.identifier != entry.blockType) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Incorrect identifier for data block at position {0}", + entry.offset); + + break; + } + + if(blockHeader.type != entry.dataType) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Expected block with data type {0} at position {1} but found data type {2}", + entry.dataType, entry.offset, blockHeader.type); + + break; + } + + byte[] data; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Found data block type {0} at position {1}", + entry.dataType, entry.offset); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + // Decompress media tag + if(blockHeader.compression == CompressionType.Lzma || + blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform) + { + if(blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform && + entry.dataType != DataType.CdSectorSubchannel) { - Dictionary leadOutStarts = new(); // Lead-out starts + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Invalid compression type {0} for block with data type {1}, continuing...", + blockHeader.compression, entry.dataType); - foreach(FullTOC.TrackDataDescriptor trk in + break; + } + + DateTime startDecompress = DateTime.Now; + byte[] compressedTag = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + _imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + _imageStream.Read(compressedTag, 0, compressedTag.Length); + data = new byte[blockHeader.length]; + int decompressedLength = LZMA.DecodeBuffer(compressedTag, data, lzmaProperties); + + if(decompressedLength != blockHeader.length) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Error decompressing block, should be {0} bytes but got {1} bytes.", + blockHeader.length, decompressedLength); + + return ErrorNumber.InOutError; + } + + if(blockHeader.compression == CompressionType.LzmaClauniaSubchannelTransform) + data = ClauniaSubchannelUntransform(data); + + DateTime endDecompress = DateTime.Now; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Took {0} seconds to decompress block", + (endDecompress - startDecompress).TotalSeconds); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + } + else if(blockHeader.compression == CompressionType.None) + { + data = new byte[blockHeader.length]; + _imageStream.Read(data, 0, (int)blockHeader.length); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + } + else + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Found unknown compression type {0}, continuing...", + (ushort)blockHeader.compression); + + break; + } + + // Check CRC, if not correct, skip it + Crc64Context.Data(data, out byte[] blockCrc); + + if(BitConverter.ToUInt64(blockCrc, 0) != blockHeader.crc64 && + blockHeader.crc64 != 0) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(blockCrc, 0), blockHeader.crc64); + + break; + } + + // Check if it's not a media tag, but a sector tag, and fill the appropriate table then + switch(entry.dataType) + { + case DataType.CdSectorPrefix: + case DataType.CdSectorPrefixCorrected: + if(entry.dataType == DataType.CdSectorPrefixCorrected) + { + _sectorPrefixMs ??= new MemoryStream(); + _sectorPrefixMs.Write(data, 0, data.Length); + } + else + _sectorPrefix = data; + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSync)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSync); + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorHeader)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorHeader); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case DataType.CdSectorSuffix: + case DataType.CdSectorSuffixCorrected: + if(entry.dataType == DataType.CdSectorSuffixCorrected) + { + _sectorSuffixMs ??= new MemoryStream(); + _sectorSuffixMs.Write(data, 0, data.Length); + } + else + _sectorSuffix = data; + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubHeader)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubHeader); + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEcc)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEcc); + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccP)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccP); + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEccQ)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEccQ); + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorEdc)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorEdc); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case DataType.CdSectorSubchannel: + _sectorSubchannel = data; + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdSectorSubchannel)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdSectorSubchannel); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case DataType.AppleProfileTag: + case DataType.AppleSonyTag: + case DataType.PriamDataTowerTag: + _sectorSubchannel = data; + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.AppleSectorTag)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.AppleSectorTag); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case DataType.CompactDiscMode2Subheader: + _mode2Subheaders = data; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case DataType.DvdSectorCpiMai: + _sectorCpiMai = data; + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdCmi)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.DvdCmi); + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdTitleKey)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.DvdTitleKey); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case DataType.DvdSectorTitleKeyDecrypted: + _sectorDecryptedTitleKey = data; + + if(!_imageInfo.ReadableSectorTags.Contains(SectorTagType.DvdTitleKeyDecrypted)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.DvdTitleKeyDecrypted); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + default: + MediaTagType mediaTagType = GetMediaTagTypeForDataType(blockHeader.type); + + if(_mediaTags.ContainsKey(mediaTagType)) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Media tag type {0} duplicated, removing previous entry...", + mediaTagType); + + _mediaTags.Remove(mediaTagType); + } + + _mediaTags.Add(mediaTagType, data); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + } + + break; + case BlockType.DeDuplicationTable: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + DdtHeader ddtHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); + _imageInfo.ImageSize += ddtHeader.cmpLength; + + if(ddtHeader.identifier != BlockType.DeDuplicationTable) + break; + + switch(entry.dataType) + { + case DataType.UserData: + _imageInfo.Sectors = ddtHeader.entries; + _shift = ddtHeader.shift; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + // Check for DDT compression + switch(ddtHeader.compression) + { + case CompressionType.Lzma: + AaruConsole.DebugWriteLine("Aaru Format plugin", "Decompressing DDT..."); + DateTime ddtStart = DateTime.UtcNow; + byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + _imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + _imageStream.Read(compressedDdt, 0, compressedDdt.Length); + byte[] decompressedDdt = new byte[ddtHeader.length]; + + ulong decompressedLength = + (ulong)LZMA.DecodeBuffer(compressedDdt, decompressedDdt, lzmaProperties); + + if(decompressedLength != ddtHeader.length) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Error decompressing DDT, should be {0} bytes but got {1} bytes.", + ddtHeader.length, decompressedLength); + + return ErrorNumber.InOutError; + } + + _userDataDdt = MemoryMarshal.Cast(decompressedDdt).ToArray(); + DateTime ddtEnd = DateTime.UtcNow; + _inMemoryDdt = true; + + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Took {0} seconds to decompress DDT", + (ddtEnd - ddtStart).TotalSeconds); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case CompressionType.None: + _inMemoryDdt = false; + _outMemoryDdtPosition = (long)entry.offset; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + default: + AaruConsole. + ErrorWriteLine($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + + return ErrorNumber.NotSupported; + } + + foundUserDataDdt = true; + + break; + case DataType.CdSectorPrefixCorrected: + case DataType.CdSectorSuffixCorrected: + { + byte[] decompressedDdt = new byte[ddtHeader.length]; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + // Check for DDT compression + switch(ddtHeader.compression) + { + case CompressionType.Lzma: + AaruConsole.DebugWriteLine("Aaru Format plugin", "Decompressing DDT..."); + DateTime ddtStart = DateTime.UtcNow; + byte[] compressedDdt = new byte[ddtHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + _imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + _imageStream.Read(compressedDdt, 0, compressedDdt.Length); + + ulong decompressedLength = + (ulong)LZMA.DecodeBuffer(compressedDdt, decompressedDdt, lzmaProperties); + + DateTime ddtEnd = DateTime.UtcNow; + + if(decompressedLength != ddtHeader.length) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Error decompressing DDT, should be {0} bytes but got {1} bytes.", + ddtHeader.length, decompressedLength); + + return ErrorNumber.InOutError; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Took {0} seconds to decompress DDT", + (ddtEnd - ddtStart).TotalSeconds); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case CompressionType.None: + _imageStream.Read(decompressedDdt, 0, decompressedDdt.Length); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + default: + AaruConsole. + ErrorWriteLine($"Found unsupported compression algorithm {(ushort)ddtHeader.compression}"); + + return ErrorNumber.NotSupported; + } + + uint[] cdDdt = MemoryMarshal.Cast(decompressedDdt).ToArray(); + + switch(entry.dataType) + { + case DataType.CdSectorPrefixCorrected: + _sectorPrefixDdt = cdDdt; + _sectorPrefixMs = new MemoryStream(); + + break; + case DataType.CdSectorSuffixCorrected: + _sectorSuffixDdt = cdDdt; + _sectorSuffixMs = new MemoryStream(); + + break; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + } + } + + break; + + // Logical geometry block. It doesn't have a CRC coz, well, it's not so important + case BlockType.GeometryBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + _geometryBlock = Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(_geometryBlock.identifier == BlockType.GeometryBlock) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Geometry set to {0} cylinders {1} heads {2} sectors per track", + _geometryBlock.cylinders, _geometryBlock.heads, + _geometryBlock.sectorsPerTrack); + + _imageInfo.Cylinders = _geometryBlock.cylinders; + _imageInfo.Heads = _geometryBlock.heads; + _imageInfo.SectorsPerTrack = _geometryBlock.sectorsPerTrack; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + } + + break; + + // Metadata block + case BlockType.MetadataBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + MetadataBlock metadataBlock = + Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(metadataBlock.identifier != entry.blockType) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Incorrect identifier for data block at position {0}", + entry.offset); + + break; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Found metadata block at position {0}", + entry.offset); + + byte[] metadata = new byte[metadataBlock.blockSize]; + _imageStream.Position = (long)entry.offset; + _imageStream.Read(metadata, 0, metadata.Length); + + if(metadataBlock.mediaSequence > 0 && + metadataBlock.lastMediaSequence > 0) + { + _imageInfo.MediaSequence = metadataBlock.mediaSequence; + _imageInfo.LastMediaSequence = metadataBlock.lastMediaSequence; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media sequence as {0} of {1}", + _imageInfo.MediaSequence, _imageInfo.LastMediaSequence); + } + + if(metadataBlock.creatorLength > 0 && + metadataBlock.creatorLength + metadataBlock.creatorOffset <= metadata.Length) + { + _imageInfo.Creator = Encoding.Unicode.GetString(metadata, (int)metadataBlock.creatorOffset, + (int)(metadataBlock.creatorLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting creator: {0}", + _imageInfo.Creator); + } + + if(metadataBlock.commentsOffset > 0 && + metadataBlock.commentsLength + metadataBlock.commentsOffset <= metadata.Length) + { + _imageInfo.Comments = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.commentsOffset, + (int)(metadataBlock.commentsLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting comments: {0}", + _imageInfo.Comments); + } + + if(metadataBlock.mediaTitleOffset > 0 && + metadataBlock.mediaTitleLength + metadataBlock.mediaTitleOffset <= metadata.Length) + { + _imageInfo.MediaTitle = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaTitleOffset, + (int)(metadataBlock.mediaTitleLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media title: {0}", + _imageInfo.MediaTitle); + } + + if(metadataBlock.mediaManufacturerOffset > 0 && + metadataBlock.mediaManufacturerLength + metadataBlock.mediaManufacturerOffset <= + metadata.Length) + { + _imageInfo.MediaManufacturer = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaManufacturerOffset, + (int)(metadataBlock.mediaManufacturerLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media manufacturer: {0}", + _imageInfo.MediaManufacturer); + } + + if(metadataBlock.mediaModelOffset > 0 && + metadataBlock.mediaModelLength + metadataBlock.mediaModelOffset <= metadata.Length) + { + _imageInfo.MediaModel = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaModelOffset, + (int)(metadataBlock.mediaModelLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media model: {0}", + _imageInfo.MediaModel); + } + + if(metadataBlock.mediaSerialNumberOffset > 0 && + metadataBlock.mediaSerialNumberLength + metadataBlock.mediaSerialNumberOffset <= + metadata.Length) + { + _imageInfo.MediaSerialNumber = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaSerialNumberOffset, + (int)(metadataBlock.mediaSerialNumberLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media serial number: {0}", + _imageInfo.MediaSerialNumber); + } + + if(metadataBlock.mediaBarcodeOffset > 0 && + metadataBlock.mediaBarcodeLength + metadataBlock.mediaBarcodeOffset <= metadata.Length) + { + _imageInfo.MediaBarcode = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaBarcodeOffset, + (int)(metadataBlock.mediaBarcodeLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media barcode: {0}", + _imageInfo.MediaBarcode); + } + + if(metadataBlock.mediaPartNumberOffset > 0 && + metadataBlock.mediaPartNumberLength + metadataBlock.mediaPartNumberOffset <= metadata.Length) + { + _imageInfo.MediaPartNumber = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.mediaPartNumberOffset, + (int)(metadataBlock.mediaPartNumberLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting media part number: {0}", + _imageInfo.MediaPartNumber); + } + + if(metadataBlock.driveManufacturerOffset > 0 && + metadataBlock.driveManufacturerLength + metadataBlock.driveManufacturerOffset <= + metadata.Length) + { + _imageInfo.DriveManufacturer = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveManufacturerOffset, + (int)(metadataBlock.driveManufacturerLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive manufacturer: {0}", + _imageInfo.DriveManufacturer); + } + + if(metadataBlock.driveModelOffset > 0 && + metadataBlock.driveModelLength + metadataBlock.driveModelOffset <= metadata.Length) + { + _imageInfo.DriveModel = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveModelOffset, + (int)(metadataBlock.driveModelLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive model: {0}", + _imageInfo.DriveModel); + } + + if(metadataBlock.driveSerialNumberOffset > 0 && + metadataBlock.driveSerialNumberLength + metadataBlock.driveSerialNumberOffset <= + metadata.Length) + { + _imageInfo.DriveSerialNumber = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveSerialNumberOffset, + (int)(metadataBlock.driveSerialNumberLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive serial number: {0}", + _imageInfo.DriveSerialNumber); + } + + if(metadataBlock.driveFirmwareRevisionOffset > 0 && + metadataBlock.driveFirmwareRevisionLength + metadataBlock.driveFirmwareRevisionOffset <= + metadata.Length) + { + _imageInfo.DriveFirmwareRevision = + Encoding.Unicode.GetString(metadata, (int)metadataBlock.driveFirmwareRevisionOffset, + (int)(metadataBlock.driveFirmwareRevisionLength - 2)); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Setting drive firmware revision: {0}", + _imageInfo.DriveFirmwareRevision); + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + + // Optical disc tracks block + case BlockType.TracksBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + TracksHeader tracksHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(tracksHeader.identifier != BlockType.TracksBlock) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Incorrect identifier for tracks block at position {0}", + entry.offset); + + break; + } + + _structureBytes = new byte[Marshal.SizeOf() * tracksHeader.entries]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + Crc64Context.Data(_structureBytes, out byte[] trksCrc); + + if(BitConverter.ToUInt64(trksCrc, 0) != tracksHeader.crc64) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(trksCrc, 0), tracksHeader.crc64); + + break; + } + + _imageStream.Position -= _structureBytes.Length; + + Tracks = new List(); + _trackFlags = new Dictionary(); + _trackIsrcs = new Dictionary(); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Found {0} tracks at position {0}", + tracksHeader.entries, entry.offset); + + for(ushort i = 0; i < tracksHeader.entries; i++) + { + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + TrackEntry trackEntry = + Marshal.ByteArrayToStructureLittleEndian(_structureBytes); + + Tracks.Add(new Track + { + Sequence = trackEntry.sequence, + Type = trackEntry.type, + StartSector = (ulong)trackEntry.start, + EndSector = (ulong)trackEntry.end, + Pregap = (ulong)trackEntry.pregap, + Session = trackEntry.session, + File = imageFilter.Filename, + FileType = "BINARY", + Filter = imageFilter + }); + + if(trackEntry.type == TrackType.Data) + continue; + + _trackFlags.Add(trackEntry.sequence, trackEntry.flags); + + if(!string.IsNullOrEmpty(trackEntry.isrc)) + _trackIsrcs.Add(trackEntry.sequence, trackEntry.isrc); + } + + if(_trackFlags.Count > 0 && + !_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackFlags)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackFlags); + + if(_trackIsrcs.Count > 0 && + !_imageInfo.ReadableSectorTags.Contains(SectorTagType.CdTrackIsrc)) + _imageInfo.ReadableSectorTags.Add(SectorTagType.CdTrackIsrc); + + _imageInfo.HasPartitions = true; + _imageInfo.HasSessions = true; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + + // CICM XML metadata block + case BlockType.CicmBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + CicmMetadataBlock cicmBlock = + Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(cicmBlock.identifier != BlockType.CicmBlock) + break; + + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Found CICM XML metadata block at position {0}", entry.offset); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + byte[] cicmBytes = new byte[cicmBlock.length]; + _imageStream.Read(cicmBytes, 0, cicmBytes.Length); + var cicmMs = new MemoryStream(cicmBytes); + var cicmXs = new XmlSerializer(typeof(CICMMetadataType)); + + try + { + var sr = new StreamReader(cicmMs); + CicmMetadata = (CICMMetadataType)cicmXs.Deserialize(sr); + sr.Close(); + } + catch(XmlException ex) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Exception {0} processing CICM XML metadata block", ex.Message); + + CicmMetadata = null; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + + // Dump hardware block + case BlockType.DumpHardwareBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + DumpHardwareHeader dumpBlock = + Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(dumpBlock.identifier != BlockType.DumpHardwareBlock) + break; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Found dump hardware block at position {0}", + entry.offset); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + _structureBytes = new byte[dumpBlock.length]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + Crc64Context.Data(_structureBytes, out byte[] dumpCrc); + + if(BitConverter.ToUInt64(dumpCrc, 0) != dumpBlock.crc64) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(dumpCrc, 0), dumpBlock.crc64); + + break; + } + + _imageStream.Position -= _structureBytes.Length; + + DumpHardware = new List(); + + for(ushort i = 0; i < dumpBlock.entries; i++) + { + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + DumpHardwareEntry dumpEntry = + Marshal.SpanToStructureLittleEndian(_structureBytes); + + var dump = new DumpHardwareType + { + Software = new SoftwareType(), + Extents = new ExtentType[dumpEntry.extents] + }; + + byte[] tmp; + + if(dumpEntry.manufacturerLength > 0) + { + tmp = new byte[dumpEntry.manufacturerLength - 1]; + _imageStream.Read(tmp, 0, tmp.Length); + _imageStream.Position += 1; + dump.Manufacturer = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.modelLength > 0) + { + tmp = new byte[dumpEntry.modelLength - 1]; + _imageStream.Read(tmp, 0, tmp.Length); + _imageStream.Position += 1; + dump.Model = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.revisionLength > 0) + { + tmp = new byte[dumpEntry.revisionLength - 1]; + _imageStream.Read(tmp, 0, tmp.Length); + _imageStream.Position += 1; + dump.Revision = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.firmwareLength > 0) + { + tmp = new byte[dumpEntry.firmwareLength - 1]; + _imageStream.Read(tmp, 0, tmp.Length); + _imageStream.Position += 1; + dump.Firmware = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.serialLength > 0) + { + tmp = new byte[dumpEntry.serialLength - 1]; + _imageStream.Read(tmp, 0, tmp.Length); + _imageStream.Position += 1; + dump.Serial = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareNameLength > 0) + { + tmp = new byte[dumpEntry.softwareNameLength - 1]; + _imageStream.Read(tmp, 0, tmp.Length); + _imageStream.Position += 1; + dump.Software.Name = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareVersionLength > 0) + { + tmp = new byte[dumpEntry.softwareVersionLength - 1]; + _imageStream.Read(tmp, 0, tmp.Length); + _imageStream.Position += 1; + dump.Software.Version = Encoding.UTF8.GetString(tmp); + } + + if(dumpEntry.softwareOperatingSystemLength > 0) + { + tmp = new byte[dumpEntry.softwareOperatingSystemLength - 1]; + _imageStream.Read(tmp, 0, tmp.Length); + _imageStream.Position += 1; + dump.Software.OperatingSystem = Encoding.UTF8.GetString(tmp); + } + + tmp = new byte[16]; + + for(uint j = 0; j < dumpEntry.extents; j++) + { + _imageStream.Read(tmp, 0, tmp.Length); + + dump.Extents[j] = new ExtentType + { + Start = BitConverter.ToUInt64(tmp, 0), + End = BitConverter.ToUInt64(tmp, 8) + }; + } + + dump.Extents = dump.Extents.OrderBy(t => t.Start).ToArray(); + + if(dump.Extents.Length > 0) + DumpHardware.Add(dump); + } + + if(DumpHardware.Count == 0) + DumpHardware = null; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + + // Tape partition block + case BlockType.TapePartitionBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + TapePartitionHeader partitionHeader = + Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(partitionHeader.identifier != BlockType.TapePartitionBlock) + break; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Found tape partition block at position {0}", + entry.offset); + + byte[] tapePartitionBytes = new byte[partitionHeader.length]; + _imageStream.Read(tapePartitionBytes, 0, tapePartitionBytes.Length); + + Span tapePartitions = + MemoryMarshal.Cast(tapePartitionBytes); + + TapePartitions = new List(); + + foreach(TapePartitionEntry tapePartition in tapePartitions) + TapePartitions.Add(new TapePartition + { + FirstBlock = tapePartition.FirstBlock, + LastBlock = tapePartition.LastBlock, + Number = tapePartition.Number + }); + + IsTape = true; + + break; + + // Tape file block + case BlockType.TapeFileBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + TapeFileHeader fileHeader = + Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(fileHeader.identifier != BlockType.TapeFileBlock) + break; + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Found tape file block at position {0}", + entry.offset); + + byte[] tapeFileBytes = new byte[fileHeader.length]; + _imageStream.Read(tapeFileBytes, 0, tapeFileBytes.Length); + Span tapeFiles = MemoryMarshal.Cast(tapeFileBytes); + Files = new List(); + + foreach(TapeFileEntry file in tapeFiles) + Files.Add(new TapeFile + { + FirstBlock = file.FirstBlock, + LastBlock = file.LastBlock, + Partition = file.Partition, + File = file.File + }); + + IsTape = true; + + break; + + // Optical disc tracks block + case BlockType.CompactDiscIndexesBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + CompactDiscIndexesHeader indexesHeader = + Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(indexesHeader.identifier != BlockType.CompactDiscIndexesBlock) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Incorrect identifier for compact disc indexes block at position {0}", + entry.offset); + + break; + } + + _structureBytes = new byte[Marshal.SizeOf() * indexesHeader.entries]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + Crc64Context.Data(_structureBytes, out byte[] idsxCrc); + + if(BitConverter.ToUInt64(idsxCrc, 0) != indexesHeader.crc64) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Incorrect CRC found: 0x{0:X16} found, expected 0x{1:X16}, continuing...", + BitConverter.ToUInt64(idsxCrc, 0), indexesHeader.crc64); + + break; + } + + _imageStream.Position -= _structureBytes.Length; + + compactDiscIndexes = new List(); + + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Found {0} compact disc indexes at position {0}", + indexesHeader.entries, entry.offset); + + for(ushort i = 0; i < indexesHeader.entries; i++) + { + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + + compactDiscIndexes.Add(Marshal. + ByteArrayToStructureLittleEndian< + CompactDiscIndexEntry>(_structureBytes)); + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + } + } + + if(!foundUserDataDdt) + { + AaruConsole.ErrorWriteLine("Could not find user data deduplication table."); + + return ErrorNumber.InvalidArgument; + } + + _imageInfo.CreationTime = DateTime.FromFileTimeUtc(_header.creationTime); + AaruConsole.DebugWriteLine("Aaru Format plugin", "Image created on {0}", _imageInfo.CreationTime); + _imageInfo.LastModificationTime = DateTime.FromFileTimeUtc(_header.lastWrittenTime); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Image last written on {0}", + _imageInfo.LastModificationTime); + + _imageInfo.XmlMediaType = GetXmlMediaType(_header.mediaType); + + if(_geometryBlock.identifier != BlockType.GeometryBlock && + _imageInfo.XmlMediaType == XmlMediaType.BlockMedia) + { + _imageInfo.Cylinders = (uint)(_imageInfo.Sectors / 16 / 63); + _imageInfo.Heads = 16; + _imageInfo.SectorsPerTrack = 63; + } + + _imageInfo.ReadableMediaTags.AddRange(_mediaTags.Keys); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); + + // Initialize caches + _blockCache = new Dictionary(); + _blockHeaderCache = new Dictionary(); + _currentCacheSize = 0; + + if(!_inMemoryDdt) + _ddtEntryCache = new Dictionary(); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); + + // Initialize tracks, sessions and partitions + if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) + { + if(Tracks != null) + { + bool leadOutFixed = false; + bool sessionPregapFixed = false; + + if(_mediaTags.TryGetValue(MediaTagType.CD_FullTOC, out byte[] fullToc)) + { + byte[] tmp = new byte[fullToc.Length + 2]; + Array.Copy(fullToc, 0, tmp, 2, fullToc.Length); + tmp[0] = (byte)(fullToc.Length >> 8); + tmp[1] = (byte)(fullToc.Length & 0xFF); + + FullTOC.CDFullTOC? decodedFullToc = FullTOC.Decode(tmp); + + if(decodedFullToc.HasValue) + { + Dictionary leadOutStarts = new(); // Lead-out starts + + foreach(FullTOC.TrackDataDescriptor trk in decodedFullToc.Value.TrackDescriptors.Where(trk => (trk.ADR == 1 || trk.ADR == 4) && trk.POINT == 0xA2)) + { + int phour, pmin, psec, pframe; + + if(trk.PFRAME == 0) { - int phour, pmin, psec, pframe; + pframe = 74; - if(trk.PFRAME == 0) + if(trk.PSEC == 0) { - pframe = 74; + psec = 59; - if(trk.PSEC == 0) + if(trk.PMIN == 0) { - psec = 59; - - if(trk.PMIN == 0) - { - pmin = 59; - phour = trk.PHOUR - 1; - } - else - { - pmin = trk.PMIN - 1; - phour = trk.PHOUR; - } + pmin = 59; + phour = trk.PHOUR - 1; } else { - psec = trk.PSEC - 1; - pmin = trk.PMIN; + pmin = trk.PMIN - 1; phour = trk.PHOUR; } } else { - pframe = trk.PFRAME - 1; - psec = trk.PSEC; - pmin = trk.PMIN; - phour = trk.PHOUR; + psec = trk.PSEC - 1; + pmin = trk.PMIN; + phour = trk.PHOUR; } - - int lastSector = (phour * 3600 * 75) + (pmin * 60 * 75) + (psec * 75) + pframe - 150; - leadOutStarts?.Add(trk.SessionNumber, lastSector + 1); } - - foreach(KeyValuePair leadOuts in leadOutStarts) + else { - var lastTrackInSession = new Track(); - - foreach(Track trk in Tracks.Where(trk => trk.Session == leadOuts.Key). - Where(trk => trk.Sequence > lastTrackInSession.Sequence)) - lastTrackInSession = trk; - - if(lastTrackInSession.Sequence == 0 || - lastTrackInSession.EndSector == (ulong)leadOuts.Value - 1) - continue; - - lastTrackInSession.EndSector = (ulong)leadOuts.Value - 1; - leadOutFixed = true; + pframe = trk.PFRAME - 1; + psec = trk.PSEC; + pmin = trk.PMIN; + phour = trk.PHOUR; } - } - } - if(_header.imageMajorVersion <= 1) - { - foreach(Track track in Tracks) + int lastSector = (phour * 3600 * 75) + (pmin * 60 * 75) + (psec * 75) + pframe - 150; + leadOutStarts?.Add(trk.SessionNumber, lastSector + 1); + } + + foreach(KeyValuePair leadOuts in leadOutStarts) { - if(track.Sequence <= 1) + var lastTrackInSession = new Track(); + + foreach(Track trk in Tracks.Where(trk => trk.Session == leadOuts.Key). + Where(trk => trk.Sequence > lastTrackInSession.Sequence)) + lastTrackInSession = trk; + + if(lastTrackInSession.Sequence == 0 || + lastTrackInSession.EndSector == (ulong)leadOuts.Value - 1) continue; - uint firstTrackNumberInSameSession = Tracks. - Where(t => t.Session == track.Session). - Min(t => t.Sequence); - - if(firstTrackNumberInSameSession != track.Sequence) - continue; - - if(track.Pregap == 150) - continue; - - long dif = (long)track.Pregap - 150; - track.Pregap = (ulong)((long)track.Pregap - dif); - track.StartSector = (ulong)((long)track.StartSector + dif); - - sessionPregapFixed = true; + lastTrackInSession.EndSector = (ulong)leadOuts.Value - 1; + leadOutFixed = true; } } - - if(leadOutFixed) - AaruConsole.ErrorWriteLine("This image has a corrupted track list, convert will fix it."); - - if(sessionPregapFixed) - AaruConsole. - ErrorWriteLine("This image has a corrupted track list, a best effort has been tried but may require manual editing or redump."); } - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - if(Tracks == null || - Tracks.Count == 0) + if(_header.imageMajorVersion <= 1) { - Tracks = new List + foreach(Track track in Tracks) { - new() - { - BytesPerSector = (int)_imageInfo.SectorSize, - EndSector = _imageInfo.Sectors - 1, - File = imageFilter.Filename, - FileType = "BINARY", - Filter = imageFilter, - RawBytesPerSector = (int)_imageInfo.SectorSize, - Session = 1, - Sequence = 1, - Type = TrackType.Data - } - }; - - _trackFlags = new Dictionary - { - { - 1, (byte)CdFlags.DataTrack - } - }; - - _trackIsrcs = new Dictionary(); - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - Sessions = new List(); - - for(int i = 1; i <= Tracks.Max(t => t.Session); i++) - Sessions.Add(new Session - { - Sequence = (ushort)i, - StartTrack = Tracks.Where(t => t.Session == i).Min(t => t.Sequence), - EndTrack = Tracks.Where(t => t.Session == i).Max(t => t.Sequence), - StartSector = Tracks.Where(t => t.Session == i).Min(t => t.StartSector), - EndSector = Tracks.Where(t => t.Session == i).Max(t => t.EndSector) - }); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - foreach(Track track in Tracks.OrderBy(t => t.StartSector)) - { - if(track.Sequence == 1) - { - track.Pregap = 150; - track.Indexes[0] = -150; - track.Indexes[1] = (int)track.StartSector; - - continue; - } - - if(track.Pregap > 0) - { - track.Indexes[0] = (int)track.StartSector; - track.Indexes[1] = (int)(track.StartSector + track.Pregap); - } - else - track.Indexes[1] = (int)track.StartSector; - } - - ulong currentTrackOffset = 0; - Partitions = new List(); - - foreach(Track track in Tracks.OrderBy(t => t.StartSector)) - { - Partitions.Add(new Partition - { - Sequence = track.Sequence, - Type = track.Type.ToString(), - Name = $"Track {track.Sequence}", - Offset = currentTrackOffset, - Start = (ulong)track.Indexes[1], - Size = (track.EndSector - (ulong)track.Indexes[1] + 1) * (ulong)track.BytesPerSector, - Length = track.EndSector - (ulong)track.Indexes[1] + 1, - Scheme = "Optical disc track" - }); - - currentTrackOffset += (track.EndSector - track.StartSector + 1) * (ulong)track.BytesPerSector; - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - Track[] tracks = Tracks.ToArray(); - - foreach(Track trk in tracks) - { - ErrorNumber errno = ReadSector(trk.StartSector, out byte[] sector); - - if(errno != ErrorNumber.NoError) - continue; - - trk.BytesPerSector = sector.Length; - - trk.RawBytesPerSector = - (_sectorPrefix != null && _sectorSuffix != null) || - (_sectorPrefixDdt != null && _sectorSuffixDdt != null) ? 2352 : sector.Length; - - if(_sectorSubchannel == null) - continue; - - trk.SubchannelFile = trk.File; - trk.SubchannelFilter = trk.Filter; - trk.SubchannelType = TrackSubchannelType.Raw; - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - Tracks = tracks.ToList(); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - if(compactDiscIndexes != null) - { - foreach(CompactDiscIndexEntry compactDiscIndex in compactDiscIndexes.OrderBy(i => i.Track). - ThenBy(i => i.Index)) - { - Track track = Tracks.FirstOrDefault(t => t.Sequence == compactDiscIndex.Track); - - if(track is null) + if(track.Sequence <= 1) continue; - track.Indexes[compactDiscIndex.Index] = compactDiscIndex.Lba; + uint firstTrackNumberInSameSession = Tracks. + Where(t => t.Session == track.Session). + Min(t => t.Sequence); + + if(firstTrackNumberInSameSession != track.Sequence) + continue; + + if(track.Pregap == 150) + continue; + + long dif = (long)track.Pregap - 150; + track.Pregap = (ulong)((long)track.Pregap - dif); + track.StartSector = (ulong)((long)track.StartSector + dif); + + sessionPregapFixed = true; } } - } - else - { - Tracks = null; - Sessions = null; - Partitions = null; + + if(leadOutFixed) + AaruConsole.ErrorWriteLine("This image has a corrupted track list, convert will fix it."); + + if(sessionPregapFixed) + AaruConsole. + ErrorWriteLine("This image has a corrupted track list, a best effort has been tried but may require manual editing or redump."); } - SetMetadataFromTags(); - - if(_sectorSuffixDdt != null) - EccInit(); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); - - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - return ErrorNumber.NoError; - - if(_imageInfo.MediaType == MediaType.CD || - _imageInfo.MediaType == MediaType.CDDA || - _imageInfo.MediaType == MediaType.CDG || - _imageInfo.MediaType == MediaType.CDEG || - _imageInfo.MediaType == MediaType.CDI || - _imageInfo.MediaType == MediaType.CDROM || - _imageInfo.MediaType == MediaType.CDROMXA || - _imageInfo.MediaType == MediaType.CDPLUS || - _imageInfo.MediaType == MediaType.CDMO || - _imageInfo.MediaType == MediaType.CDR || - _imageInfo.MediaType == MediaType.CDRW || - _imageInfo.MediaType == MediaType.CDMRW || - _imageInfo.MediaType == MediaType.VCD || - _imageInfo.MediaType == MediaType.SVCD || - _imageInfo.MediaType == MediaType.PCD || - _imageInfo.MediaType == MediaType.DTSCD || - _imageInfo.MediaType == MediaType.CDMIDI || - _imageInfo.MediaType == MediaType.CDV || - _imageInfo.MediaType == MediaType.CDIREADY || - _imageInfo.MediaType == MediaType.FMTOWNS || - _imageInfo.MediaType == MediaType.PS1CD || - _imageInfo.MediaType == MediaType.PS2CD || - _imageInfo.MediaType == MediaType.MEGACD || - _imageInfo.MediaType == MediaType.SATURNCD || - _imageInfo.MediaType == MediaType.GDROM || - _imageInfo.MediaType == MediaType.GDR || - _imageInfo.MediaType == MediaType.MilCD || - _imageInfo.MediaType == MediaType.SuperCDROM2 || - _imageInfo.MediaType == MediaType.JaguarCD || - _imageInfo.MediaType == MediaType.ThreeDO || - _imageInfo.MediaType == MediaType.PCFX || - _imageInfo.MediaType == MediaType.NeoGeoCD || - _imageInfo.MediaType == MediaType.CDTV || - _imageInfo.MediaType == MediaType.CD32 || - _imageInfo.MediaType == MediaType.Playdia || - _imageInfo.MediaType == MediaType.Pippin || - _imageInfo.MediaType == MediaType.VideoNow || - _imageInfo.MediaType == MediaType.VideoNowColor || - _imageInfo.MediaType == MediaType.VideoNowXp || - _imageInfo.MediaType == MediaType.CVD) - return ErrorNumber.NoError; + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + if(Tracks == null || + Tracks.Count == 0) { - foreach(Track track in Tracks) + Tracks = new List { - track.Pregap = 0; - track.Indexes?.Clear(); + new() + { + BytesPerSector = (int)_imageInfo.SectorSize, + EndSector = _imageInfo.Sectors - 1, + File = imageFilter.Filename, + FileType = "BINARY", + Filter = imageFilter, + RawBytesPerSector = (int)_imageInfo.SectorSize, + Session = 1, + Sequence = 1, + Type = TrackType.Data + } + }; + + _trackFlags = new Dictionary + { + { + 1, (byte)CdFlags.DataTrack + } + }; + + _trackIsrcs = new Dictionary(); + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + Sessions = new List(); + + for(int i = 1; i <= Tracks.Max(t => t.Session); i++) + Sessions.Add(new Session + { + Sequence = (ushort)i, + StartTrack = Tracks.Where(t => t.Session == i).Min(t => t.Sequence), + EndTrack = Tracks.Where(t => t.Session == i).Max(t => t.Sequence), + StartSector = Tracks.Where(t => t.Session == i).Min(t => t.StartSector), + EndSector = Tracks.Where(t => t.Session == i).Max(t => t.EndSector) + }); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + foreach(Track track in Tracks.OrderBy(t => t.StartSector)) + { + if(track.Sequence == 1) + { + track.Pregap = 150; + track.Indexes[0] = -150; + track.Indexes[1] = (int)track.StartSector; + + continue; + } + + if(track.Pregap > 0) + { + track.Indexes[0] = (int)track.StartSector; + track.Indexes[1] = (int)(track.StartSector + track.Pregap); + } + else + track.Indexes[1] = (int)track.StartSector; + } + + ulong currentTrackOffset = 0; + Partitions = new List(); + + foreach(Track track in Tracks.OrderBy(t => t.StartSector)) + { + Partitions.Add(new Partition + { + Sequence = track.Sequence, + Type = track.Type.ToString(), + Name = $"Track {track.Sequence}", + Offset = currentTrackOffset, + Start = (ulong)track.Indexes[1], + Size = (track.EndSector - (ulong)track.Indexes[1] + 1) * (ulong)track.BytesPerSector, + Length = track.EndSector - (ulong)track.Indexes[1] + 1, + Scheme = "Optical disc track" + }); + + currentTrackOffset += (track.EndSector - track.StartSector + 1) * (ulong)track.BytesPerSector; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + Track[] tracks = Tracks.ToArray(); + + foreach(Track trk in tracks) + { + ErrorNumber errno = ReadSector(trk.StartSector, out byte[] sector); + + if(errno != ErrorNumber.NoError) + continue; + + trk.BytesPerSector = sector.Length; + + trk.RawBytesPerSector = + (_sectorPrefix != null && _sectorSuffix != null) || + (_sectorPrefixDdt != null && _sectorSuffixDdt != null) ? 2352 : sector.Length; + + if(_sectorSubchannel == null) + continue; + + trk.SubchannelFile = trk.File; + trk.SubchannelFilter = trk.Filter; + trk.SubchannelType = TrackSubchannelType.Raw; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + Tracks = tracks.ToList(); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + if(compactDiscIndexes != null) + { + foreach(CompactDiscIndexEntry compactDiscIndex in compactDiscIndexes.OrderBy(i => i.Track). + ThenBy(i => i.Index)) + { + Track track = Tracks.FirstOrDefault(t => t.Sequence == compactDiscIndex.Track); + + if(track is null) + continue; + + track.Indexes[compactDiscIndex.Index] = compactDiscIndex.Lba; } } + } + else + { + Tracks = null; + Sessions = null; + Partitions = null; + } + + SetMetadataFromTags(); + + if(_sectorSuffixDdt != null) + EccInit(); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); + + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + return ErrorNumber.NoError; + + if(_imageInfo.MediaType == MediaType.CD || + _imageInfo.MediaType == MediaType.CDDA || + _imageInfo.MediaType == MediaType.CDG || + _imageInfo.MediaType == MediaType.CDEG || + _imageInfo.MediaType == MediaType.CDI || + _imageInfo.MediaType == MediaType.CDROM || + _imageInfo.MediaType == MediaType.CDROMXA || + _imageInfo.MediaType == MediaType.CDPLUS || + _imageInfo.MediaType == MediaType.CDMO || + _imageInfo.MediaType == MediaType.CDR || + _imageInfo.MediaType == MediaType.CDRW || + _imageInfo.MediaType == MediaType.CDMRW || + _imageInfo.MediaType == MediaType.VCD || + _imageInfo.MediaType == MediaType.SVCD || + _imageInfo.MediaType == MediaType.PCD || + _imageInfo.MediaType == MediaType.DTSCD || + _imageInfo.MediaType == MediaType.CDMIDI || + _imageInfo.MediaType == MediaType.CDV || + _imageInfo.MediaType == MediaType.CDIREADY || + _imageInfo.MediaType == MediaType.FMTOWNS || + _imageInfo.MediaType == MediaType.PS1CD || + _imageInfo.MediaType == MediaType.PS2CD || + _imageInfo.MediaType == MediaType.MEGACD || + _imageInfo.MediaType == MediaType.SATURNCD || + _imageInfo.MediaType == MediaType.GDROM || + _imageInfo.MediaType == MediaType.GDR || + _imageInfo.MediaType == MediaType.MilCD || + _imageInfo.MediaType == MediaType.SuperCDROM2 || + _imageInfo.MediaType == MediaType.JaguarCD || + _imageInfo.MediaType == MediaType.ThreeDO || + _imageInfo.MediaType == MediaType.PCFX || + _imageInfo.MediaType == MediaType.NeoGeoCD || + _imageInfo.MediaType == MediaType.CDTV || + _imageInfo.MediaType == MediaType.CD32 || + _imageInfo.MediaType == MediaType.Playdia || + _imageInfo.MediaType == MediaType.Pippin || + _imageInfo.MediaType == MediaType.VideoNow || + _imageInfo.MediaType == MediaType.VideoNowColor || + _imageInfo.MediaType == MediaType.VideoNowXp || + _imageInfo.MediaType == MediaType.CVD) + return ErrorNumber.NoError; + + { + foreach(Track track in Tracks) + { + track.Pregap = 0; + track.Indexes?.Clear(); + } + } + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer) + { + buffer = null; + + return _mediaTags.TryGetValue(tag, out buffer) ? ErrorNumber.NoError : ErrorNumber.NoData; + } + + /// + public ErrorNumber ReadSector(ulong sectorAddress, out byte[] buffer) + { + buffer = null; + + if(sectorAddress > _imageInfo.Sectors - 1) + return ErrorNumber.OutOfRange; + + ulong ddtEntry = GetDdtEntry(sectorAddress); + uint offsetMask = (uint)((1 << _shift) - 1); + ulong offset = ddtEntry & offsetMask; + ulong blockOffset = ddtEntry >> _shift; + + // Partially written image... as we can't know the real sector size just assume it's common :/ + if(ddtEntry == 0) + { + buffer = new byte[_imageInfo.SectorSize]; return ErrorNumber.NoError; } - /// - public ErrorNumber ReadMediaTag(MediaTagType tag, out byte[] buffer) + // Check if block is cached + if(_blockCache.TryGetValue(blockOffset, out byte[] block) && + _blockHeaderCache.TryGetValue(blockOffset, out BlockHeader blockHeader)) { - buffer = null; - - return _mediaTags.TryGetValue(tag, out buffer) ? ErrorNumber.NoError : ErrorNumber.NoData; - } - - /// - public ErrorNumber ReadSector(ulong sectorAddress, out byte[] buffer) - { - buffer = null; - - if(sectorAddress > _imageInfo.Sectors - 1) - return ErrorNumber.OutOfRange; - - ulong ddtEntry = GetDdtEntry(sectorAddress); - uint offsetMask = (uint)((1 << _shift) - 1); - ulong offset = ddtEntry & offsetMask; - ulong blockOffset = ddtEntry >> _shift; - - // Partially written image... as we can't know the real sector size just assume it's common :/ - if(ddtEntry == 0) - { - buffer = new byte[_imageInfo.SectorSize]; - - return ErrorNumber.NoError; - } - - // Check if block is cached - if(_blockCache.TryGetValue(blockOffset, out byte[] block) && - _blockHeaderCache.TryGetValue(blockOffset, out BlockHeader blockHeader)) - { - buffer = new byte[blockHeader.sectorSize]; - Array.Copy(block, (long)(offset * blockHeader.sectorSize), buffer, 0, blockHeader.sectorSize); - - return ErrorNumber.NoError; - } - - // Read block header - _imageStream.Position = (long)blockOffset; - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - blockHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - - // Decompress block - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); - - ulong decompressedLength; - - switch(blockHeader.compression) - { - case CompressionType.None: - block = new byte[blockHeader.length]; - _imageStream.Read(block, 0, (int)blockHeader.length); - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case CompressionType.Lzma: - byte[] compressedBlock = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; - byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; - _imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); - _imageStream.Read(compressedBlock, 0, compressedBlock.Length); - block = new byte[blockHeader.length]; - decompressedLength = (ulong)LZMA.DecodeBuffer(compressedBlock, block, lzmaProperties); - - if(decompressedLength != blockHeader.length) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Error decompressing block, should be {0} bytes but got {1} bytes.", - blockHeader.length, decompressedLength); - - return ErrorNumber.InOutError; - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - case CompressionType.Flac: - byte[] flacBlock = new byte[blockHeader.cmpLength]; - _imageStream.Read(flacBlock, 0, flacBlock.Length); - block = new byte[blockHeader.length]; - decompressedLength = (ulong)FLAC.DecodeBuffer(flacBlock, block); - - if(decompressedLength != blockHeader.length) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Error decompressing block, should be {0} bytes but got {1} bytes.", - blockHeader.length, decompressedLength); - - return ErrorNumber.InOutError; - } - - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", - GC.GetTotalMemory(false)); - - break; - default: return ErrorNumber.NotSupported; - } - - // Check if cache needs to be emptied - if(_currentCacheSize + blockHeader.length >= MAX_CACHE_SIZE) - { - _currentCacheSize = 0; - _blockHeaderCache = new Dictionary(); - _blockCache = new Dictionary(); - } - - // Add block to cache - _currentCacheSize += blockHeader.length; - _blockHeaderCache.Add(blockOffset, blockHeader); - _blockCache.Add(blockOffset, block); - buffer = new byte[blockHeader.sectorSize]; Array.Copy(block, (long)(offset * blockHeader.sectorSize), buffer, 0, blockHeader.sectorSize); - AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); - return ErrorNumber.NoError; } - /// - public ErrorNumber ReadSectorTag(ulong sectorAddress, SectorTagType tag, out byte[] buffer) => - ReadSectorsTag(sectorAddress, 1, tag, out buffer); + // Read block header + _imageStream.Position = (long)blockOffset; + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + blockHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - /// - public ErrorNumber ReadSector(ulong sectorAddress, uint track, out byte[] buffer) + // Decompress block + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); + + ulong decompressedLength; + + switch(blockHeader.compression) { - buffer = null; + case CompressionType.None: + block = new byte[blockHeader.length]; + _imageStream.Read(block, 0, (int)blockHeader.length); - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - return ErrorNumber.NotSupported; + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); - Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); + break; + case CompressionType.Lzma: + byte[] compressedBlock = new byte[blockHeader.cmpLength - LZMA_PROPERTIES_LENGTH]; + byte[] lzmaProperties = new byte[LZMA_PROPERTIES_LENGTH]; + _imageStream.Read(lzmaProperties, 0, LZMA_PROPERTIES_LENGTH); + _imageStream.Read(compressedBlock, 0, compressedBlock.Length); + block = new byte[blockHeader.length]; + decompressedLength = (ulong)LZMA.DecodeBuffer(compressedBlock, block, lzmaProperties); - return trk?.Sequence != track ? ErrorNumber.SectorNotFound - : ReadSector(trk.StartSector + sectorAddress, out buffer); + if(decompressedLength != blockHeader.length) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Error decompressing block, should be {0} bytes but got {1} bytes.", + blockHeader.length, decompressedLength); + + return ErrorNumber.InOutError; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + case CompressionType.Flac: + byte[] flacBlock = new byte[blockHeader.cmpLength]; + _imageStream.Read(flacBlock, 0, flacBlock.Length); + block = new byte[blockHeader.length]; + decompressedLength = (ulong)FLAC.DecodeBuffer(flacBlock, block); + + if(decompressedLength != blockHeader.length) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Error decompressing block, should be {0} bytes but got {1} bytes.", + blockHeader.length, decompressedLength); + + return ErrorNumber.InOutError; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", + GC.GetTotalMemory(false)); + + break; + default: return ErrorNumber.NotSupported; } - /// - public ErrorNumber ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag, out byte[] buffer) + // Check if cache needs to be emptied + if(_currentCacheSize + blockHeader.length >= MAX_CACHE_SIZE) { - buffer = null; - - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - return ErrorNumber.NotSupported; - - Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); - - return trk?.Sequence != track ? ErrorNumber.SectorNotFound - : ReadSectorTag(trk.StartSector + sectorAddress, tag, out buffer); + _currentCacheSize = 0; + _blockHeaderCache = new Dictionary(); + _blockCache = new Dictionary(); } - /// - public ErrorNumber ReadSectors(ulong sectorAddress, uint length, out byte[] buffer) + // Add block to cache + _currentCacheSize += blockHeader.length; + _blockHeaderCache.Add(blockOffset, blockHeader); + _blockCache.Add(blockOffset, block); + + buffer = new byte[blockHeader.sectorSize]; + Array.Copy(block, (long)(offset * blockHeader.sectorSize), buffer, 0, blockHeader.sectorSize); + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Memory snapshot: {0} bytes", GC.GetTotalMemory(false)); + + return ErrorNumber.NoError; + } + + /// + public ErrorNumber ReadSectorTag(ulong sectorAddress, SectorTagType tag, out byte[] buffer) => + ReadSectorsTag(sectorAddress, 1, tag, out buffer); + + /// + public ErrorNumber ReadSector(ulong sectorAddress, uint track, out byte[] buffer) + { + buffer = null; + + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + return ErrorNumber.NotSupported; + + Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); + + return trk?.Sequence != track ? ErrorNumber.SectorNotFound + : ReadSector(trk.StartSector + sectorAddress, out buffer); + } + + /// + public ErrorNumber ReadSectorTag(ulong sectorAddress, uint track, SectorTagType tag, out byte[] buffer) + { + buffer = null; + + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + return ErrorNumber.NotSupported; + + Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); + + return trk?.Sequence != track ? ErrorNumber.SectorNotFound + : ReadSectorTag(trk.StartSector + sectorAddress, tag, out buffer); + } + + /// + public ErrorNumber ReadSectors(ulong sectorAddress, uint length, out byte[] buffer) + { + buffer = null; + + if(sectorAddress > _imageInfo.Sectors - 1) + return ErrorNumber.OutOfRange; + + if(sectorAddress + length > _imageInfo.Sectors) + return ErrorNumber.OutOfRange; + + var ms = new MemoryStream(); + + for(uint i = 0; i < length; i++) { - buffer = null; + ErrorNumber errno = ReadSector(sectorAddress + i, out byte[] sector); - if(sectorAddress > _imageInfo.Sectors - 1) - return ErrorNumber.OutOfRange; + if(errno != ErrorNumber.NoError) + return errno; - if(sectorAddress + length > _imageInfo.Sectors) - return ErrorNumber.OutOfRange; + ms.Write(sector, 0, sector.Length); + } - var ms = new MemoryStream(); + buffer = ms.ToArray(); - for(uint i = 0; i < length; i++) + return ErrorNumber.NoError; + } + + /// + public ErrorNumber ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag, out byte[] buffer) + { + uint sectorOffset; + uint sectorSize; + uint sectorSkip; + byte[] dataSource; + buffer = null; + + if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) + { + Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && sectorAddress <= t.EndSector); + + if(trk is null) + return ErrorNumber.SectorNotFound; + + if(trk.Sequence == 0 && + trk.StartSector == 0 && + trk.EndSector == 0) + return ErrorNumber.SectorNotFound; + + if(trk.Type == TrackType.Data) + return ErrorNumber.NotSupported; + + switch(tag) { - ErrorNumber errno = ReadSector(sectorAddress + i, out byte[] sector); + case SectorTagType.CdSectorEcc: + case SectorTagType.CdSectorEccP: + case SectorTagType.CdSectorEccQ: + case SectorTagType.CdSectorEdc: + case SectorTagType.CdSectorHeader: + case SectorTagType.CdSectorSubchannel: + case SectorTagType.CdSectorSubHeader: + case SectorTagType.CdSectorSync: + case SectorTagType.DvdCmi: + case SectorTagType.DvdTitleKey: + case SectorTagType.DvdTitleKeyDecrypted: break; + case SectorTagType.CdTrackFlags: + if(!_trackFlags.TryGetValue((byte)sectorAddress, out byte flags)) + return ErrorNumber.NoData; - if(errno != ErrorNumber.NoError) - return errno; + buffer = new[] + { + flags + }; - ms.Write(sector, 0, sector.Length); + return ErrorNumber.NoError; + case SectorTagType.CdTrackIsrc: + if(!_trackIsrcs.TryGetValue((byte)sectorAddress, out string isrc)) + return ErrorNumber.NoData; + + buffer = Encoding.UTF8.GetBytes(isrc); + + return ErrorNumber.NoError; + default: return ErrorNumber.NotSupported; } - buffer = ms.ToArray(); + switch(trk.Type) + { + case TrackType.CdMode1: + switch(tag) + { + case SectorTagType.CdSectorSync: + { + sectorOffset = 0; + sectorSize = 12; + sectorSkip = 4; + dataSource = _sectorPrefix; + + break; + } + + case SectorTagType.CdSectorHeader: + { + sectorOffset = 12; + sectorSize = 4; + sectorSkip = 2336; + dataSource = _sectorPrefix; + + break; + } + + case SectorTagType.CdSectorSubHeader: return ErrorNumber.NotSupported; + case SectorTagType.CdSectorEcc: + { + sectorOffset = 12; + sectorSize = 276; + sectorSkip = 0; + dataSource = _sectorSuffix; + + break; + } + + case SectorTagType.CdSectorEccP: + { + sectorOffset = 12; + sectorSize = 172; + sectorSkip = 104; + dataSource = _sectorSuffix; + + break; + } + + case SectorTagType.CdSectorEccQ: + { + sectorOffset = 184; + sectorSize = 104; + sectorSkip = 0; + dataSource = _sectorSuffix; + + break; + } + + case SectorTagType.CdSectorEdc: + { + sectorOffset = 0; + sectorSize = 4; + sectorSkip = 284; + dataSource = _sectorSuffix; + + break; + } + + case SectorTagType.CdSectorSubchannel: + { + sectorOffset = 0; + sectorSize = 96; + sectorSkip = 0; + dataSource = _sectorSubchannel; + + break; + } + + default: return ErrorNumber.NotSupported; + } + + break; + case TrackType.CdMode2Formless: + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + { + switch(tag) + { + case SectorTagType.CdSectorSync: + { + sectorOffset = 0; + sectorSize = 12; + sectorSkip = 4; + dataSource = _sectorPrefix; + + break; + } + + case SectorTagType.CdSectorHeader: + { + sectorOffset = 12; + sectorSize = 4; + sectorSkip = 0; + dataSource = _sectorPrefix; + + break; + } + + case SectorTagType.CdSectorSubHeader: + { + sectorOffset = 0; + sectorSize = 8; + sectorSkip = 0; + dataSource = _mode2Subheaders; + + break; + } + + // These could be implemented + case SectorTagType.CdSectorEcc: + case SectorTagType.CdSectorEccP: + case SectorTagType.CdSectorEccQ: + case SectorTagType.CdSectorEdc: return ErrorNumber.NotSupported; + case SectorTagType.CdSectorSubchannel: + { + sectorOffset = 0; + sectorSize = 96; + sectorSkip = 0; + dataSource = _sectorSubchannel; + + break; + } + + default: return ErrorNumber.NotSupported; + } + + break; + } + + case TrackType.Audio: + { + switch(tag) + { + case SectorTagType.CdSectorSubchannel: + { + sectorOffset = 0; + sectorSize = 96; + sectorSkip = 0; + dataSource = _sectorSubchannel; + + break; + } + + default: return ErrorNumber.NotSupported; + } + + break; + } + + case TrackType.Data: + { + if(_imageInfo.MediaType == MediaType.DVDROM) + { + switch(tag) + { + case SectorTagType.DvdCmi: + { + sectorOffset = 0; + sectorSize = 1; + sectorSkip = 5; + dataSource = _sectorCpiMai; + + break; + } + case SectorTagType.DvdTitleKey: + { + sectorOffset = 1; + sectorSize = 5; + sectorSkip = 0; + dataSource = _sectorCpiMai; + + break; + } + case SectorTagType.DvdTitleKeyDecrypted: + { + sectorOffset = 0; + sectorSize = 5; + sectorSkip = 0; + dataSource = _sectorDecryptedTitleKey; + + break; + } + default: return ErrorNumber.NotSupported; + } + } + else + return ErrorNumber.NotSupported; + + break; + } + + default: return ErrorNumber.NotSupported; + } + } + else + return ErrorNumber.NoData; + + if(dataSource == null) + return ErrorNumber.NotSupported; + + buffer = new byte[sectorSize * length]; + + if(sectorOffset == 0 && + sectorSkip == 0) + { + Array.Copy(dataSource, (long)(sectorAddress * sectorSize), buffer, 0, length * sectorSize); return ErrorNumber.NoError; } - /// - public ErrorNumber ReadSectorsTag(ulong sectorAddress, uint length, SectorTagType tag, out byte[] buffer) - { - uint sectorOffset; - uint sectorSize; - uint sectorSkip; - byte[] dataSource; - buffer = null; + for(int i = 0; i < length; i++) + Array.Copy(dataSource, (long)(sectorAddress * (sectorOffset + sectorSize + sectorSkip)), buffer, + i * sectorSize, sectorSize); - if(_imageInfo.XmlMediaType == XmlMediaType.OpticalDisc) - { - Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && sectorAddress <= t.EndSector); + return ErrorNumber.NoError; + } + + /// + public ErrorNumber ReadSectors(ulong sectorAddress, uint length, uint track, out byte[] buffer) + { + buffer = null; + + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + return ErrorNumber.NotSupported; + + Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); + + if(trk?.Sequence != track) + return ErrorNumber.SectorNotFound; + + return trk.StartSector + sectorAddress + length > trk.EndSector + 1 ? ErrorNumber.OutOfRange + : ReadSectors(trk.StartSector + sectorAddress, length, out buffer); + } + + /// + public ErrorNumber ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag, + out byte[] buffer) + { + buffer = null; + + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + return ErrorNumber.NotSupported; + + Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); + + return trk?.Sequence != track + ? ErrorNumber.SectorNotFound + : trk.StartSector + sectorAddress + length > trk.EndSector + 1 + ? ErrorNumber.OutOfRange + : ReadSectorsTag(trk.StartSector + sectorAddress, length, tag, out buffer); + } + + /// + public ErrorNumber ReadSectorLong(ulong sectorAddress, out byte[] buffer) + { + ErrorNumber errno; + buffer = null; + + switch(_imageInfo.XmlMediaType) + { + case XmlMediaType.OpticalDisc: + Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && + sectorAddress <= t.EndSector); if(trk is null) return ErrorNumber.SectorNotFound; @@ -1714,729 +2030,412 @@ namespace Aaru.DiscImages trk.EndSector == 0) return ErrorNumber.SectorNotFound; - if(trk.Type == TrackType.Data) - return ErrorNumber.NotSupported; + if((_sectorSuffix == null || _sectorPrefix == null) && + (_sectorSuffixDdt == null || _sectorPrefixDdt == null)) + return ReadSector(sectorAddress, out buffer); - switch(tag) - { - case SectorTagType.CdSectorEcc: - case SectorTagType.CdSectorEccP: - case SectorTagType.CdSectorEccQ: - case SectorTagType.CdSectorEdc: - case SectorTagType.CdSectorHeader: - case SectorTagType.CdSectorSubchannel: - case SectorTagType.CdSectorSubHeader: - case SectorTagType.CdSectorSync: - case SectorTagType.DvdCmi: - case SectorTagType.DvdTitleKey: - case SectorTagType.DvdTitleKeyDecrypted: break; - case SectorTagType.CdTrackFlags: - if(!_trackFlags.TryGetValue((byte)sectorAddress, out byte flags)) - return ErrorNumber.NoData; + buffer = new byte[2352]; + errno = ReadSector(sectorAddress, out byte[] data); - buffer = new[] - { - flags - }; - - return ErrorNumber.NoError; - case SectorTagType.CdTrackIsrc: - if(!_trackIsrcs.TryGetValue((byte)sectorAddress, out string isrc)) - return ErrorNumber.NoData; - - buffer = Encoding.UTF8.GetBytes(isrc); - - return ErrorNumber.NoError; - default: return ErrorNumber.NotSupported; - } + if(errno != ErrorNumber.NoError) + return errno; switch(trk.Type) { - case TrackType.CdMode1: - switch(tag) - { - case SectorTagType.CdSectorSync: - { - sectorOffset = 0; - sectorSize = 12; - sectorSkip = 4; - dataSource = _sectorPrefix; - - break; - } - - case SectorTagType.CdSectorHeader: - { - sectorOffset = 12; - sectorSize = 4; - sectorSkip = 2336; - dataSource = _sectorPrefix; - - break; - } - - case SectorTagType.CdSectorSubHeader: return ErrorNumber.NotSupported; - case SectorTagType.CdSectorEcc: - { - sectorOffset = 12; - sectorSize = 276; - sectorSkip = 0; - dataSource = _sectorSuffix; - - break; - } - - case SectorTagType.CdSectorEccP: - { - sectorOffset = 12; - sectorSize = 172; - sectorSkip = 104; - dataSource = _sectorSuffix; - - break; - } - - case SectorTagType.CdSectorEccQ: - { - sectorOffset = 184; - sectorSize = 104; - sectorSkip = 0; - dataSource = _sectorSuffix; - - break; - } - - case SectorTagType.CdSectorEdc: - { - sectorOffset = 0; - sectorSize = 4; - sectorSkip = 284; - dataSource = _sectorSuffix; - - break; - } - - case SectorTagType.CdSectorSubchannel: - { - sectorOffset = 0; - sectorSize = 96; - sectorSkip = 0; - dataSource = _sectorSubchannel; - - break; - } - - default: return ErrorNumber.NotSupported; - } - - break; - case TrackType.CdMode2Formless: - case TrackType.CdMode2Form1: - case TrackType.CdMode2Form2: - { - switch(tag) - { - case SectorTagType.CdSectorSync: - { - sectorOffset = 0; - sectorSize = 12; - sectorSkip = 4; - dataSource = _sectorPrefix; - - break; - } - - case SectorTagType.CdSectorHeader: - { - sectorOffset = 12; - sectorSize = 4; - sectorSkip = 0; - dataSource = _sectorPrefix; - - break; - } - - case SectorTagType.CdSectorSubHeader: - { - sectorOffset = 0; - sectorSize = 8; - sectorSkip = 0; - dataSource = _mode2Subheaders; - - break; - } - - // These could be implemented - case SectorTagType.CdSectorEcc: - case SectorTagType.CdSectorEccP: - case SectorTagType.CdSectorEccQ: - case SectorTagType.CdSectorEdc: return ErrorNumber.NotSupported; - case SectorTagType.CdSectorSubchannel: - { - sectorOffset = 0; - sectorSize = 96; - sectorSkip = 0; - dataSource = _sectorSubchannel; - - break; - } - - default: return ErrorNumber.NotSupported; - } - - break; - } - case TrackType.Audio: - { - switch(tag) - { - case SectorTagType.CdSectorSubchannel: - { - sectorOffset = 0; - sectorSize = 96; - sectorSkip = 0; - dataSource = _sectorSubchannel; - - break; - } - - default: return ErrorNumber.NotSupported; - } - - break; - } - case TrackType.Data: - { - if(_imageInfo.MediaType == MediaType.DVDROM) + buffer = data; + + return ErrorNumber.NoError; + case TrackType.CdMode1: + Array.Copy(data, 0, buffer, 16, 2048); + + if(_sectorPrefix != null) + Array.Copy(_sectorPrefix, (int)sectorAddress * 16, buffer, 0, 16); + else if(_sectorPrefixDdt != null) { - switch(tag) + if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct) + ReconstructPrefix(ref buffer, trk.Type, (long)sectorAddress); + else if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == + (uint)CdFixFlags.NotDumped || + _sectorPrefixDdt[sectorAddress] == 0) { - case SectorTagType.DvdCmi: - { - sectorOffset = 0; - sectorSize = 1; - sectorSkip = 5; - dataSource = _sectorCpiMai; + // Do nothing + } + else + { + uint prefixPosition = ((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16; - break; - } - case SectorTagType.DvdTitleKey: - { - sectorOffset = 1; - sectorSize = 5; - sectorSkip = 0; - dataSource = _sectorCpiMai; + if(prefixPosition > _sectorPrefixMs.Length) + return ErrorNumber.InvalidArgument; - break; - } - case SectorTagType.DvdTitleKeyDecrypted: - { - sectorOffset = 0; - sectorSize = 5; - sectorSkip = 0; - dataSource = _sectorDecryptedTitleKey; + _sectorPrefixMs.Position = prefixPosition; - break; - } - default: return ErrorNumber.NotSupported; + _sectorPrefixMs.Read(buffer, 0, 16); } } else - return ErrorNumber.NotSupported; + return ErrorNumber.InvalidArgument; - break; - } + if(_sectorSuffix != null) + Array.Copy(_sectorSuffix, (int)sectorAddress * 288, buffer, 2064, 288); + else if(_sectorSuffixDdt != null) + { + if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct) + ReconstructEcc(ref buffer, trk.Type); + else if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == + (uint)CdFixFlags.NotDumped || + _sectorSuffixDdt[sectorAddress] == 0) + { + // Do nothing + } + else + { + uint suffixPosition = ((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288; - default: return ErrorNumber.NotSupported; + if(suffixPosition > _sectorSuffixMs.Length) + return ErrorNumber.InvalidArgument; + + _sectorSuffixMs.Position = suffixPosition; + + _sectorSuffixMs.Read(buffer, 2064, 288); + } + } + else + return ErrorNumber.InvalidArgument; + + return ErrorNumber.NoError; + case TrackType.CdMode2Formless: + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + if(_sectorPrefix != null) + Array.Copy(_sectorPrefix, (int)sectorAddress * 16, buffer, 0, 16); + else if(_sectorPrefixMs != null) + { + if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct) + ReconstructPrefix(ref buffer, trk.Type, (long)sectorAddress); + else if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == + (uint)CdFixFlags.NotDumped || + _sectorPrefixDdt[sectorAddress] == 0) + { + // Do nothing + } + else + { + uint prefixPosition = ((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16; + + if(prefixPosition > _sectorPrefixMs.Length) + return ErrorNumber.InvalidArgument; + + _sectorPrefixMs.Position = prefixPosition; + + _sectorPrefixMs.Read(buffer, 0, 16); + } + } + else + return ErrorNumber.InvalidArgument; + + if(_mode2Subheaders != null && + _sectorSuffixDdt != null) + { + Array.Copy(_mode2Subheaders, (int)sectorAddress * 8, buffer, 16, 8); + + switch(_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) + { + case (uint)CdFixFlags.Mode2Form1Ok: + Array.Copy(data, 0, buffer, 24, 2048); + ReconstructEcc(ref buffer, TrackType.CdMode2Form1); + + break; + case (uint)CdFixFlags.Mode2Form2Ok: + case (uint)CdFixFlags.Mode2Form2NoCrc: + { + Array.Copy(data, 0, buffer, 24, 2324); + + if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == + (uint)CdFixFlags.Mode2Form2Ok) + ReconstructEcc(ref buffer, TrackType.CdMode2Form2); + + break; + } + default: + { + if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == + (uint)CdFixFlags.NotDumped || + _sectorSuffixDdt[sectorAddress] == 0) + { + // Do nothing + } + else // Mode 2 where EDC failed + { + // Incorrectly written images + if(data.Length == 2328) + { + Array.Copy(data, 0, buffer, 24, 2328); + } + else + { + bool form2 = (buffer[18] & 0x20) == 0x20 || (buffer[22] & 0x20) == 0x20; + + uint suffixPosition = + ((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288; + + if(suffixPosition > _sectorSuffixMs.Length) + return ErrorNumber.InvalidArgument; + + _sectorSuffixMs.Position = suffixPosition; + + _sectorSuffixMs.Read(buffer, form2 ? 2348 : 2072, form2 ? 4 : 280); + Array.Copy(data, 0, buffer, 24, form2 ? 2324 : 2048); + } + } + + break; + } + } + } + else if(_mode2Subheaders != null) + { + Array.Copy(_mode2Subheaders, (int)sectorAddress * 8, buffer, 16, 8); + Array.Copy(data, 0, buffer, 24, 2328); + } + else + Array.Copy(data, 0, buffer, 16, 2336); + + return ErrorNumber.NoError; } - } - else - return ErrorNumber.NoData; - if(dataSource == null) - return ErrorNumber.NotSupported; + break; + case XmlMediaType.BlockMedia: + switch(_imageInfo.MediaType) + { + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleSonySS: + case MediaType.AppleSonyDS: + case MediaType.AppleWidget: + case MediaType.PriamDataTower: return ReadSectorsLong(sectorAddress, 1, out buffer); + } - buffer = new byte[sectorSize * length]; - - if(sectorOffset == 0 && - sectorSkip == 0) - { - Array.Copy(dataSource, (long)(sectorAddress * sectorSize), buffer, 0, length * sectorSize); - - return ErrorNumber.NoError; - } - - for(int i = 0; i < length; i++) - Array.Copy(dataSource, (long)(sectorAddress * (sectorOffset + sectorSize + sectorSkip)), buffer, - i * sectorSize, sectorSize); - - return ErrorNumber.NoError; + break; } - /// - public ErrorNumber ReadSectors(ulong sectorAddress, uint length, uint track, out byte[] buffer) - { - buffer = null; + return ErrorNumber.NotSupported; + } - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - return ErrorNumber.NotSupported; - - Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); - - if(trk?.Sequence != track) - return ErrorNumber.SectorNotFound; - - return trk.StartSector + sectorAddress + length > trk.EndSector + 1 ? ErrorNumber.OutOfRange - : ReadSectors(trk.StartSector + sectorAddress, length, out buffer); - } - - /// - public ErrorNumber ReadSectorsTag(ulong sectorAddress, uint length, uint track, SectorTagType tag, - out byte[] buffer) - { - buffer = null; - - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - return ErrorNumber.NotSupported; - - Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); - - return trk?.Sequence != track - ? ErrorNumber.SectorNotFound - : trk.StartSector + sectorAddress + length > trk.EndSector + 1 - ? ErrorNumber.OutOfRange - : ReadSectorsTag(trk.StartSector + sectorAddress, length, tag, out buffer); - } - - /// - public ErrorNumber ReadSectorLong(ulong sectorAddress, out byte[] buffer) - { - ErrorNumber errno; - buffer = null; - - switch(_imageInfo.XmlMediaType) - { - case XmlMediaType.OpticalDisc: - Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && - sectorAddress <= t.EndSector); - - if(trk is null) - return ErrorNumber.SectorNotFound; - - if(trk.Sequence == 0 && - trk.StartSector == 0 && - trk.EndSector == 0) - return ErrorNumber.SectorNotFound; - - if((_sectorSuffix == null || _sectorPrefix == null) && - (_sectorSuffixDdt == null || _sectorPrefixDdt == null)) - return ReadSector(sectorAddress, out buffer); - - buffer = new byte[2352]; - errno = ReadSector(sectorAddress, out byte[] data); - - if(errno != ErrorNumber.NoError) - return errno; - - switch(trk.Type) - { - case TrackType.Audio: - case TrackType.Data: - buffer = data; - - return ErrorNumber.NoError; - case TrackType.CdMode1: - Array.Copy(data, 0, buffer, 16, 2048); - - if(_sectorPrefix != null) - Array.Copy(_sectorPrefix, (int)sectorAddress * 16, buffer, 0, 16); - else if(_sectorPrefixDdt != null) - { - if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct) - ReconstructPrefix(ref buffer, trk.Type, (long)sectorAddress); - else if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == - (uint)CdFixFlags.NotDumped || - _sectorPrefixDdt[sectorAddress] == 0) - { - // Do nothing - } - else - { - uint prefixPosition = ((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16; - - if(prefixPosition > _sectorPrefixMs.Length) - return ErrorNumber.InvalidArgument; - - _sectorPrefixMs.Position = prefixPosition; - - _sectorPrefixMs.Read(buffer, 0, 16); - } - } - else - return ErrorNumber.InvalidArgument; - - if(_sectorSuffix != null) - Array.Copy(_sectorSuffix, (int)sectorAddress * 288, buffer, 2064, 288); - else if(_sectorSuffixDdt != null) - { - if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct) - ReconstructEcc(ref buffer, trk.Type); - else if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == - (uint)CdFixFlags.NotDumped || - _sectorSuffixDdt[sectorAddress] == 0) - { - // Do nothing - } - else - { - uint suffixPosition = ((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288; - - if(suffixPosition > _sectorSuffixMs.Length) - return ErrorNumber.InvalidArgument; - - _sectorSuffixMs.Position = suffixPosition; - - _sectorSuffixMs.Read(buffer, 2064, 288); - } - } - else - return ErrorNumber.InvalidArgument; - - return ErrorNumber.NoError; - case TrackType.CdMode2Formless: - case TrackType.CdMode2Form1: - case TrackType.CdMode2Form2: - if(_sectorPrefix != null) - Array.Copy(_sectorPrefix, (int)sectorAddress * 16, buffer, 0, 16); - else if(_sectorPrefixMs != null) - { - if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == (uint)CdFixFlags.Correct) - ReconstructPrefix(ref buffer, trk.Type, (long)sectorAddress); - else if((_sectorPrefixDdt[sectorAddress] & CD_XFIX_MASK) == - (uint)CdFixFlags.NotDumped || - _sectorPrefixDdt[sectorAddress] == 0) - { - // Do nothing - } - else - { - uint prefixPosition = ((_sectorPrefixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 16; - - if(prefixPosition > _sectorPrefixMs.Length) - return ErrorNumber.InvalidArgument; - - _sectorPrefixMs.Position = prefixPosition; - - _sectorPrefixMs.Read(buffer, 0, 16); - } - } - else - return ErrorNumber.InvalidArgument; - - if(_mode2Subheaders != null && - _sectorSuffixDdt != null) - { - Array.Copy(_mode2Subheaders, (int)sectorAddress * 8, buffer, 16, 8); - - switch(_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) - { - case (uint)CdFixFlags.Mode2Form1Ok: - Array.Copy(data, 0, buffer, 24, 2048); - ReconstructEcc(ref buffer, TrackType.CdMode2Form1); - - break; - case (uint)CdFixFlags.Mode2Form2Ok: - case (uint)CdFixFlags.Mode2Form2NoCrc: - { - Array.Copy(data, 0, buffer, 24, 2324); - - if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == - (uint)CdFixFlags.Mode2Form2Ok) - ReconstructEcc(ref buffer, TrackType.CdMode2Form2); - - break; - } - default: - { - if((_sectorSuffixDdt[sectorAddress] & CD_XFIX_MASK) == - (uint)CdFixFlags.NotDumped || - _sectorSuffixDdt[sectorAddress] == 0) - { - // Do nothing - } - else // Mode 2 where EDC failed - { - // Incorrectly written images - if(data.Length == 2328) - { - Array.Copy(data, 0, buffer, 24, 2328); - } - else - { - bool form2 = (buffer[18] & 0x20) == 0x20 || (buffer[22] & 0x20) == 0x20; - - uint suffixPosition = - ((_sectorSuffixDdt[sectorAddress] & CD_DFIX_MASK) - 1) * 288; - - if(suffixPosition > _sectorSuffixMs.Length) - return ErrorNumber.InvalidArgument; - - _sectorSuffixMs.Position = suffixPosition; - - _sectorSuffixMs.Read(buffer, form2 ? 2348 : 2072, form2 ? 4 : 280); - Array.Copy(data, 0, buffer, 24, form2 ? 2324 : 2048); - } - } - - break; - } - } - } - else if(_mode2Subheaders != null) - { - Array.Copy(_mode2Subheaders, (int)sectorAddress * 8, buffer, 16, 8); - Array.Copy(data, 0, buffer, 24, 2328); - } - else - Array.Copy(data, 0, buffer, 16, 2336); - - return ErrorNumber.NoError; - } - - break; - case XmlMediaType.BlockMedia: - switch(_imageInfo.MediaType) - { - case MediaType.AppleFileWare: - case MediaType.AppleProfile: - case MediaType.AppleSonySS: - case MediaType.AppleSonyDS: - case MediaType.AppleWidget: - case MediaType.PriamDataTower: return ReadSectorsLong(sectorAddress, 1, out buffer); - } - - break; - } + /// + public ErrorNumber ReadSectorLong(ulong sectorAddress, uint track, out byte[] buffer) + { + buffer = null; + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) return ErrorNumber.NotSupported; - } - /// - public ErrorNumber ReadSectorLong(ulong sectorAddress, uint track, out byte[] buffer) + Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); + + return trk?.Sequence != track ? ErrorNumber.SectorNotFound + : ReadSectorLong(trk.StartSector + sectorAddress, out buffer); + } + + /// + public ErrorNumber ReadSectorsLong(ulong sectorAddress, uint length, out byte[] buffer) + { + byte[] data; + ErrorNumber errno; + buffer = null; + + switch(_imageInfo.XmlMediaType) { - buffer = null; + case XmlMediaType.OpticalDisc: + Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && + sectorAddress <= t.EndSector); - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - return ErrorNumber.NotSupported; + if(trk is null) + return ErrorNumber.SectorNotFound; - Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); + if(trk.Sequence == 0 && + trk.StartSector == 0 && + trk.EndSector == 0) + return ErrorNumber.SectorNotFound; - return trk?.Sequence != track ? ErrorNumber.SectorNotFound - : ReadSectorLong(trk.StartSector + sectorAddress, out buffer); - } + if(sectorAddress + length > trk.EndSector + 1) + return ErrorNumber.OutOfRange; - /// - public ErrorNumber ReadSectorsLong(ulong sectorAddress, uint length, out byte[] buffer) - { - byte[] data; - ErrorNumber errno; - buffer = null; + switch(trk.Type) + { + // These types only contain user data + case TrackType.Audio: + case TrackType.Data: return ReadSectors(sectorAddress, length, out buffer); - switch(_imageInfo.XmlMediaType) - { - case XmlMediaType.OpticalDisc: - Track trk = Tracks.FirstOrDefault(t => sectorAddress >= t.StartSector && - sectorAddress <= t.EndSector); - - if(trk is null) - return ErrorNumber.SectorNotFound; - - if(trk.Sequence == 0 && - trk.StartSector == 0 && - trk.EndSector == 0) - return ErrorNumber.SectorNotFound; - - if(sectorAddress + length > trk.EndSector + 1) - return ErrorNumber.OutOfRange; - - switch(trk.Type) - { - // These types only contain user data - case TrackType.Audio: - case TrackType.Data: return ReadSectors(sectorAddress, length, out buffer); - - // Join prefix (sync, header) with user data with suffix (edc, ecc p, ecc q) - case TrackType.CdMode1: - if(_sectorPrefix != null && - _sectorSuffix != null) - { - buffer = new byte[2352 * length]; - errno = ReadSectors(sectorAddress, length, out data); - - if(errno != ErrorNumber.NoError) - return errno; - - for(uint i = 0; i < length; i++) - { - Array.Copy(_sectorPrefix, (int)((sectorAddress + i) * 16), buffer, (int)(i * 2352), - 16); - - Array.Copy(data, (int)(i * 2048), buffer, (int)(i * 2352) + 16, 2048); - - Array.Copy(_sectorSuffix, (int)((sectorAddress + i) * 288), buffer, - (int)(i * 2352) + 2064, 288); - } - - return ErrorNumber.NoError; - } - else if(_sectorPrefixDdt != null && - _sectorSuffixDdt != null) - { - buffer = new byte[2352 * length]; - - for(uint i = 0; i < length; i++) - { - errno = ReadSectorLong(sectorAddress + i, out byte[] temp); - - if(errno != ErrorNumber.NoError) - return errno; - - Array.Copy(temp, 0, buffer, 2352 * i, 2352); - } - - return ErrorNumber.NoError; - } - else - return ReadSectors(sectorAddress, length, out buffer); - - // Join prefix (sync, header) with user data - case TrackType.CdMode2Formless: - case TrackType.CdMode2Form1: - case TrackType.CdMode2Form2: - if(_sectorPrefix != null && - _sectorSuffix != null) - { - buffer = new byte[2352 * length]; - errno = ReadSectors(sectorAddress, length, out data); - - if(errno != ErrorNumber.NoError) - return errno; - - for(uint i = 0; i < length; i++) - { - Array.Copy(_sectorPrefix, (int)((sectorAddress + i) * 16), buffer, (int)(i * 2352), - 16); - - Array.Copy(data, (int)(i * 2336), buffer, (int)(i * 2352) + 16, 2336); - } - - return ErrorNumber.NoError; - } - else if(_sectorPrefixDdt != null && - _sectorSuffixDdt != null) - { - buffer = new byte[2352 * length]; - - for(uint i = 0; i < length; i++) - { - errno = ReadSectorLong(sectorAddress + i, out byte[] temp); - - if(errno != ErrorNumber.NoError) - return errno; - - Array.Copy(temp, 0, buffer, 2352 * i, 2352); - } - - return ErrorNumber.NoError; - } - - return ReadSectors(sectorAddress, length, out buffer); - } - - break; - case XmlMediaType.BlockMedia: - switch(_imageInfo.MediaType) - { - // Join user data with tags - case MediaType.AppleFileWare: - case MediaType.AppleProfile: - case MediaType.AppleSonySS: - case MediaType.AppleSonyDS: - case MediaType.AppleWidget: - case MediaType.PriamDataTower: - if(_sectorSubchannel == null) - return ReadSector(sectorAddress, out buffer); - - uint tagSize = 0; - - switch(_imageInfo.MediaType) - { - case MediaType.AppleFileWare: - case MediaType.AppleProfile: - case MediaType.AppleWidget: - tagSize = 20; - - break; - case MediaType.AppleSonySS: - case MediaType.AppleSonyDS: - tagSize = 12; - - break; - case MediaType.PriamDataTower: - tagSize = 24; - - break; - } - - uint sectorSize = 512 + tagSize; - errno = ReadSectors(sectorAddress, length, out data); + // Join prefix (sync, header) with user data with suffix (edc, ecc p, ecc q) + case TrackType.CdMode1: + if(_sectorPrefix != null && + _sectorSuffix != null) + { + buffer = new byte[2352 * length]; + errno = ReadSectors(sectorAddress, length, out data); if(errno != ErrorNumber.NoError) return errno; - buffer = new byte[(sectorSize + 512) * length]; - for(uint i = 0; i < length; i++) { - Array.Copy(_sectorSubchannel, (int)((sectorAddress + i) * tagSize), buffer, - (int)((i * sectorSize) + 512), tagSize); + Array.Copy(_sectorPrefix, (int)((sectorAddress + i) * 16), buffer, (int)(i * 2352), + 16); - Array.Copy(data, (int)((sectorAddress + i) * 512), buffer, (int)(i * 512), 512); + Array.Copy(data, (int)(i * 2048), buffer, (int)(i * 2352) + 16, 2048); + + Array.Copy(_sectorSuffix, (int)((sectorAddress + i) * 288), buffer, + (int)(i * 2352) + 2064, 288); } return ErrorNumber.NoError; - } + } + else if(_sectorPrefixDdt != null && + _sectorSuffixDdt != null) + { + buffer = new byte[2352 * length]; - break; - } + for(uint i = 0; i < length; i++) + { + errno = ReadSectorLong(sectorAddress + i, out byte[] temp); - return ErrorNumber.NotSupported; + if(errno != ErrorNumber.NoError) + return errno; + + Array.Copy(temp, 0, buffer, 2352 * i, 2352); + } + + return ErrorNumber.NoError; + } + else + return ReadSectors(sectorAddress, length, out buffer); + + // Join prefix (sync, header) with user data + case TrackType.CdMode2Formless: + case TrackType.CdMode2Form1: + case TrackType.CdMode2Form2: + if(_sectorPrefix != null && + _sectorSuffix != null) + { + buffer = new byte[2352 * length]; + errno = ReadSectors(sectorAddress, length, out data); + + if(errno != ErrorNumber.NoError) + return errno; + + for(uint i = 0; i < length; i++) + { + Array.Copy(_sectorPrefix, (int)((sectorAddress + i) * 16), buffer, (int)(i * 2352), + 16); + + Array.Copy(data, (int)(i * 2336), buffer, (int)(i * 2352) + 16, 2336); + } + + return ErrorNumber.NoError; + } + else if(_sectorPrefixDdt != null && + _sectorSuffixDdt != null) + { + buffer = new byte[2352 * length]; + + for(uint i = 0; i < length; i++) + { + errno = ReadSectorLong(sectorAddress + i, out byte[] temp); + + if(errno != ErrorNumber.NoError) + return errno; + + Array.Copy(temp, 0, buffer, 2352 * i, 2352); + } + + return ErrorNumber.NoError; + } + + return ReadSectors(sectorAddress, length, out buffer); + } + + break; + case XmlMediaType.BlockMedia: + switch(_imageInfo.MediaType) + { + // Join user data with tags + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleSonySS: + case MediaType.AppleSonyDS: + case MediaType.AppleWidget: + case MediaType.PriamDataTower: + if(_sectorSubchannel == null) + return ReadSector(sectorAddress, out buffer); + + uint tagSize = 0; + + switch(_imageInfo.MediaType) + { + case MediaType.AppleFileWare: + case MediaType.AppleProfile: + case MediaType.AppleWidget: + tagSize = 20; + + break; + case MediaType.AppleSonySS: + case MediaType.AppleSonyDS: + tagSize = 12; + + break; + case MediaType.PriamDataTower: + tagSize = 24; + + break; + } + + uint sectorSize = 512 + tagSize; + errno = ReadSectors(sectorAddress, length, out data); + + if(errno != ErrorNumber.NoError) + return errno; + + buffer = new byte[(sectorSize + 512) * length]; + + for(uint i = 0; i < length; i++) + { + Array.Copy(_sectorSubchannel, (int)((sectorAddress + i) * tagSize), buffer, + (int)((i * sectorSize) + 512), tagSize); + + Array.Copy(data, (int)((sectorAddress + i) * 512), buffer, (int)(i * 512), 512); + } + + return ErrorNumber.NoError; + } + + break; } - /// - public ErrorNumber ReadSectorsLong(ulong sectorAddress, uint length, uint track, out byte[] buffer) - { - buffer = null; - - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - return ErrorNumber.NotSupported; - - Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); - - return trk?.Sequence != track - ? ErrorNumber.SectorNotFound - : trk.StartSector + sectorAddress + length > trk.EndSector + 1 - ? ErrorNumber.OutOfRange - : ReadSectorsLong(trk.StartSector + sectorAddress, length, out buffer); - } - - /// - public List GetSessionTracks(Session session) => - Tracks.Where(t => t.Sequence == session.Sequence).ToList(); - - /// - public List GetSessionTracks(ushort session) => Tracks.Where(t => t.Sequence == session).ToList(); + return ErrorNumber.NotSupported; } + + /// + public ErrorNumber ReadSectorsLong(ulong sectorAddress, uint length, uint track, out byte[] buffer) + { + buffer = null; + + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + return ErrorNumber.NotSupported; + + Track trk = Tracks.FirstOrDefault(t => t.Sequence == track); + + return trk?.Sequence != track + ? ErrorNumber.SectorNotFound + : trk.StartSector + sectorAddress + length > trk.EndSector + 1 + ? ErrorNumber.OutOfRange + : ReadSectorsLong(trk.StartSector + sectorAddress, length, out buffer); + } + + /// + public List GetSessionTracks(Session session) => + Tracks.Where(t => t.Sequence == session.Sequence).ToList(); + + /// + public List GetSessionTracks(ushort session) => Tracks.Where(t => t.Sequence == session).ToList(); } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Structs.cs b/Aaru.Images/AaruFormat/Structs.cs index 1cedb2a09..5717acd46 100644 --- a/Aaru.Images/AaruFormat/Structs.cs +++ b/Aaru.Images/AaruFormat/Structs.cs @@ -34,379 +34,378 @@ using System.Runtime.InteropServices; using Aaru.CommonTypes; using Aaru.CommonTypes.Enums; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + /// Header, at start of file + [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)] + struct AaruHeader { - /// Header, at start of file - [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Unicode)] - struct AaruHeader - { - /// Header identifier, - public ulong identifier; - /// UTF-16LE name of the application that created the image - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] - public string application; - /// Image format major version. A new major version means a possibly incompatible change of format - public byte imageMajorVersion; - /// Image format minor version. A new minor version indicates a compatible change of format - public byte imageMinorVersion; - /// Major version of the application that created the image - public byte applicationMajorVersion; - /// Minor version of the application that created the image - public byte applicationMinorVersion; - /// Type of media contained on image - public MediaType mediaType; - /// Offset to index - public ulong indexOffset; - /// Windows filetime (100 nanoseconds since 1601/01/01 00:00:00 UTC) of image creation time - public long creationTime; - /// Windows filetime (100 nanoseconds since 1601/01/01 00:00:00 UTC) of image last written time - public long lastWrittenTime; - } + /// Header identifier, + public ulong identifier; + /// UTF-16LE name of the application that created the image + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] + public string application; + /// Image format major version. A new major version means a possibly incompatible change of format + public byte imageMajorVersion; + /// Image format minor version. A new minor version indicates a compatible change of format + public byte imageMinorVersion; + /// Major version of the application that created the image + public byte applicationMajorVersion; + /// Minor version of the application that created the image + public byte applicationMinorVersion; + /// Type of media contained on image + public MediaType mediaType; + /// Offset to index + public ulong indexOffset; + /// Windows filetime (100 nanoseconds since 1601/01/01 00:00:00 UTC) of image creation time + public long creationTime; + /// Windows filetime (100 nanoseconds since 1601/01/01 00:00:00 UTC) of image last written time + public long lastWrittenTime; + } - /// Header for a deduplication table. Table follows it - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct DdtHeader - { - /// Identifier, - public BlockType identifier; - /// Type of data pointed by this DDT - public DataType type; - /// Compression algorithm used to compress the DDT - public CompressionType compression; - /// Each entry is ((byte offset in file) << shift) + (sector offset in block) - public byte shift; - /// How many entries are in the table - public ulong entries; - /// Compressed length for the DDT - public ulong cmpLength; - /// Uncompressed length for the DDT - public ulong length; - /// CRC64-ECMA of the compressed DDT - public ulong cmpCrc64; - /// CRC64-ECMA of the uncompressed DDT - public readonly ulong crc64; - } + /// Header for a deduplication table. Table follows it + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DdtHeader + { + /// Identifier, + public BlockType identifier; + /// Type of data pointed by this DDT + public DataType type; + /// Compression algorithm used to compress the DDT + public CompressionType compression; + /// Each entry is ((byte offset in file) << shift) + (sector offset in block) + public byte shift; + /// How many entries are in the table + public ulong entries; + /// Compressed length for the DDT + public ulong cmpLength; + /// Uncompressed length for the DDT + public ulong length; + /// CRC64-ECMA of the compressed DDT + public ulong cmpCrc64; + /// CRC64-ECMA of the uncompressed DDT + public readonly ulong crc64; + } - /// Header for the index, followed by entries - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct IndexHeader - { - /// Identifier, - public BlockType identifier; - /// How many entries follow this header - public ushort entries; - /// CRC64-ECMA of the index - public ulong crc64; - } + /// Header for the index, followed by entries + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct IndexHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public ushort entries; + /// CRC64-ECMA of the index + public ulong crc64; + } - /// Header for the index, followed by entries - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct IndexHeader2 - { - /// Identifier, - public BlockType identifier; - /// How many entries follow this header - public ulong entries; - /// CRC64-ECMA of the index - public ulong crc64; - } + /// Header for the index, followed by entries + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct IndexHeader2 + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public ulong entries; + /// CRC64-ECMA of the index + public ulong crc64; + } - /// Index entry - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct IndexEntry - { - /// Type of item pointed by this entry - public BlockType blockType; - /// Type of data contained by the block pointed by this entry - public DataType dataType; - /// Offset in file where item is stored - public ulong offset; - } + /// Index entry + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct IndexEntry + { + /// Type of item pointed by this entry + public BlockType blockType; + /// Type of data contained by the block pointed by this entry + public DataType dataType; + /// Offset in file where item is stored + public ulong offset; + } - /// Block header, precedes block data - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct BlockHeader - { - /// Identifier, - public BlockType identifier; - /// Type of data contained by this block - public DataType type; - /// Compression algorithm used to compress the block - public CompressionType compression; - /// Size in bytes of each sector contained in this block - public uint sectorSize; - /// Compressed length for the block - public uint cmpLength; - /// Uncompressed length for the block - public uint length; - /// CRC64-ECMA of the compressed block - public ulong cmpCrc64; - /// CRC64-ECMA of the uncompressed block - public ulong crc64; - } + /// Block header, precedes block data + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct BlockHeader + { + /// Identifier, + public BlockType identifier; + /// Type of data contained by this block + public DataType type; + /// Compression algorithm used to compress the block + public CompressionType compression; + /// Size in bytes of each sector contained in this block + public uint sectorSize; + /// Compressed length for the block + public uint cmpLength; + /// Uncompressed length for the block + public uint length; + /// CRC64-ECMA of the compressed block + public ulong cmpCrc64; + /// CRC64-ECMA of the uncompressed block + public ulong crc64; + } - /// Geometry block, contains physical geometry information - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct GeometryBlock - { - /// Identifier, - public BlockType identifier; - public uint cylinders; - public uint heads; - public uint sectorsPerTrack; - } + /// Geometry block, contains physical geometry information + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct GeometryBlock + { + /// Identifier, + public BlockType identifier; + public uint cylinders; + public uint heads; + public uint sectorsPerTrack; + } - /// Metadata block, contains metadata - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct MetadataBlock - { - /// Identifier, - public BlockType identifier; - /// Size in bytes of this whole metadata block - public uint blockSize; - /// Sequence of media set this media belongs to - public int mediaSequence; - /// Total number of media on the media set this media belongs to - public int lastMediaSequence; - /// Offset to start of creator string from start of this block - public uint creatorOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint creatorLength; - /// Offset to start of creator string from start of this block - public uint commentsOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint commentsLength; - /// Offset to start of creator string from start of this block - public uint mediaTitleOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint mediaTitleLength; - /// Offset to start of creator string from start of this block - public uint mediaManufacturerOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint mediaManufacturerLength; - /// Offset to start of creator string from start of this block - public uint mediaModelOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint mediaModelLength; - /// Offset to start of creator string from start of this block - public uint mediaSerialNumberOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint mediaSerialNumberLength; - /// Offset to start of creator string from start of this block - public uint mediaBarcodeOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint mediaBarcodeLength; - /// Offset to start of creator string from start of this block - public uint mediaPartNumberOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint mediaPartNumberLength; - /// Offset to start of creator string from start of this block - public uint driveManufacturerOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint driveManufacturerLength; - /// Offset to start of creator string from start of this block - public uint driveModelOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint driveModelLength; - /// Offset to start of creator string from start of this block - public uint driveSerialNumberOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint driveSerialNumberLength; - /// Offset to start of creator string from start of this block - public uint driveFirmwareRevisionOffset; - /// Length in bytes of the null-terminated UTF-16LE creator string - public uint driveFirmwareRevisionLength; - } + /// Metadata block, contains metadata + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct MetadataBlock + { + /// Identifier, + public BlockType identifier; + /// Size in bytes of this whole metadata block + public uint blockSize; + /// Sequence of media set this media belongs to + public int mediaSequence; + /// Total number of media on the media set this media belongs to + public int lastMediaSequence; + /// Offset to start of creator string from start of this block + public uint creatorOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint creatorLength; + /// Offset to start of creator string from start of this block + public uint commentsOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint commentsLength; + /// Offset to start of creator string from start of this block + public uint mediaTitleOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaTitleLength; + /// Offset to start of creator string from start of this block + public uint mediaManufacturerOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaManufacturerLength; + /// Offset to start of creator string from start of this block + public uint mediaModelOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaModelLength; + /// Offset to start of creator string from start of this block + public uint mediaSerialNumberOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaSerialNumberLength; + /// Offset to start of creator string from start of this block + public uint mediaBarcodeOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaBarcodeLength; + /// Offset to start of creator string from start of this block + public uint mediaPartNumberOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint mediaPartNumberLength; + /// Offset to start of creator string from start of this block + public uint driveManufacturerOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint driveManufacturerLength; + /// Offset to start of creator string from start of this block + public uint driveModelOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint driveModelLength; + /// Offset to start of creator string from start of this block + public uint driveSerialNumberOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint driveSerialNumberLength; + /// Offset to start of creator string from start of this block + public uint driveFirmwareRevisionOffset; + /// Length in bytes of the null-terminated UTF-16LE creator string + public uint driveFirmwareRevisionLength; + } - /// Contains list of optical disc tracks - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct TracksHeader - { - /// Identifier, - public BlockType identifier; - /// How many entries follow this header - public ushort entries; - /// CRC64-ECMA of the block - public ulong crc64; - } + /// Contains list of optical disc tracks + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct TracksHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public ushort entries; + /// CRC64-ECMA of the block + public ulong crc64; + } - /// Optical disc track - [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] - struct TrackEntry - { - /// Track sequence - public byte sequence; - /// Track type - public TrackType type; - /// Track starting LBA - public long start; - /// Track last LBA - public long end; - /// Track pregap in sectors - public long pregap; - /// Track session - public byte session; - /// Track's ISRC in ASCII - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)] - public string isrc; - /// Track flags - public byte flags; - } + /// Optical disc track + [StructLayout(LayoutKind.Sequential, Pack = 1, CharSet = CharSet.Ansi)] + struct TrackEntry + { + /// Track sequence + public byte sequence; + /// Track type + public TrackType type; + /// Track starting LBA + public long start; + /// Track last LBA + public long end; + /// Track pregap in sectors + public long pregap; + /// Track session + public byte session; + /// Track's ISRC in ASCII + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 13)] + public string isrc; + /// Track flags + public byte flags; + } - /// Geometry block, contains physical geometry information - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct CicmMetadataBlock - { - /// Identifier, - public BlockType identifier; - public uint length; - } + /// Geometry block, contains physical geometry information + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct CicmMetadataBlock + { + /// Identifier, + public BlockType identifier; + public uint length; + } - /// Dump hardware block, contains a list of hardware used to dump the media on this image - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct DumpHardwareHeader - { - /// Identifier, - public BlockType identifier; - /// How many entries follow this header - public ushort entries; - /// Size of the whole block, not including this header, in bytes - public uint length; - /// CRC64-ECMA of the block - public ulong crc64; - } + /// Dump hardware block, contains a list of hardware used to dump the media on this image + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DumpHardwareHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public ushort entries; + /// Size of the whole block, not including this header, in bytes + public uint length; + /// CRC64-ECMA of the block + public ulong crc64; + } - /// Dump hardware entry, contains length of strings that follow, in the same order as the length, this structure - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct DumpHardwareEntry - { - /// Length of UTF-8 manufacturer string - public uint manufacturerLength; - /// Length of UTF-8 model string - public uint modelLength; - /// Length of UTF-8 revision string - public uint revisionLength; - /// Length of UTF-8 firmware version string - public uint firmwareLength; - /// Length of UTF-8 serial string - public uint serialLength; - /// Length of UTF-8 software name string - public uint softwareNameLength; - /// Length of UTF-8 software version string - public uint softwareVersionLength; - /// Length of UTF-8 software operating system string - public uint softwareOperatingSystemLength; - /// How many extents are after the strings - public uint extents; - } + /// Dump hardware entry, contains length of strings that follow, in the same order as the length, this structure + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct DumpHardwareEntry + { + /// Length of UTF-8 manufacturer string + public uint manufacturerLength; + /// Length of UTF-8 model string + public uint modelLength; + /// Length of UTF-8 revision string + public uint revisionLength; + /// Length of UTF-8 firmware version string + public uint firmwareLength; + /// Length of UTF-8 serial string + public uint serialLength; + /// Length of UTF-8 software name string + public uint softwareNameLength; + /// Length of UTF-8 software version string + public uint softwareVersionLength; + /// Length of UTF-8 software operating system string + public uint softwareOperatingSystemLength; + /// How many extents are after the strings + public uint extents; + } - /// - /// Checksum block, contains a checksum of all user data sectors (except for optical discs that is 2352 bytes raw - /// sector if available - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ChecksumHeader - { - /// Identifier, - public BlockType identifier; - /// Length in bytes of the block - public uint length; - /// How many checksums follow - public byte entries; - } + /// + /// Checksum block, contains a checksum of all user data sectors (except for optical discs that is 2352 bytes raw + /// sector if available + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ChecksumHeader + { + /// Identifier, + public BlockType identifier; + /// Length in bytes of the block + public uint length; + /// How many checksums follow + public byte entries; + } - /// Checksum entry, followed by checksum data itself - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct ChecksumEntry - { - /// Checksum algorithm - public ChecksumAlgorithm type; - /// Length in bytes of checksum that follows this structure - public uint length; - } + /// Checksum entry, followed by checksum data itself + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct ChecksumEntry + { + /// Checksum algorithm + public ChecksumAlgorithm type; + /// Length in bytes of checksum that follows this structure + public uint length; + } - /// Tape file block, contains a list of all files in a tape - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct TapeFileHeader - { - /// Identifier, - public BlockType identifier; - /// How many entries follow this header - public uint entries; - /// Size of the whole block, not including this header, in bytes - public ulong length; - /// CRC64-ECMA of the block - public ulong crc64; - } + /// Tape file block, contains a list of all files in a tape + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct TapeFileHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public uint entries; + /// Size of the whole block, not including this header, in bytes + public ulong length; + /// CRC64-ECMA of the block + public ulong crc64; + } - /// Tape file entry - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct TapeFileEntry - { - /// File number - public uint File; - /// Partition number - public readonly byte Partition; - /// First block, inclusive, of the file - public ulong FirstBlock; - /// Last block, inclusive, of the file - public ulong LastBlock; - } + /// Tape file entry + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct TapeFileEntry + { + /// File number + public uint File; + /// Partition number + public readonly byte Partition; + /// First block, inclusive, of the file + public ulong FirstBlock; + /// Last block, inclusive, of the file + public ulong LastBlock; + } - /// Tape partition block, contains a list of all partitions in a tape - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct TapePartitionHeader - { - /// Identifier, - public BlockType identifier; - /// How many entries follow this header - public byte entries; - /// Size of the whole block, not including this header, in bytes - public ulong length; - /// CRC64-ECMA of the block - public ulong crc64; - } + /// Tape partition block, contains a list of all partitions in a tape + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct TapePartitionHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public byte entries; + /// Size of the whole block, not including this header, in bytes + public ulong length; + /// CRC64-ECMA of the block + public ulong crc64; + } - /// Tape partition entry - [StructLayout(LayoutKind.Sequential, Pack = 1)] - public struct TapePartitionEntry - { - /// Partition number - public byte Number; - /// First block, inclusive, of the partition - public ulong FirstBlock; - /// Last block, inclusive, of the partition - public ulong LastBlock; - } + /// Tape partition entry + [StructLayout(LayoutKind.Sequential, Pack = 1)] + public struct TapePartitionEntry + { + /// Partition number + public byte Number; + /// First block, inclusive, of the partition + public ulong FirstBlock; + /// Last block, inclusive, of the partition + public ulong LastBlock; + } - /// - /// Compact Disc track indexes block, contains a cache of all Compact Disc indexes to not need to interpret - /// subchannel - /// - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct CompactDiscIndexesHeader - { - /// Identifier, - public BlockType identifier; - /// How many entries follow this header - public ushort entries; - /// Size of the whole block, not including this header, in bytes - public readonly ulong length; - /// CRC64-ECMA of the block - public ulong crc64; - } + /// + /// Compact Disc track indexes block, contains a cache of all Compact Disc indexes to not need to interpret + /// subchannel + /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct CompactDiscIndexesHeader + { + /// Identifier, + public BlockType identifier; + /// How many entries follow this header + public ushort entries; + /// Size of the whole block, not including this header, in bytes + public readonly ulong length; + /// CRC64-ECMA of the block + public ulong crc64; + } - [StructLayout(LayoutKind.Sequential, Pack = 1)] - struct CompactDiscIndexEntry - { - /// How many entries follow this header - public ushort Track; - /// Size of the whole block, not including this header, in bytes - public ushort Index; - /// CRC64-ECMA of the block - public int Lba; - } + [StructLayout(LayoutKind.Sequential, Pack = 1)] + struct CompactDiscIndexEntry + { + /// How many entries follow this header + public ushort Track; + /// Size of the whole block, not including this header, in bytes + public ushort Index; + /// CRC64-ECMA of the block + public int Lba; } } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Tape.cs b/Aaru.Images/AaruFormat/Tape.cs index fb6fd02e6..37bd618fc 100644 --- a/Aaru.Images/AaruFormat/Tape.cs +++ b/Aaru.Images/AaruFormat/Tape.cs @@ -34,52 +34,51 @@ using System.Collections.Generic; using System.Linq; using Aaru.CommonTypes.Structs; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + /// + public List Files { get; private set; } + /// + public List TapePartitions { get; private set; } + /// + public bool IsTape { get; private set; } + + /// + public bool AddFile(TapeFile file) { - /// - public List Files { get; private set; } - /// - public List TapePartitions { get; private set; } - /// - public bool IsTape { get; private set; } - - /// - public bool AddFile(TapeFile file) + if(Files.Any(f => f.File == file.File)) { - if(Files.Any(f => f.File == file.File)) - { - TapeFile removeMe = Files.FirstOrDefault(f => f.File == file.File); - Files.Remove(removeMe); - } - - Files.Add(file); - - return true; + TapeFile removeMe = Files.FirstOrDefault(f => f.File == file.File); + Files.Remove(removeMe); } - /// - public bool AddPartition(TapePartition partition) + Files.Add(file); + + return true; + } + + /// + public bool AddPartition(TapePartition partition) + { + if(TapePartitions.Any(f => f.Number == partition.Number)) { - if(TapePartitions.Any(f => f.Number == partition.Number)) - { - TapePartition removeMe = TapePartitions.FirstOrDefault(f => f.Number == partition.Number); - TapePartitions.Remove(removeMe); - } - - TapePartitions.Add(partition); - - return true; + TapePartition removeMe = TapePartitions.FirstOrDefault(f => f.Number == partition.Number); + TapePartitions.Remove(removeMe); } - /// - public bool SetTape() - { - Files = new List(); - TapePartitions = new List(); + TapePartitions.Add(partition); - return IsTape = true; - } + return true; + } + + /// + public bool SetTape() + { + Files = new List(); + TapePartitions = new List(); + + return IsTape = true; } } \ No newline at end of file diff --git a/Aaru.Images/AaruFormat/Verify.cs b/Aaru.Images/AaruFormat/Verify.cs index 0fc49c4cf..2bb248835 100644 --- a/Aaru.Images/AaruFormat/Verify.cs +++ b/Aaru.Images/AaruFormat/Verify.cs @@ -37,285 +37,284 @@ using Aaru.CommonTypes.Enums; using Aaru.Console; using Aaru.Helpers; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class AaruFormat { - public sealed partial class AaruFormat + /// + public bool? VerifyMediaImage() { - /// - public bool? VerifyMediaImage() + // This will traverse all blocks and check their CRC64 without uncompressing them + AaruConsole.DebugWriteLine("Aaru Format plugin", "Checking index integrity at {0}", _header.indexOffset); + _imageStream.Position = (long)_header.indexOffset; + + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + IndexHeader idxHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); + + if(idxHeader.identifier != BlockType.Index) { - // This will traverse all blocks and check their CRC64 without uncompressing them - AaruConsole.DebugWriteLine("Aaru Format plugin", "Checking index integrity at {0}", _header.indexOffset); - _imageStream.Position = (long)_header.indexOffset; + AaruConsole.DebugWriteLine("Aaru Format plugin", "Incorrect index identifier"); - _structureBytes = new byte[Marshal.SizeOf()]; + return false; + } + + AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", _header.indexOffset, + idxHeader.entries); + + _structureBytes = new byte[Marshal.SizeOf() * idxHeader.entries]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + Crc64Context.Data(_structureBytes, out byte[] verifyCrc); + + if(BitConverter.ToUInt64(verifyCrc, 0) != idxHeader.crc64) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", "Expected index CRC {0:X16} but got {1:X16}", + idxHeader.crc64, BitConverter.ToUInt64(verifyCrc, 0)); + + return false; + } + + _imageStream.Position -= _structureBytes.Length; + + List vrIndex = new(); + + for(ushort i = 0; i < idxHeader.entries; i++) + { + _structureBytes = new byte[Marshal.SizeOf()]; _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - IndexHeader idxHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); + IndexEntry entry = Marshal.SpanToStructureLittleEndian(_structureBytes); - if(idxHeader.identifier != BlockType.Index) + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Block type {0} with data type {1} is indexed to be at {2}", entry.blockType, + entry.dataType, entry.offset); + + vrIndex.Add(entry); + } + + // Read up to 1MiB at a time for verification + const int verifySize = 1024 * 1024; + + foreach(IndexEntry entry in vrIndex) + { + _imageStream.Position = (long)entry.offset; + Crc64Context crcVerify; + ulong readBytes; + byte[] verifyBytes; + + switch(entry.blockType) { - AaruConsole.DebugWriteLine("Aaru Format plugin", "Incorrect index identifier"); + case BlockType.DataBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + BlockHeader blockHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - return false; - } + crcVerify = new Crc64Context(); + readBytes = 0; - AaruConsole.DebugWriteLine("Aaru Format plugin", "Index at {0} contains {1} entries", _header.indexOffset, - idxHeader.entries); + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Verifying data block type {0} at position {1}", entry.dataType, + entry.offset); - _structureBytes = new byte[Marshal.SizeOf() * idxHeader.entries]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - Crc64Context.Data(_structureBytes, out byte[] verifyCrc); - - if(BitConverter.ToUInt64(verifyCrc, 0) != idxHeader.crc64) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", "Expected index CRC {0:X16} but got {1:X16}", - idxHeader.crc64, BitConverter.ToUInt64(verifyCrc, 0)); - - return false; - } - - _imageStream.Position -= _structureBytes.Length; - - List vrIndex = new(); - - for(ushort i = 0; i < idxHeader.entries; i++) - { - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - IndexEntry entry = Marshal.SpanToStructureLittleEndian(_structureBytes); - - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Block type {0} with data type {1} is indexed to be at {2}", entry.blockType, - entry.dataType, entry.offset); - - vrIndex.Add(entry); - } - - // Read up to 1MiB at a time for verification - const int verifySize = 1024 * 1024; - - foreach(IndexEntry entry in vrIndex) - { - _imageStream.Position = (long)entry.offset; - Crc64Context crcVerify; - ulong readBytes; - byte[] verifyBytes; - - switch(entry.blockType) - { - case BlockType.DataBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - BlockHeader blockHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - - crcVerify = new Crc64Context(); - readBytes = 0; - - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Verifying data block type {0} at position {1}", entry.dataType, - entry.offset); - - while(readBytes + verifySize < blockHeader.cmpLength) - { - verifyBytes = new byte[verifySize]; - _imageStream.Read(verifyBytes, 0, verifyBytes.Length); - crcVerify.Update(verifyBytes); - readBytes += (ulong)verifyBytes.LongLength; - } - - verifyBytes = new byte[blockHeader.cmpLength - readBytes]; + while(readBytes + verifySize < blockHeader.cmpLength) + { + verifyBytes = new byte[verifySize]; _imageStream.Read(verifyBytes, 0, verifyBytes.Length); crcVerify.Update(verifyBytes); + readBytes += (ulong)verifyBytes.LongLength; + } - verifyCrc = crcVerify.Final(); + verifyBytes = new byte[blockHeader.cmpLength - readBytes]; + _imageStream.Read(verifyBytes, 0, verifyBytes.Length); + crcVerify.Update(verifyBytes); - if(BitConverter.ToUInt64(verifyCrc, 0) != blockHeader.cmpCrc64) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Expected block CRC {0:X16} but got {1:X16}", - blockHeader.cmpCrc64, BitConverter.ToUInt64(verifyCrc, 0)); - - return false; - } - - break; - case BlockType.DeDuplicationTable: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - DdtHeader ddtHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - - crcVerify = new Crc64Context(); - readBytes = 0; + verifyCrc = crcVerify.Final(); + if(BitConverter.ToUInt64(verifyCrc, 0) != blockHeader.cmpCrc64) + { AaruConsole.DebugWriteLine("Aaru Format plugin", - "Verifying deduplication table type {0} at position {1}", - entry.dataType, entry.offset); + "Expected block CRC {0:X16} but got {1:X16}", + blockHeader.cmpCrc64, BitConverter.ToUInt64(verifyCrc, 0)); - while(readBytes + verifySize < ddtHeader.cmpLength) - { - verifyBytes = new byte[readBytes]; - _imageStream.Read(verifyBytes, 0, verifyBytes.Length); - crcVerify.Update(verifyBytes); - readBytes += (ulong)verifyBytes.LongLength; - } + return false; + } - verifyBytes = new byte[ddtHeader.cmpLength - readBytes]; + break; + case BlockType.DeDuplicationTable: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + DdtHeader ddtHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); + + crcVerify = new Crc64Context(); + readBytes = 0; + + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Verifying deduplication table type {0} at position {1}", + entry.dataType, entry.offset); + + while(readBytes + verifySize < ddtHeader.cmpLength) + { + verifyBytes = new byte[readBytes]; _imageStream.Read(verifyBytes, 0, verifyBytes.Length); crcVerify.Update(verifyBytes); + readBytes += (ulong)verifyBytes.LongLength; + } - verifyCrc = crcVerify.Final(); + verifyBytes = new byte[ddtHeader.cmpLength - readBytes]; + _imageStream.Read(verifyBytes, 0, verifyBytes.Length); + crcVerify.Update(verifyBytes); - if(BitConverter.ToUInt64(verifyCrc, 0) != ddtHeader.cmpCrc64) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", "Expected DDT CRC {0:X16} but got {1:X16}", - ddtHeader.cmpCrc64, BitConverter.ToUInt64(verifyCrc, 0)); + verifyCrc = crcVerify.Final(); - return false; - } + if(BitConverter.ToUInt64(verifyCrc, 0) != ddtHeader.cmpCrc64) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", "Expected DDT CRC {0:X16} but got {1:X16}", + ddtHeader.cmpCrc64, BitConverter.ToUInt64(verifyCrc, 0)); - break; - case BlockType.TracksBlock: - _structureBytes = new byte[Marshal.SizeOf()]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - TracksHeader trkHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); + return false; + } - AaruConsole.DebugWriteLine("Aaru Format plugin", "Track block at {0} contains {1} entries", - _header.indexOffset, trkHeader.entries); + break; + case BlockType.TracksBlock: + _structureBytes = new byte[Marshal.SizeOf()]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + TracksHeader trkHeader = Marshal.SpanToStructureLittleEndian(_structureBytes); - _structureBytes = new byte[Marshal.SizeOf() * trkHeader.entries]; - _imageStream.Read(_structureBytes, 0, _structureBytes.Length); - Crc64Context.Data(_structureBytes, out verifyCrc); + AaruConsole.DebugWriteLine("Aaru Format plugin", "Track block at {0} contains {1} entries", + _header.indexOffset, trkHeader.entries); - if(BitConverter.ToUInt64(verifyCrc, 0) != trkHeader.crc64) - { - AaruConsole.DebugWriteLine("Aaru Format plugin", - "Expected index CRC {0:X16} but got {1:X16}", trkHeader.crc64, - BitConverter.ToUInt64(verifyCrc, 0)); + _structureBytes = new byte[Marshal.SizeOf() * trkHeader.entries]; + _imageStream.Read(_structureBytes, 0, _structureBytes.Length); + Crc64Context.Data(_structureBytes, out verifyCrc); - return false; - } + if(BitConverter.ToUInt64(verifyCrc, 0) != trkHeader.crc64) + { + AaruConsole.DebugWriteLine("Aaru Format plugin", + "Expected index CRC {0:X16} but got {1:X16}", trkHeader.crc64, + BitConverter.ToUInt64(verifyCrc, 0)); - break; - default: - AaruConsole.DebugWriteLine("Aaru Format plugin", "Ignored field type {0}", entry.blockType); + return false; + } - break; - } + break; + default: + AaruConsole.DebugWriteLine("Aaru Format plugin", "Ignored field type {0}", entry.blockType); + + break; } - - return true; } - /// - public bool? VerifySector(ulong sectorAddress) + return true; + } + + /// + public bool? VerifySector(ulong sectorAddress) + { + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + return null; + + ErrorNumber errno = ReadSectorLong(sectorAddress, out byte[] buffer); + + return errno != ErrorNumber.NoError ? null : CdChecksums.CheckCdSector(buffer); + } + + /// + public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, + out List unknownLbas) + { + failingLbas = new List(); + unknownLbas = new List(); + + // Right now only CompactDisc sectors are verifiable + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) { - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - return null; + for(ulong i = sectorAddress; i < sectorAddress + length; i++) + unknownLbas.Add(i); - ErrorNumber errno = ReadSectorLong(sectorAddress, out byte[] buffer); - - return errno != ErrorNumber.NoError ? null : CdChecksums.CheckCdSector(buffer); + return null; } - /// - public bool? VerifySectors(ulong sectorAddress, uint length, out List failingLbas, - out List unknownLbas) + ErrorNumber errno = ReadSectorsLong(sectorAddress, length, out byte[] buffer); + + if(errno != ErrorNumber.NoError) + return null; + + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + failingLbas = new List(); + unknownLbas = new List(); + + for(int i = 0; i < length; i++) + { + Array.Copy(buffer, i * bps, sector, 0, bps); + bool? sectorStatus = CdChecksums.CheckCdSector(sector); + + switch(sectorStatus) + { + case null: + unknownLbas.Add((ulong)i + sectorAddress); + + break; + case false: + failingLbas.Add((ulong)i + sectorAddress); + + break; + } + } + + if(unknownLbas.Count > 0) + return null; + + return failingLbas.Count <= 0; + } + + /// + public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, + out List unknownLbas) + { + // Right now only CompactDisc sectors are verifiable + if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) { failingLbas = new List(); unknownLbas = new List(); - // Right now only CompactDisc sectors are verifiable - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) - { - for(ulong i = sectorAddress; i < sectorAddress + length; i++) - unknownLbas.Add(i); + for(ulong i = sectorAddress; i < sectorAddress + length; i++) + unknownLbas.Add(i); - return null; - } - - ErrorNumber errno = ReadSectorsLong(sectorAddress, length, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return null; - - int bps = (int)(buffer.Length / length); - byte[] sector = new byte[bps]; - failingLbas = new List(); - unknownLbas = new List(); - - for(int i = 0; i < length; i++) - { - Array.Copy(buffer, i * bps, sector, 0, bps); - bool? sectorStatus = CdChecksums.CheckCdSector(sector); - - switch(sectorStatus) - { - case null: - unknownLbas.Add((ulong)i + sectorAddress); - - break; - case false: - failingLbas.Add((ulong)i + sectorAddress); - - break; - } - } - - if(unknownLbas.Count > 0) - return null; - - return failingLbas.Count <= 0; + return null; } - /// - public bool? VerifySectors(ulong sectorAddress, uint length, uint track, out List failingLbas, - out List unknownLbas) + failingLbas = new List(); + unknownLbas = new List(); + + ErrorNumber errno = ReadSectorsLong(sectorAddress, length, track, out byte[] buffer); + + if(errno != ErrorNumber.NoError) + return null; + + int bps = (int)(buffer.Length / length); + byte[] sector = new byte[bps]; + + for(int i = 0; i < length; i++) { - // Right now only CompactDisc sectors are verifiable - if(_imageInfo.XmlMediaType != XmlMediaType.OpticalDisc) + Array.Copy(buffer, i * bps, sector, 0, bps); + bool? sectorStatus = CdChecksums.CheckCdSector(sector); + + switch(sectorStatus) { - failingLbas = new List(); - unknownLbas = new List(); + case null: + unknownLbas.Add((ulong)i + sectorAddress); - for(ulong i = sectorAddress; i < sectorAddress + length; i++) - unknownLbas.Add(i); + break; + case false: + failingLbas.Add((ulong)i + sectorAddress); - return null; + break; } - - failingLbas = new List(); - unknownLbas = new List(); - - ErrorNumber errno = ReadSectorsLong(sectorAddress, length, track, out byte[] buffer); - - if(errno != ErrorNumber.NoError) - return null; - - int bps = (int)(buffer.Length / length); - byte[] sector = new byte[bps]; - - for(int i = 0; i < length; i++) - { - Array.Copy(buffer, i * bps, sector, 0, bps); - bool? sectorStatus = CdChecksums.CheckCdSector(sector); - - switch(sectorStatus) - { - case null: - unknownLbas.Add((ulong)i + sectorAddress); - - break; - case false: - failingLbas.Add((ulong)i + sectorAddress); - - break; - } - } - - if(unknownLbas.Count > 0) - return null; - - return failingLbas.Count <= 0; } + + if(unknownLbas.Count > 0) + return null; + + return failingLbas.Count <= 0; } } \ No newline at end of file diff --git a/Aaru.Images/Alcohol120/Alcohol120.cs b/Aaru.Images/Alcohol120/Alcohol120.cs index 16c1063ee..d495979de 100644 --- a/Aaru.Images/Alcohol120/Alcohol120.cs +++ b/Aaru.Images/Alcohol120/Alcohol120.cs @@ -36,53 +36,52 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Interfaces; using Aaru.CommonTypes.Structs; -namespace Aaru.DiscImages -{ - /// - /// Implements reading and writing Alcohol 120% disk images - public sealed partial class Alcohol120 : IWritableOpticalImage - { - Footer _alcFooter; - IFilter _alcImage; - Dictionary _alcSessions; - Dictionary> _alcToc; - Dictionary _alcTrackExtras; - Dictionary _alcTracks; - byte[] _bca; - FileStream _descriptorStream; - byte[] _dmi; - byte[] _fullToc; - Header _header; - ImageInfo _imageInfo; - Stream _imageStream; - bool _isDvd; - Dictionary _offsetMap; - byte[] _pfi; - Dictionary _trackFlags; - List _writingTracks; +namespace Aaru.DiscImages; - public Alcohol120() => _imageInfo = new ImageInfo - { - ReadableSectorTags = new List(), - ReadableMediaTags = new List(), - HasPartitions = true, - HasSessions = true, - Version = null, - Application = null, - ApplicationVersion = null, - Creator = null, - Comments = null, - MediaManufacturer = null, - MediaModel = null, - MediaSerialNumber = null, - MediaBarcode = null, - MediaPartNumber = null, - MediaSequence = 0, - LastMediaSequence = 0, - DriveManufacturer = null, - DriveModel = null, - DriveSerialNumber = null, - DriveFirmwareRevision = null - }; - } +/// +/// Implements reading and writing Alcohol 120% disk images +public sealed partial class Alcohol120 : IWritableOpticalImage +{ + Footer _alcFooter; + IFilter _alcImage; + Dictionary _alcSessions; + Dictionary> _alcToc; + Dictionary _alcTrackExtras; + Dictionary _alcTracks; + byte[] _bca; + FileStream _descriptorStream; + byte[] _dmi; + byte[] _fullToc; + Header _header; + ImageInfo _imageInfo; + Stream _imageStream; + bool _isDvd; + Dictionary _offsetMap; + byte[] _pfi; + Dictionary _trackFlags; + List _writingTracks; + + public Alcohol120() => _imageInfo = new ImageInfo + { + ReadableSectorTags = new List(), + ReadableMediaTags = new List(), + HasPartitions = true, + HasSessions = true, + Version = null, + Application = null, + ApplicationVersion = null, + Creator = null, + Comments = null, + MediaManufacturer = null, + MediaModel = null, + MediaSerialNumber = null, + MediaBarcode = null, + MediaPartNumber = null, + MediaSequence = 0, + LastMediaSequence = 0, + DriveManufacturer = null, + DriveModel = null, + DriveSerialNumber = null, + DriveFirmwareRevision = null + }; } \ No newline at end of file diff --git a/Aaru.Images/Alcohol120/Constants.cs b/Aaru.Images/Alcohol120/Constants.cs index 73ded6780..9c09b0ce7 100644 --- a/Aaru.Images/Alcohol120/Constants.cs +++ b/Aaru.Images/Alcohol120/Constants.cs @@ -30,14 +30,13 @@ // Copyright © 2011-2022 Natalia Portillo // ****************************************************************************/ -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class Alcohol120 { - public sealed partial class Alcohol120 + const byte MAXIMUM_SUPPORTED_VERSION = 1; + readonly byte[] _alcoholSignature = { - const byte MAXIMUM_SUPPORTED_VERSION = 1; - readonly byte[] _alcoholSignature = - { - 0x4d, 0x45, 0x44, 0x49, 0x41, 0x20, 0x44, 0x45, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x4f, 0x52 - }; - } + 0x4d, 0x45, 0x44, 0x49, 0x41, 0x20, 0x44, 0x45, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x4f, 0x52 + }; } \ No newline at end of file diff --git a/Aaru.Images/Alcohol120/Enums.cs b/Aaru.Images/Alcohol120/Enums.cs index a67c0701e..aaf4cb8d0 100644 --- a/Aaru.Images/Alcohol120/Enums.cs +++ b/Aaru.Images/Alcohol120/Enums.cs @@ -32,29 +32,28 @@ using System.Diagnostics.CodeAnalysis; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class Alcohol120 { - public sealed partial class Alcohol120 + [SuppressMessage("ReSharper", "InconsistentNaming")] + enum MediumType : ushort { - [SuppressMessage("ReSharper", "InconsistentNaming")] - enum MediumType : ushort - { - CD = 0x00, CDR = 0x01, CDRW = 0x02, - DVD = 0x10, DVDR = 0x12 - } + CD = 0x00, CDR = 0x01, CDRW = 0x02, + DVD = 0x10, DVDR = 0x12 + } - [SuppressMessage("ReSharper", "InconsistentNaming")] - enum TrackMode : byte - { - NoData = 0x00, DVD = 0x02, Audio = 0xA9, - AudioAlt = 0xE9, Mode1 = 0xAA, Mode1Alt = 0xEA, - Mode2 = 0xAB, Mode2F1 = 0xEC, Mode2F2 = 0xED, - Mode2F1Alt = 0xAC, Mode2F2Alt = 0xAD - } + [SuppressMessage("ReSharper", "InconsistentNaming")] + enum TrackMode : byte + { + NoData = 0x00, DVD = 0x02, Audio = 0xA9, + AudioAlt = 0xE9, Mode1 = 0xAA, Mode1Alt = 0xEA, + Mode2 = 0xAB, Mode2F1 = 0xEC, Mode2F2 = 0xED, + Mode2F1Alt = 0xAC, Mode2F2Alt = 0xAD + } - enum SubchannelMode : byte - { - None = 0x00, Interleaved = 0x08 - } + enum SubchannelMode : byte + { + None = 0x00, Interleaved = 0x08 } } \ No newline at end of file diff --git a/Aaru.Images/Alcohol120/Helpers.cs b/Aaru.Images/Alcohol120/Helpers.cs index 0a4f3c286..b1906aa09 100644 --- a/Aaru.Images/Alcohol120/Helpers.cs +++ b/Aaru.Images/Alcohol120/Helpers.cs @@ -33,124 +33,123 @@ using Aaru.CommonTypes; using Aaru.CommonTypes.Enums; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class Alcohol120 { - public sealed partial class Alcohol120 + static ushort TrackModeToCookedBytesPerSector(TrackMode trackMode) { - static ushort TrackModeToCookedBytesPerSector(TrackMode trackMode) + switch(trackMode) { - switch(trackMode) - { - case TrackMode.Mode1: - case TrackMode.Mode1Alt: - case TrackMode.Mode2F1: - case TrackMode.Mode2F1Alt: return 2048; - case TrackMode.Mode2F2: - case TrackMode.Mode2F2Alt: return 2324; - case TrackMode.Mode2: return 2336; - case TrackMode.Audio: - case TrackMode.AudioAlt: return 2352; - case TrackMode.DVD: return 2048; - default: return 0; - } + case TrackMode.Mode1: + case TrackMode.Mode1Alt: + case TrackMode.Mode2F1: + case TrackMode.Mode2F1Alt: return 2048; + case TrackMode.Mode2F2: + case TrackMode.Mode2F2Alt: return 2324; + case TrackMode.Mode2: return 2336; + case TrackMode.Audio: + case TrackMode.AudioAlt: return 2352; + case TrackMode.DVD: return 2048; + default: return 0; } - - static TrackType TrackModeToTrackType(TrackMode trackType) - { - switch(trackType) - { - case TrackMode.Mode1: - case TrackMode.Mode1Alt: return TrackType.CdMode1; - case TrackMode.Mode2F1: - case TrackMode.Mode2F1Alt: return TrackType.CdMode2Form1; - case TrackMode.Mode2F2: - case TrackMode.Mode2F2Alt: return TrackType.CdMode2Form2; - case TrackMode.Mode2: return TrackType.CdMode2Formless; - case TrackMode.Audio: - case TrackMode.AudioAlt: return TrackType.Audio; - default: return TrackType.Data; - } - } - - static MediaType MediumTypeToMediaType(MediumType discType) - { - switch(discType) - { - case MediumType.CD: return MediaType.CD; - case MediumType.CDR: return MediaType.CDR; - case MediumType.CDRW: return MediaType.CDRW; - case MediumType.DVD: return MediaType.DVDROM; - case MediumType.DVDR: return MediaType.DVDR; - default: return MediaType.Unknown; - } - } - - static MediumType MediaTypeToMediumType(MediaType type) - { - switch(type) - { - case MediaType.CD: - case MediaType.CDDA: - case MediaType.CDEG: - case MediaType.CDG: - case MediaType.CDI: - case MediaType.CDMIDI: - case MediaType.CDPLUS: - case MediaType.CDROM: - case MediaType.CDROMXA: - case MediaType.CDV: - case MediaType.DTSCD: - case MediaType.JaguarCD: - case MediaType.MEGACD: - case MediaType.PS1CD: - case MediaType.PS2CD: - case MediaType.SuperCDROM2: - case MediaType.SVCD: - case MediaType.SATURNCD: - case MediaType.ThreeDO: - case MediaType.VCD: - case MediaType.VCDHD: - case MediaType.NeoGeoCD: - case MediaType.PCFX: - case MediaType.CDTV: - case MediaType.CD32: - case MediaType.Nuon: - case MediaType.Playdia: - case MediaType.Pippin: - case MediaType.FMTOWNS: - case MediaType.MilCD: - case MediaType.VideoNow: - case MediaType.VideoNowColor: - case MediaType.VideoNowXp: - case MediaType.CVD: return MediumType.CD; - case MediaType.CDR: return MediumType.CDR; - case MediaType.CDRW: - case MediaType.CDMRW: return MediumType.CDRW; - case MediaType.DVDR: - case MediaType.DVDRW: - case MediaType.DVDPR: - case MediaType.DVDRDL: - case MediaType.DVDRWDL: - case MediaType.DVDPRDL: - case MediaType.DVDPRWDL: return MediumType.DVDR; - default: return MediumType.DVD; - } - } - - static TrackMode TrackTypeToTrackMode(TrackType type) - { - switch(type) - { - case TrackType.Audio: return TrackMode.Audio; - case TrackType.CdMode1: return TrackMode.Mode1; - case TrackType.CdMode2Formless: return TrackMode.Mode2; - case TrackType.CdMode2Form1: return TrackMode.Mode2F1; - case TrackType.CdMode2Form2: return TrackMode.Mode2F2; - default: return TrackMode.DVD; - } - } - - static (byte minute, byte second, byte frame) LbaToMsf(ulong sector) => - ((byte)((sector + 150) / 75 / 60), (byte)((sector + 150) / 75 % 60), (byte)((sector + 150) % 75)); } + + static TrackType TrackModeToTrackType(TrackMode trackType) + { + switch(trackType) + { + case TrackMode.Mode1: + case TrackMode.Mode1Alt: return TrackType.CdMode1; + case TrackMode.Mode2F1: + case TrackMode.Mode2F1Alt: return TrackType.CdMode2Form1; + case TrackMode.Mode2F2: + case TrackMode.Mode2F2Alt: return TrackType.CdMode2Form2; + case TrackMode.Mode2: return TrackType.CdMode2Formless; + case TrackMode.Audio: + case TrackMode.AudioAlt: return TrackType.Audio; + default: return TrackType.Data; + } + } + + static MediaType MediumTypeToMediaType(MediumType discType) + { + switch(discType) + { + case MediumType.CD: return MediaType.CD; + case MediumType.CDR: return MediaType.CDR; + case MediumType.CDRW: return MediaType.CDRW; + case MediumType.DVD: return MediaType.DVDROM; + case MediumType.DVDR: return MediaType.DVDR; + default: return MediaType.Unknown; + } + } + + static MediumType MediaTypeToMediumType(MediaType type) + { + switch(type) + { + case MediaType.CD: + case MediaType.CDDA: + case MediaType.CDEG: + case MediaType.CDG: + case MediaType.CDI: + case MediaType.CDMIDI: + case MediaType.CDPLUS: + case MediaType.CDROM: + case MediaType.CDROMXA: + case MediaType.CDV: + case MediaType.DTSCD: + case MediaType.JaguarCD: + case MediaType.MEGACD: + case MediaType.PS1CD: + case MediaType.PS2CD: + case MediaType.SuperCDROM2: + case MediaType.SVCD: + case MediaType.SATURNCD: + case MediaType.ThreeDO: + case MediaType.VCD: + case MediaType.VCDHD: + case MediaType.NeoGeoCD: + case MediaType.PCFX: + case MediaType.CDTV: + case MediaType.CD32: + case MediaType.Nuon: + case MediaType.Playdia: + case MediaType.Pippin: + case MediaType.FMTOWNS: + case MediaType.MilCD: + case MediaType.VideoNow: + case MediaType.VideoNowColor: + case MediaType.VideoNowXp: + case MediaType.CVD: return MediumType.CD; + case MediaType.CDR: return MediumType.CDR; + case MediaType.CDRW: + case MediaType.CDMRW: return MediumType.CDRW; + case MediaType.DVDR: + case MediaType.DVDRW: + case MediaType.DVDPR: + case MediaType.DVDRDL: + case MediaType.DVDRWDL: + case MediaType.DVDPRDL: + case MediaType.DVDPRWDL: return MediumType.DVDR; + default: return MediumType.DVD; + } + } + + static TrackMode TrackTypeToTrackMode(TrackType type) + { + switch(type) + { + case TrackType.Audio: return TrackMode.Audio; + case TrackType.CdMode1: return TrackMode.Mode1; + case TrackType.CdMode2Formless: return TrackMode.Mode2; + case TrackType.CdMode2Form1: return TrackMode.Mode2F1; + case TrackType.CdMode2Form2: return TrackMode.Mode2F2; + default: return TrackMode.DVD; + } + } + + static (byte minute, byte second, byte frame) LbaToMsf(ulong sector) => + ((byte)((sector + 150) / 75 / 60), (byte)((sector + 150) / 75 % 60), (byte)((sector + 150) % 75)); } \ No newline at end of file diff --git a/Aaru.Images/Alcohol120/Identify.cs b/Aaru.Images/Alcohol120/Identify.cs index fb875b8a4..ae1b3cbbd 100644 --- a/Aaru.Images/Alcohol120/Identify.cs +++ b/Aaru.Images/Alcohol120/Identify.cs @@ -35,24 +35,23 @@ using System.Linq; using Aaru.CommonTypes.Interfaces; using Aaru.Helpers; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class Alcohol120 { - public sealed partial class Alcohol120 + /// + public bool Identify(IFilter imageFilter) { - /// - public bool Identify(IFilter imageFilter) - { - Stream stream = imageFilter.GetDataForkStream(); - stream.Seek(0, SeekOrigin.Begin); + Stream stream = imageFilter.GetDataForkStream(); + stream.Seek(0, SeekOrigin.Begin); - if(stream.Length < 88) - return false; + if(stream.Length < 88) + return false; - byte[] hdr = new byte[88]; - stream.Read(hdr, 0, 88); - Header header = Marshal.ByteArrayToStructureLittleEndian
(hdr); + byte[] hdr = new byte[88]; + stream.Read(hdr, 0, 88); + Header header = Marshal.ByteArrayToStructureLittleEndian
(hdr); - return header.signature.SequenceEqual(_alcoholSignature) && header.version[0] <= MAXIMUM_SUPPORTED_VERSION; - } + return header.signature.SequenceEqual(_alcoholSignature) && header.version[0] <= MAXIMUM_SUPPORTED_VERSION; } } \ No newline at end of file diff --git a/Aaru.Images/Alcohol120/Properties.cs b/Aaru.Images/Alcohol120/Properties.cs index 87524bdbc..c15f7ffe0 100644 --- a/Aaru.Images/Alcohol120/Properties.cs +++ b/Aaru.Images/Alcohol120/Properties.cs @@ -38,165 +38,164 @@ using Aaru.CommonTypes.Enums; using Aaru.CommonTypes.Structs; using Schemas; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class Alcohol120 { - public sealed partial class Alcohol120 + /// + public OpticalImageCapabilities OpticalCapabilities => OpticalImageCapabilities.CanStoreAudioTracks | + OpticalImageCapabilities.CanStoreDataTracks | + OpticalImageCapabilities.CanStoreSubchannelRw | + + // TODO: Disabled until 6.0 + //OpticalImageCapabilities.CanStoreSessions | + OpticalImageCapabilities.CanStoreIsrc | + OpticalImageCapabilities.CanStoreCdText | + OpticalImageCapabilities.CanStoreMcn | + OpticalImageCapabilities.CanStoreRawData | + OpticalImageCapabilities.CanStoreCookedData | + OpticalImageCapabilities.CanStoreMultipleTracks; + /// + public ImageInfo Info => _imageInfo; + /// + public string Name => "Alcohol 120% Media Descriptor Structure"; + /// + public Guid Id => new("A78FBEBA-0307-4915-BDE3-B8A3B57F843F"); + /// + public string Author => "Natalia Portillo"; + + /// + public string Format => "Alcohol 120% Media Descriptor Structure"; + + /// + public List Partitions { get; private set; } + + /// + public List Tracks { - /// - public OpticalImageCapabilities OpticalCapabilities => OpticalImageCapabilities.CanStoreAudioTracks | - OpticalImageCapabilities.CanStoreDataTracks | - OpticalImageCapabilities.CanStoreSubchannelRw | - - // TODO: Disabled until 6.0 - //OpticalImageCapabilities.CanStoreSessions | - OpticalImageCapabilities.CanStoreIsrc | - OpticalImageCapabilities.CanStoreCdText | - OpticalImageCapabilities.CanStoreMcn | - OpticalImageCapabilities.CanStoreRawData | - OpticalImageCapabilities.CanStoreCookedData | - OpticalImageCapabilities.CanStoreMultipleTracks; - /// - public ImageInfo Info => _imageInfo; - /// - public string Name => "Alcohol 120% Media Descriptor Structure"; - /// - public Guid Id => new("A78FBEBA-0307-4915-BDE3-B8A3B57F843F"); - /// - public string Author => "Natalia Portillo"; - - /// - public string Format => "Alcohol 120% Media Descriptor Structure"; - - /// - public List Partitions { get; private set; } - - /// - public List Tracks + get { - get + if(_writingTracks != null && + _alcTracks == null) + return _writingTracks; + + List tracks = new(); + _alcTracks ??= new Dictionary(); + + foreach(Track alcTrack in _alcTracks.Values) { - if(_writingTracks != null && - _alcTracks == null) - return _writingTracks; + ushort sessionNo = + (from session in Sessions + where alcTrack.point >= session.StartTrack && alcTrack.point <= session.EndTrack + select session.Sequence).FirstOrDefault(); - List tracks = new(); - _alcTracks ??= new Dictionary(); + if(!_alcTrackExtras.TryGetValue(alcTrack.point, out TrackExtra alcExtra)) + continue; - foreach(Track alcTrack in _alcTracks.Values) + var aaruTrack = new CommonTypes.Structs.Track { - ushort sessionNo = - (from session in Sessions - where alcTrack.point >= session.StartTrack && alcTrack.point <= session.EndTrack - select session.Sequence).FirstOrDefault(); + StartSector = alcTrack.startLba, + EndSector = alcTrack.startLba + alcExtra.sectors - 1, + Pregap = alcExtra.pregap, + Session = sessionNo, + Sequence = alcTrack.point, + Type = TrackModeToTrackType(alcTrack.mode), + Filter = _alcImage, + File = _alcImage.Filename, + FileOffset = alcTrack.startOffset, + FileType = "BINARY", + RawBytesPerSector = alcTrack.sectorSize, + BytesPerSector = TrackModeToCookedBytesPerSector(alcTrack.mode) + }; - if(!_alcTrackExtras.TryGetValue(alcTrack.point, out TrackExtra alcExtra)) - continue; + if(alcExtra.pregap > 0) + aaruTrack.Indexes.Add(0, (int)(alcTrack.startLba - alcExtra.pregap)); - var aaruTrack = new CommonTypes.Structs.Track - { - StartSector = alcTrack.startLba, - EndSector = alcTrack.startLba + alcExtra.sectors - 1, - Pregap = alcExtra.pregap, - Session = sessionNo, - Sequence = alcTrack.point, - Type = TrackModeToTrackType(alcTrack.mode), - Filter = _alcImage, - File = _alcImage.Filename, - FileOffset = alcTrack.startOffset, - FileType = "BINARY", - RawBytesPerSector = alcTrack.sectorSize, - BytesPerSector = TrackModeToCookedBytesPerSector(alcTrack.mode) - }; + aaruTrack.Indexes.Add(1, (int)alcTrack.startLba); - if(alcExtra.pregap > 0) - aaruTrack.Indexes.Add(0, (int)(alcTrack.startLba - alcExtra.pregap)); + if(aaruTrack.Indexes.ContainsKey(0) && + aaruTrack.Indexes[0] >= 0) + aaruTrack.StartSector = (ulong)aaruTrack.Indexes[0]; - aaruTrack.Indexes.Add(1, (int)alcTrack.startLba); + switch(alcTrack.subMode) + { + case SubchannelMode.Interleaved: + aaruTrack.SubchannelFilter = _alcImage; + aaruTrack.SubchannelFile = _alcImage.Filename; + aaruTrack.SubchannelOffset = alcTrack.startOffset; + aaruTrack.SubchannelType = TrackSubchannelType.RawInterleaved; - if(aaruTrack.Indexes.ContainsKey(0) && - aaruTrack.Indexes[0] >= 0) - aaruTrack.StartSector = (ulong)aaruTrack.Indexes[0]; + break; + case SubchannelMode.None: + aaruTrack.SubchannelType = TrackSubchannelType.None; - switch(alcTrack.subMode) - { - case SubchannelMode.Interleaved: - aaruTrack.SubchannelFilter = _alcImage; - aaruTrack.SubchannelFile = _alcImage.Filename; - aaruTrack.SubchannelOffset = alcTrack.startOffset; - aaruTrack.SubchannelType = TrackSubchannelType.RawInterleaved; - - break; - case SubchannelMode.None: - aaruTrack.SubchannelType = TrackSubchannelType.None; - - break; - } - - if(_header.type != MediumType.CD && - _header.type != MediumType.CDR && - _header.type != MediumType.CDRW) - { - aaruTrack.Pregap = 0; - aaruTrack.Indexes?.Clear(); - } - - tracks.Add(aaruTrack); + break; } - return tracks; + if(_header.type != MediumType.CD && + _header.type != MediumType.CDR && + _header.type != MediumType.CDRW) + { + aaruTrack.Pregap = 0; + aaruTrack.Indexes?.Clear(); + } + + tracks.Add(aaruTrack); } + + return tracks; } - - /// - public List Sessions { get; private set; } - - /// - public List DumpHardware => null; - /// - public CICMMetadataType CicmMetadata => null; - - /// - public IEnumerable SupportedMediaTags => new[] - { - MediaTagType.CD_FullTOC, MediaTagType.DVD_BCA, MediaTagType.DVD_DMI, MediaTagType.DVD_PFI - }; - /// - public IEnumerable SupportedSectorTags => new[] - { - SectorTagType.CdSectorEcc, SectorTagType.CdSectorEccP, SectorTagType.CdSectorEccQ, - SectorTagType.CdSectorEdc, SectorTagType.CdSectorHeader, SectorTagType.CdSectorSubHeader, - SectorTagType.CdSectorSync, SectorTagType.CdTrackFlags, SectorTagType.CdSectorSubchannel - }; - /// - public IEnumerable SupportedMediaTypes => new[] - { - MediaType.BDR, MediaType.BDRE, MediaType.BDREXL, MediaType.BDROM, MediaType.UHDBD, MediaType.BDRXL, - MediaType.CBHD, MediaType.CD, MediaType.CDDA, MediaType.CDEG, MediaType.CDG, MediaType.CDI, - MediaType.CDMIDI, MediaType.CDMRW, MediaType.CDPLUS, MediaType.CDR, MediaType.CDROM, MediaType.CDROMXA, - MediaType.CDRW, MediaType.CDV, MediaType.DVDDownload, MediaType.DVDPR, MediaType.DVDPRDL, MediaType.DVDPRW, - MediaType.DVDPRWDL, MediaType.DVDR, MediaType.DVDRAM, MediaType.DVDRDL, MediaType.DVDROM, MediaType.DVDRW, - MediaType.DVDRWDL, MediaType.EVD, MediaType.FDDVD, MediaType.DTSCD, MediaType.FVD, MediaType.HDDVDR, - MediaType.HDDVDRAM, MediaType.HDDVDRDL, MediaType.HDDVDROM, MediaType.HDDVDRW, MediaType.HDDVDRWDL, - MediaType.HDVMD, MediaType.HVD, MediaType.JaguarCD, MediaType.MEGACD, MediaType.PS1CD, MediaType.PS2CD, - MediaType.PS2DVD, MediaType.PS3BD, MediaType.PS3DVD, MediaType.PS4BD, MediaType.PS5BD, - MediaType.SuperCDROM2, MediaType.SVCD, MediaType.SVOD, MediaType.SATURNCD, MediaType.ThreeDO, MediaType.UDO, - MediaType.UDO2, MediaType.UDO2_WORM, MediaType.UMD, MediaType.VCD, MediaType.VCDHD, MediaType.NeoGeoCD, - MediaType.PCFX, MediaType.CDTV, MediaType.CD32, MediaType.Nuon, MediaType.Playdia, MediaType.Pippin, - MediaType.FMTOWNS, MediaType.MilCD, MediaType.VideoNow, MediaType.VideoNowColor, MediaType.VideoNowXp, - MediaType.CVD, MediaType.PCD - }; - /// - public IEnumerable<(string name, Type type, string description, object @default)> SupportedOptions => - new (string name, Type type, string description, object @default)[] - {}; - /// - public IEnumerable KnownExtensions => new[] - { - ".mds" - }; - /// - public bool IsWriting { get; private set; } - /// - public string ErrorMessage { get; private set; } } + + /// + public List Sessions { get; private set; } + + /// + public List DumpHardware => null; + /// + public CICMMetadataType CicmMetadata => null; + + /// + public IEnumerable SupportedMediaTags => new[] + { + MediaTagType.CD_FullTOC, MediaTagType.DVD_BCA, MediaTagType.DVD_DMI, MediaTagType.DVD_PFI + }; + /// + public IEnumerable SupportedSectorTags => new[] + { + SectorTagType.CdSectorEcc, SectorTagType.CdSectorEccP, SectorTagType.CdSectorEccQ, + SectorTagType.CdSectorEdc, SectorTagType.CdSectorHeader, SectorTagType.CdSectorSubHeader, + SectorTagType.CdSectorSync, SectorTagType.CdTrackFlags, SectorTagType.CdSectorSubchannel + }; + /// + public IEnumerable SupportedMediaTypes => new[] + { + MediaType.BDR, MediaType.BDRE, MediaType.BDREXL, MediaType.BDROM, MediaType.UHDBD, MediaType.BDRXL, + MediaType.CBHD, MediaType.CD, MediaType.CDDA, MediaType.CDEG, MediaType.CDG, MediaType.CDI, + MediaType.CDMIDI, MediaType.CDMRW, MediaType.CDPLUS, MediaType.CDR, MediaType.CDROM, MediaType.CDROMXA, + MediaType.CDRW, MediaType.CDV, MediaType.DVDDownload, MediaType.DVDPR, MediaType.DVDPRDL, MediaType.DVDPRW, + MediaType.DVDPRWDL, MediaType.DVDR, MediaType.DVDRAM, MediaType.DVDRDL, MediaType.DVDROM, MediaType.DVDRW, + MediaType.DVDRWDL, MediaType.EVD, MediaType.FDDVD, MediaType.DTSCD, MediaType.FVD, MediaType.HDDVDR, + MediaType.HDDVDRAM, MediaType.HDDVDRDL, MediaType.HDDVDROM, MediaType.HDDVDRW, MediaType.HDDVDRWDL, + MediaType.HDVMD, MediaType.HVD, MediaType.JaguarCD, MediaType.MEGACD, MediaType.PS1CD, MediaType.PS2CD, + MediaType.PS2DVD, MediaType.PS3BD, MediaType.PS3DVD, MediaType.PS4BD, MediaType.PS5BD, + MediaType.SuperCDROM2, MediaType.SVCD, MediaType.SVOD, MediaType.SATURNCD, MediaType.ThreeDO, MediaType.UDO, + MediaType.UDO2, MediaType.UDO2_WORM, MediaType.UMD, MediaType.VCD, MediaType.VCDHD, MediaType.NeoGeoCD, + MediaType.PCFX, MediaType.CDTV, MediaType.CD32, MediaType.Nuon, MediaType.Playdia, MediaType.Pippin, + MediaType.FMTOWNS, MediaType.MilCD, MediaType.VideoNow, MediaType.VideoNowColor, MediaType.VideoNowXp, + MediaType.CVD, MediaType.PCD + }; + /// + public IEnumerable<(string name, Type type, string description, object @default)> SupportedOptions => + new (string name, Type type, string description, object @default)[] + {}; + /// + public IEnumerable KnownExtensions => new[] + { + ".mds" + }; + /// + public bool IsWriting { get; private set; } + /// + public string ErrorMessage { get; private set; } } \ No newline at end of file diff --git a/Aaru.Images/Alcohol120/Read.cs b/Aaru.Images/Alcohol120/Read.cs index d7f62ad1b..514fc1526 100644 --- a/Aaru.Images/Alcohol120/Read.cs +++ b/Aaru.Images/Alcohol120/Read.cs @@ -44,1510 +44,1509 @@ using Aaru.Decoders.DVD; using Aaru.Helpers; using DMI = Aaru.Decoders.Xbox.DMI; -namespace Aaru.DiscImages +namespace Aaru.DiscImages; + +public sealed partial class Alcohol120 { - public sealed partial class Alcohol120 + /// + public ErrorNumber Open(IFilter imageFilter) { - /// - public ErrorNumber Open(IFilter imageFilter) + Stream stream = imageFilter.GetDataForkStream(); + stream.Seek(0, SeekOrigin.Begin); + + if(stream.Length < 88) + return ErrorNumber.InvalidArgument; + + _isDvd = false; + byte[] hdr = new byte[88]; + stream.Read(hdr, 0, 88); + _header = Marshal.ByteArrayToStructureLittleEndian
(hdr); + + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.signature = {0}", + Encoding.ASCII.GetString(_header.signature)); + + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.version = {0}.{1}", _header.version[0], + _header.version[1]); + + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.type = {0}", _header.type); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.sessions = {0}", _header.sessions); + + for(int i = 0; i < _header.unknown1.Length; i++) + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown1[{1}] = 0x{0:X4}", + _header.unknown1[i], i); + + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.bcaLength = {0}", _header.bcaLength); + + for(int i = 0; i < _header.unknown2.Length; i++) + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown2[{1}] = 0x{0:X8}", + _header.unknown2[i], i); + + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.bcaOffset = {0}", _header.bcaOffset); + + for(int i = 0; i < _header.unknown3.Length; i++) + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown3[{1}] = 0x{0:X8}", + _header.unknown3[i], i); + + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.structuresOffset = {0}", + _header.structuresOffset); + + for(int i = 0; i < _header.unknown4.Length; i++) + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown4[{1}] = 0x{0:X8}", + _header.unknown4[i], i); + + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.sessionOffset = {0}", _header.sessionOffset); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.dpmOffset = {0}", _header.dpmOffset); + + if(_header.version[0] > MAXIMUM_SUPPORTED_VERSION) + return ErrorNumber.NotSupported; + + stream.Seek(_header.sessionOffset, SeekOrigin.Begin); + _alcSessions = new Dictionary(); + + for(int i = 0; i < _header.sessions; i++) { - Stream stream = imageFilter.GetDataForkStream(); - stream.Seek(0, SeekOrigin.Begin); + byte[] sesHdr = new byte[24]; + stream.Read(sesHdr, 0, 24); + Session session = Marshal.SpanToStructureLittleEndian(sesHdr); - if(stream.Length < 88) - return ErrorNumber.InvalidArgument; + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionStart = {0}", + session.sessionStart, i); - _isDvd = false; - byte[] hdr = new byte[88]; - stream.Read(hdr, 0, 88); - _header = Marshal.ByteArrayToStructureLittleEndian
(hdr); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionEnd = {0}", session.sessionEnd, + i); - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.signature = {0}", - Encoding.ASCII.GetString(_header.signature)); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionSequence = {0}", + session.sessionSequence, i); - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.version = {0}.{1}", _header.version[0], - _header.version[1]); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].allBlocks = {0}", session.allBlocks, i); - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.type = {0}", _header.type); - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.sessions = {0}", _header.sessions); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].nonTrackBlocks = {0}", + session.nonTrackBlocks, i); - for(int i = 0; i < _header.unknown1.Length; i++) - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown1[{1}] = 0x{0:X4}", - _header.unknown1[i], i); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].firstTrack = {0}", session.firstTrack, + i); - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.bcaLength = {0}", _header.bcaLength); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].lastTrack = {0}", session.lastTrack, i); - for(int i = 0; i < _header.unknown2.Length; i++) - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown2[{1}] = 0x{0:X8}", - _header.unknown2[i], i); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].unknown = 0x{0:X8}", session.unknown, + i); - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.bcaOffset = {0}", _header.bcaOffset); + AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].trackOffset = {0}", session.trackOffset, + i); - for(int i = 0; i < _header.unknown3.Length; i++) - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown3[{1}] = 0x{0:X8}", - _header.unknown3[i], i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.structuresOffset = {0}", - _header.structuresOffset); - - for(int i = 0; i < _header.unknown4.Length; i++) - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.unknown4[{1}] = 0x{0:X8}", - _header.unknown4[i], i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.sessionOffset = {0}", _header.sessionOffset); - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "header.dpmOffset = {0}", _header.dpmOffset); - - if(_header.version[0] > MAXIMUM_SUPPORTED_VERSION) - return ErrorNumber.NotSupported; - - stream.Seek(_header.sessionOffset, SeekOrigin.Begin); - _alcSessions = new Dictionary(); - - for(int i = 0; i < _header.sessions; i++) - { - byte[] sesHdr = new byte[24]; - stream.Read(sesHdr, 0, 24); - Session session = Marshal.SpanToStructureLittleEndian(sesHdr); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionStart = {0}", - session.sessionStart, i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionEnd = {0}", session.sessionEnd, - i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].sessionSequence = {0}", - session.sessionSequence, i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].allBlocks = {0}", session.allBlocks, i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].nonTrackBlocks = {0}", - session.nonTrackBlocks, i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].firstTrack = {0}", session.firstTrack, - i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].lastTrack = {0}", session.lastTrack, i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].unknown = 0x{0:X8}", session.unknown, - i); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{1}].trackOffset = {0}", session.trackOffset, - i); - - _alcSessions.Add(session.sessionSequence, session); - } - - long footerOff = 0; - bool oldIncorrectImage = false; - - _alcTracks = new Dictionary(); - _alcToc = new Dictionary>(); - uint track1Index1 = 0; - - foreach(Session session in _alcSessions.Values) - { - stream.Seek(session.trackOffset, SeekOrigin.Begin); - Dictionary sesToc = new(); - - for(int i = 0; i < session.allBlocks; i++) - { - byte[] trkHdr = new byte[80]; - stream.Read(trkHdr, 0, 80); - Track track = Marshal.ByteArrayToStructureLittleEndian(trkHdr); - - if(track.mode == TrackMode.Mode2F1Alt || - track.mode == TrackMode.Mode2F1Alt) - oldIncorrectImage = true; - - // Solve our own mistake here, sorry, but anyway seems Alcohol doesn't support DDCD - if(track.zero > 0 && - track.point >= 1 && - track.point <= 99) - { - track.pmin += (byte)(track.zero * 60); - track.zero = 0; - oldIncorrectImage = true; - } - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].mode = {0}", track.mode, - track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].subMode = {0}", - track.subMode, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].adrCtl = {0}", - track.adrCtl, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].tno = {0}", track.tno, - track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].point = {0:X2}", - track.point, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].min = {0}", track.min, - track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].sec = {0}", track.sec, - track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].frame = {0}", - track.frame, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].zero = {0}", track.zero, - track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].pmin = {0}", track.pmin, - track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].psec = {0}", track.psec, - track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].pframe = {0}", - track.pframe, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].extraOffset = {0}", - track.extraOffset, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].sectorSize = {0}", - track.sectorSize, track.point, session.sessionSequence); - - //for(int j = 0; j < track.unknown.Length; j++) - // AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].unknown[{2}] = {0}", track.unknown[j], i, j, session.sessionSequence); - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].startLba = {0}", - track.startLba, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].startOffset = {0}", - track.startOffset, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].files = {0}", - track.files, track.point, session.sessionSequence); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].footerOffset = {0}", - track.footerOffset, track.point, session.sessionSequence); - - //for(int j = 0; j < track.unknown2.Length; j++) - // AaruConsole.DebugWriteLine("Alcohol 120% plugin", "session[{2}].track[{1}].unknown2[{2}] = {0}", track.unknown2[j], i, j, session.sessionSequence); - - if(track.subMode == SubchannelMode.Interleaved) - track.sectorSize -= 96; - - if(track.point == 1 && - track.startLba > 0) - { - AaruConsole. - ErrorWriteLine("The disc this image represents contained a hidden track in the first pregap, that this image format cannot store. This dump is therefore, incorrect."); - - track1Index1 = track.startLba; - track.startLba = 0; - } - - if(!sesToc.ContainsKey(track.point)) - sesToc.Add(track.point, track); - - if(track.point < 0xA0) - _alcTracks.Add(track.point, track); - - if(footerOff == 0) - footerOff = track.footerOffset; - - _isDvd |= track.mode == TrackMode.DVD; - } - - _alcToc.Add(session.sessionSequence, sesToc); - } - - _alcTrackExtras = new Dictionary(); - - foreach(Track track in _alcTracks.Values) - if(track.extraOffset > 0 && - !_isDvd) - { - byte[] extHdr = new byte[8]; - stream.Seek(track.extraOffset, SeekOrigin.Begin); - stream.Read(extHdr, 0, 8); - TrackExtra extra = Marshal.SpanToStructureLittleEndian(extHdr); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "track[{1}].extra.pregap = {0}", extra.pregap, - track.point); - - AaruConsole.DebugWriteLine("Alcohol 120% plugin", "track[{1}].extra.sectors = {0}", extra.sectors, - track.point); - - if(track.point == 1) - { - extra.pregap = track1Index1 + 150; // Needed because faulty UltraISO implementation - extra.sectors += extra.pregap - 150; - } - - _alcTrackExtras.Add(track.point, extra); - } - else if(_isDvd) - { - var extra = new TrackExtra - { - sectors = track.extraOffset - }; - - _alcTrackExtras.Add(track.point, extra); - } - - if(footerOff > 0) - { - byte[] footer = new byte[16]; - stream.Seek(footerOff, SeekOrigin.Begin); - stream.Read(footer, 0, 16); - _alcFooter = Marshal.SpanToStructureLittleEndian